ESP32 Firmware for Solar & Energy Monitoring
Solar monitoring fails in the field for boring reasons: a sun-fade brownout corrupts the flash mid-write, a Modbus CRC error gets logged as a 0V string, or the inverter changes register maps in a firmware update and your dashboard silently flatlines. We write ESP32/ESP32-S3 firmware that treats every reading as suspect, every link as intermittent, and every power rail as about to drop.
Challenges specific to Energy & Solar
Brownouts corrupt flash mid-write
When clouds drop PV output and the controller dips, the ESP32 browns out exactly while it is committing a log record, leaving a torn write that bricks the filesystem.
Modbus CRC errors logged as real zeros
A noisy RS-485 run between inverter and controller produces CRC failures that naive firmware records as 0A / 0V, faking a dead string and triggering false night-discharge alarms.
Inverter register maps drift across firmware
Vendors silently remap holding registers between inverter firmware revisions, so a hardcoded SoC or status address starts returning the wrong scaled value with no error flag.
Battery SoC drifts without recalibration
Coulomb-counting on a lead-acid or LFP bank drifts hours of error per week unless the firmware re-anchors SoC at the float/rest OCV plateau the controller reports.
Backhaul gaps lose overnight telemetry
Remote solar sites on cellular or LoRa lose connectivity for hours, and firmware that publishes-and-forgets drops the exact battery-depletion window operators need to diagnose.
Heat derates the node before the array
A combiner-box enclosure hits 70C in summer, pushing the ESP32 into thermal throttle and clock drift that skews sample timestamps and Wi-Fi association just when load is peaking.
How GizanTech solves them
- Power-loss-safe logging. 1. Append-only ring buffer in a wear-leveled LittleFS partition with a per-record CRC and the brownout detector (BOD) armed, so a torn write is detected and discarded on boot, never bricking the volume.
- Validated Modbus RTU acquisition. 2. CRC-checked Modbus RTU reads with retry-and-quarantine: a failed frame yields a NULL/stale flag, never a fabricated 0, and per-register scaling factors live in versioned config, not in code.
- Register-map version guarding. 3. We probe an inverter identity/firmware register on connect and select the matching register-map profile, alarming on an unknown map instead of silently misreading SoC or status words.
- OCV-anchored SoC estimation. 4. Coulomb counting fused with rest-voltage (OCV) re-anchoring at the float plateau, with chemistry-specific curves for LFP and lead-acid, keeping SoC error bounded across weeks unattended.
- Store-and-forward backhaul. 5. Time-stamped store-and-forward over MQTT with QoS 1 and on-flash backlog, so a multi-hour cellular/LoRa outage replays the full overnight depletion curve once the link returns.
- Thermal-aware scheduling and OTA. 6. Industrial-temp BOM, internal-temp-gated Wi-Fi/TLS work, and signed A/B OTA with rollback so a remote combiner-box node updates safely and never thermally throttles into a bad flash.
| Parameter | Source | Sample rate | Safe action on out-of-range | Data retention need |
|---|---|---|---|---|
| PV string voltage / current | ADC analog (divider + shunt) or controller Modbus register | 1 Hz | Flag string as faulted, hold last-good, raise low/no-yield alarm; never log fabricated 0 | 1 s raw rolling 48 h, 1 min aggregates 2 yr for degradation trend |
| MPPT charge state | Controller Modbus holding register (bulk/absorb/float/fault enum) | 0.2 Hz | Quarantine on CRC fail / unknown enum, keep prior state, alarm on stuck-bulk | State-change events kept 5 yr; full audit of float transitions |
| Battery SoC | Coulomb count fused with OCV at rest; controller register cross-check | 0.5 Hz internal, re-anchor at float | Clamp 0-100%, flag if drift vs OCV exceeds band, latch low-SoC cutoff | 1 min SoC 2 yr; depletion-window events at full rate |
| Inverter status / AC output | Inverter Modbus RTU (status word, kW, fault code) over RS-485 | 0.2 Hz | Map-version guard; on grid/fault bit, alarm immediately and snapshot pre-fault buffer | Fault codes + 60 s pre-fault ring buffer kept 5 yr |
| Enclosure / heatsink temperature | I2C/1-Wire sensor inside combiner box | 0.1 Hz | On >70C, derate node duty cycle, defer OTA, alarm before throttle | 1 min 1 yr for thermal-correlation analysis |
Go deeper
ESP32 Firmware & IoT Development for other industries
Frequently asked questions
Which solar charge controllers and inverters can you integrate?
Any device exposing Modbus RTU/TCP or analog telemetry: Victron, EPEVER, Morningstar, SMA, Growatt and similar. We build a versioned register-map profile per model so firmware updates that remap registers do not silently break monitoring.
How do you keep battery SoC accurate without a dedicated BMS?
We fuse coulomb counting with open-circuit-voltage re-anchoring at the rest/float plateau using chemistry-specific curves for LFP and lead-acid. That bounds the cumulative drift that pure current integration accumulates over weeks of unattended operation.
What happens to data when a remote site loses connectivity?
Telemetry is time-stamped and written to a flash backlog, then replayed over MQTT QoS 1 when the cellular or LoRa link returns. The overnight battery-depletion window is preserved at full resolution instead of being dropped on publish failure.
Will a brownout from a cloudy-day dip corrupt the logger?
No. We use an append-only, per-record-CRC ring buffer on a wear-leveled partition with the brownout detector armed, so a write interrupted by a power dip is detected and discarded on boot rather than corrupting the filesystem.
Can the firmware update safely on a remote unattended array?
Yes. We ship signed A/B OTA with automatic rollback, and the update is gated on enclosure temperature and link health, so a 70C combiner-box node never flashes during a thermal throttle or a marginal connection.