Skip to content

guyifergan1/solar-monitor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Solar IoT Node — ESP32

PlatformIO Language Hardware License

Low-power solar monitoring node built on ESP32 with modular firmware architecture. Reads voltage (INA3221) and ambient light (LDR), renders live data on an OLED display, and enters Deep Sleep between cycles to minimise power consumption. Part of an embedded systems portfolio targeting real-world IoT applications.


Hardware

Component Interface Pin / Address
ESP32 DevKit
OLED SSD1306 (128×64) I2C 0x3C
INA3221 (3-channel voltage/current sensor) I2C 0x40
LDR + 10 kΩ pull-down resistor ADC GPIO34

Wiring

I2C Bus (OLED + INA3221)

ESP32 Pin Signal OLED INA3221
GPIO 21 SDA SDA SDA
GPIO 22 SCL SCL SCL
3.3 V VCC VCC VCC
GND GND GND GND

LDR Voltage Divider

3.3V ──── LDR ──── GPIO34 ──── 10kΩ ──── GND
                      ↑
               ADC measurement node

System Flow

Wake from Deep Sleep
        │
        ▼
  Init peripherals
  (OLED, INA3221, LDR)
        │
        ▼
  ┌─────────────────┐
  │  5 s wake window│
  │  Poll every 300 ms:│
  │  · Read INA3221 CH1│
  │  · Read LDR ADC │
  │  · Update OLED  │
  │  · Print Serial │
  └─────────────────┘
        │
        ▼
   OLED display off
        │
        ▼
  Deep Sleep 25 s
  (RTC memory retained)
        │
        └──────► (repeat)

Total duty cycle: 5 s active / 25 s sleeping = ~17% duty cycle.


Project Structure

src/
├── config.h          — Centralised #defines: pins, I2C addresses, timing constants
├── display.h/.cpp    — OLED driver: init, combined data screen, error screen, power-off
├── sensors.h/.cpp    — INA3221 driver: init, per-channel bus voltage read
├── ldr.h/.cpp        — LDR driver: init, ADC read with clamp, return as 0–100 %
└── main.cpp          — Entry point: wake → sense → display → sleep

test/
├── validator.py          — Serial port reader; validates voltage and light readings
├── test_solar_node.py    — pytest suite: connection, range, light, stability (7 tests)
└── requirements.txt      — pyserial, pytest

Each hardware driver is a self-contained .h/.cpp pair. The hardware object is static inside the .cpp file and never exposed beyond that translation unit — all interaction goes through the public API functions.


Architecture Decisions

Deep Sleep — The node reads sensors, updates the display for 5 s, then sleeps for 25 s (30 s total cycle). setup() executes on every wake; loop() is intentionally empty. This eliminates the need for a state machine in the main loop.

Graceful degradation — Every peripheral init function returns bool. If a device is absent, the fault is logged to Serial and the system continues. An OLED-only or sensor-only failure doesn't crash the node.

F() macro for string literals — All fixed strings are stored in Flash rather than copied to SRAM at runtime. On a device with 520 KB SRAM and several KB of string output, this matters.

Encapsulation — Hardware objects are hidden inside their translation units and accessed only through function calls, preventing unintended shared state between drivers.

RTC_DATA_ATTR boot counter — The boot counter is placed in RTC-retained memory so it survives Deep Sleep without a battery-backed RTC chip. Costs 4 bytes of RTC SRAM.

OLED off before sleepdisplayOff() clears and blanks the panel before the MCU sleeps, eliminating the ~1–2 mA idle current draw of a lit OLED.

ADC on GPIO34 (ADC1) — ADC2 is shared with the Wi-Fi radio and is unreliable when Wi-Fi is active. GPIO34 belongs to ADC1, which is safe to use regardless of radio state.


Serial Output

=== Solar IoT Node - Stage 3 | Boot #1 ===
[OK] SSD1306 OLED initialized at 0x3C
[OK] INA3221 initialized at 0x40
[DATA] CH1 Bus Voltage: 3.300 V
[DATA] Light: 75.8 %
[DATA] CH1 Bus Voltage: 3.301 V
[DATA] Light: 75.6 %
... (every 300 ms for 5 seconds)
[INFO] Sleeping for 25 seconds...

=== Solar IoT Node - Stage 3 | Boot #2 ===
...

Development Stages

Stage 1 — OLED + INA3221 ✅

  • I2C bus initialised with explicit SDA/SCL pins (GPIO21/22)
  • INA3221 reads CH1 bus voltage; result rendered on OLED
  • Fault logged to Serial if no I2C device responds

Stage 2 — LDR Light Sensor ✅

  • LDR + 10 kΩ voltage divider on GPIO34 (ADC1 — safe with Wi-Fi)
  • ADC value clamped to [0, 4095] before conversion to 0–100 %
  • Isolated into its own ldr.h / ldr.cpp module

Stage 3 — Deep Sleep ✅ ← current

  • 30 s duty cycle: 5 s active → 25 s Deep Sleep
  • Single OLED frame shows bus voltage, light %, and boot count
  • Boot count persisted across sleep cycles via RTC_DATA_ATTR
  • Sensors polled every 300 ms while awake

Stage 4 — Wi-Fi + Remote Logging 🔜

  • Connect to Wi-Fi on wake; publish readings via MQTT or HTTP POST
  • Log data to a time-series store (InfluxDB, Google Sheets, or similar)
  • OTA (over-the-air) firmware updates via ArduinoOTA

Stage 5 — Autonomous Solar Operation 🔜

  • 6 V solar panel stepped down via LM2596 buck converter
  • INA3221 CH1: panel voltage / CH2: battery voltage / CH3: load current
  • Fully off-grid — no USB power required

Setup

Prerequisites

  • PlatformIO (VS Code extension or CLI)
  • ESP32 DevKit connected via USB

Steps

# Clone
git clone https://github.com/guyifergan1/solar-monitor.git
cd solar-monitor

# Open in VS Code — PlatformIO resolves and installs all libraries
code .

# Or upload directly from CLI
pio run --target upload

Library dependencies (auto-installed via platformio.ini):

  • adafruit/Adafruit INA3221 Library @ ^1.0.1
  • adafruit/Adafruit GFX Library @ ^1.11.9
  • adafruit/Adafruit SSD1306 @ ^2.5.9

Validation Tests

cd test
pip install -r requirements.txt
pytest test_solar_node.py -v

The suite opens the serial port, reads live output, and verifies:

  • Serial connection established within timeout
  • Voltage readings in valid range (0–26 V)
  • Light readings in valid range (0–100 %)
  • Reading stability across 5 consecutive samples

Skills Demonstrated

Area Detail
Embedded C++ Modular driver design, translation-unit encapsulation
Power management Deep Sleep, OLED power-off, ADC1 vs ADC2 awareness
Hardware integration I2C (multi-device), ADC, voltage divider
Firmware architecture Graceful degradation, F() macro, RTC memory
Testing Python pytest suite against live serial output
Toolchain PlatformIO, library management, multi-platform build

License

MIT — see LICENSE.


Author

Guy Ifergan — Electrical Engineering student
GitHub: @guyifergan1

About

ESP32 solar monitoring node with OLED + INA3221. Modular architecture, non-blocking design.

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors