Skip to main content

ESP32 Firmware for Solar & Energy Monitoring

GizanTech EngineeringIndustrial Firmware TeamUpdated June 15, 2026

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
ParameterSourceSample rateSafe action on out-of-rangeData retention need
PV string voltage / currentADC analog (divider + shunt) or controller Modbus register1 HzFlag string as faulted, hold last-good, raise low/no-yield alarm; never log fabricated 01 s raw rolling 48 h, 1 min aggregates 2 yr for degradation trend
MPPT charge stateController Modbus holding register (bulk/absorb/float/fault enum)0.2 HzQuarantine on CRC fail / unknown enum, keep prior state, alarm on stuck-bulkState-change events kept 5 yr; full audit of float transitions
Battery SoCCoulomb count fused with OCV at rest; controller register cross-check0.5 Hz internal, re-anchor at floatClamp 0-100%, flag if drift vs OCV exceeds band, latch low-SoC cutoff1 min SoC 2 yr; depletion-window events at full rate
Inverter status / AC outputInverter Modbus RTU (status word, kW, fault code) over RS-4850.2 HzMap-version guard; on grid/fault bit, alarm immediately and snapshot pre-fault bufferFault codes + 60 s pre-fault ring buffer kept 5 yr
Enclosure / heatsink temperatureI2C/1-Wire sensor inside combiner box0.1 HzOn >70C, derate node duty cycle, defer OTA, alarm before throttle1 min 1 yr for thermal-correlation analysis
Solar/battery monitoring parameters: acquisition source, rate, out-of-range safe action, and retention

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.