MPPT Charger

MPPT Charger

Open in ChatGPT
Ask ChatGPT about this page
Open in Claude
Ask Claude about this page

Software

Software

Firmware architecture, start-up behaviour, configuration and the web interface protocol. For the method-by-method API see API_REFERENCE.md.

Layout

src/main.cpp                    Application: setup() and loop()
lib/ModbeeMPPT/src/
  ModbeeMPPT.h/.cpp             Top-level controller: start-up, periodic tasks, status LED
  ModbeeMpptAPI.h/.cpp          Validated, unit-based interface to the BQ25798
  ModbeeMpptConfig.h/.cpp       Settings: defaults, validation, JSON persistence on LittleFS
  ModbeeMpptWebServer.h/.cpp    Wi-Fi access point, captive DNS, HTTP and WebSocket server
  ModbeeMpptPowerSave.h/.cpp    Wi-Fi timeout, WIFI button, CPU frequency, light sleep
  ModbeeMpptLog.h/.cpp          Energy and peak-power statistics persisted to LittleFS
  ModbeeMpptDebug.h/.cpp        Serial status and register dumps
  ModbeeMpptGlobal.h            Common includes
lib/bq25798/                    BQ25798 register driver (derived from the Adafruit library)
data/                           Web interface (LittleFS image)

Third-party libraries (vendored in lib/):

Library Version License Used for
ESP32_SoftWire 1.0.2 MIT Software I2C to the BQ25798
ESPAsyncWebServer 3.9.4 LGPL-3.0 HTTP and WebSocket server
AsyncTCP 3.4.5 LGPL-3.0 TCP for ESPAsyncWebServer
ArduinoJson 7.4.1 MIT Settings file and WebSocket messages
FastLED 3.9.16 MIT WS2812B status LED

Application

#include "ModbeeMpptGlobal.h"
#include "ModbeeMPPT.h"

ModbeeMPPT modbeeMPPT;

void setup() {
  Serial.begin(115200);
  modbeeMPPT.initializeLEDs();
  modbeeMPPT.begin();          // I2C, charger, settings
  modbeeMPPT.initWebServer();  // Wi-Fi AP, DNS, HTTP, WebSocket
}

void loop() {
  modbeeMPPT.loop();           // periodic tasks, LED, web server, power save
}

src/main.cpp also prints printQuickStatus() every 2 seconds. Swap in one of the other print*() functions listed there for more detail.

Start-up sequence

ModbeeMPPT::begin() does the following, in order:

  1. Radios and CPU. Turns Wi-Fi and Bluetooth off and runs the CPU at 80 MHz.
  2. Charger bus. Starts software I2C on GPIO3 (SDA) and GPIO2 (SCL) to the BQ25798 at address 0x6B.
  3. Settings. Mounts LittleFS and loads /config/mppt_config.json. If the file is missing or fails validation, the defaults are used.
  4. Fixed charger settings (applyCriticalSettings()):
    • ADC on: continuous, 15-bit
    • BQ25798 I2C watchdog disabled, so the charger keeps the host settings if the ESP32-C3 resets or stalls
    • HIZ off, backup mode off
    • 1.5 MHz switching
    • input current optimiser (ICO) off, so it does not interfere with MPPT
    • battery discharge current sensing on
  5. Saved settings. Writes them to the charger: cell count, charge voltage and current, minimum system voltage, termination, recharge and pre-charge, input limits, VAC OVP, safety timers, MPPT and PFM/OOA.
  6. Battery check. Reads the battery voltage and estimates state of charge.
  7. Charging enabled. This happens in all cases, so the board recovers and charges unattended after any power loss. If the battery reads missing or below 5 %, the board starts in low-power boot mode with the access point off until the battery recovers above 8 % while charging, or the WIFI button is pressed.
  8. Statistics and power saving. Loads the saved energy statistics and starts the power-save manager.

Note that until step 5 runs, the BQ25798 operates on its own power-on defaults, which the PROG resistor selects. See HARDWARE.md for why the battery should be connected first.

Runtime

Task Interval
Status LED update 1 s
WebSocket data push (when a client is connected) 1 s
Battery presence check 30 s (intervals.battery_check)
State-of-charge check 60 s (intervals.soc_check)
Re-apply fixed charger settings 60 s
Re-apply all saved settings 5 min
Save energy statistics 5 min

Interval changes made in the web interface take effect after a restart.

Power saving

  • Access point lifetime. The access point starts at boot (except in low-power boot) and turns off 5 minutes after boot or after the last WIFI button press. A press turns it back on for 5 minutes.
  • CPU frequency. The CPU runs at 80 MHz, and at 160 MHz while the access point is on after a button press.
  • Light sleep. When the state of charge is below 20 %, the ESP32-C3 light-sleeps for 10 s per minute. It wakes on its timer or on the WIFI button. The charger keeps running while the MCU sleeps.

Status LED (WS2812B)

Colour Meaning
Orange Starting up
Red A BQ25798 fault or regulation flag is set
Yellow Charging
Green Charge complete (termination done)
Blue Not charging

Settings file

Settings are stored as JSON in /config/mppt_config.json on LittleFS. Default contents:

{
  "battery":   { "type": 1, "cell_count": 3, "charge_voltage": 12.6, "charge_current": 1.0, "min_system_voltage": 10.0 },
  "charging":  { "termination_current": 0.12, "recharge_threshold": 0.4, "precharge_current": 0.2, "precharge_voltage_threshold": 3 },
  "input":     { "voltage_limit": 22.0, "current_limit": 3.0, "vac_ovp_threshold": 26.0 },
  "timers":    { "fast_charge_enable": true, "fast_charge_timer": 2, "precharge_enable": true, "precharge_timer": 0, "topoff_timer": 1 },
  "mppt":      { "voc_percent": 5, "voc_delay": 1, "voc_rate": 0, "enable": true },
  "power":     { "pfm_forward_enable": false, "ooa_forward_enable": true },
  "intervals": { "battery_check": 30000, "soc_check": 60000, "config_apply": 300000 },
  "version": "1.0"
}

Enumerated fields:

Field Values
battery.type 0 LiFePO4, 1 Li-Po / Li-ion, 2 lead-acid, 3 custom
charging.precharge_voltage_threshold 0 = 15 %, 1 = 62.2 %, 2 = 66.7 %, 3 = 71.4 % of VREG
timers.fast_charge_timer 0 = 5 h, 1 = 8 h, 2 = 12 h, 3 = 24 h
timers.precharge_timer 0 = 2 h, 1 = 0.5 h
timers.topoff_timer 0 = off, 1 = 15 min, 2 = 30 min, 3 = 45 min
mppt.voc_percent 0-7 = 56.25, 62.5, 68.75, 75, 81.25, 87.5, 93.75, 100 % of VOC
mppt.voc_delay 0 = 50 ms, 1 = 300 ms, 2 = 2 s, 3 = 5 s
mppt.voc_rate 0 = 30 s, 1 = 2 min, 2 = 10 min, 3 = 30 min

input.voltage_limit is the VINDPM setting. While MPPT is enabled, the BQ25798 overwrites VINDPM after each VOC measurement.

Validation

ModbeeMpptConfig::validateConfig() checks every value before it is saved or applied. For the battery, the charge voltage must:

  • fall within the BQ25798 window for the cell count: 1S 3.0-4.99 V, 2S 5.0-9.99 V, 3S 10.0-13.99 V, 4S 14.0-18.8 V;
  • not exceed 3.65 V/cell for LiFePO4 or 4.35 V/cell for Li-Po.

A save from the web interface that fails validation is rejected, and the previous settings stay in force.

Settings can also be changed in code:

modbeeMPPT.config.data.charge_current = 2.0f;   // A
if (modbeeMPPT.config.validateConfig()) {
  modbeeMPPT.config.saveConfig();
  modbeeMPPT.config.applyToMPPT(modbeeMPPT.api);
}

Web server

Item Value
Wi-Fi mode Access point only
SSID / password ModbeeMPPT / none (open)
Address 192.168.4.1, with captive DNS for all names
HTTP GET / → index.html, GET /settings → settings.html, GET /debug → debug.html; other LittleFS files served statically; unknown paths redirect to /
WebSocket ws://192.168.4.1/ws, JSON text messages

To change the SSID or add a password, edit WIFI_SSID and WIFI_PASSWORD in lib/ModbeeMPPT/src/ModbeeMpptWebServer.h. A WPA2 password must be at least 8 characters.

WebSocket protocol

Client to device, {"command": "<name>", ...}:

Command Extra fields Reply type
getSystemData data
getSettings settings
getDebugData debug
saveSettings settings: object with any of the keys below status
resetDefaults (alias resetSettings) status
resetStat domain: vin1, vin2, vbus, vbat or vsys status
resetBatteryAmpStats status
resetBatteryDischargePowerStats status

saveSettings keys:

  • Battery: batteryType, cellCount, chargeVoltage, chargeCurrent, systemVoltage
  • Charging: termCurrent, rechargeThreshold, prechargeCurrent, prechargeVoltageThreshold
  • Input: inputVoltage, inputCurrent, vacOvp
  • Timers: chargeTimerEnable, chargeTimer, prechargeTimerEnable, prechargeTimer, topoffTimer
  • MPPT: mpptEnable, vocPercent, vocDelay, vocRate
  • Power: pfmForwardEnable, ooaForwardEnable
  • Intervals: batteryCheckInterval, socCheckInterval

status replies are {"type": "status", "success": true|false, "message": "..."}.

data messages are pushed to all clients every second. They contain voltage, current and power for VAC1, VAC2, VBUS, the battery and the system rail. They also carry:

  • Charge: state of charge, charge state and isCharging
  • Status: fault flag, battery and die temperature
  • Statistics: energy totals and peak values

The exact key list is in ModbeeMpptWebServer::getSystemData().

Serial diagnostics

Function Output
printQuickStatus() Essential status
printStatus() Full status
printPowerMeasurements() Input, battery and system power
printConfiguration() Current charger settings
printFaults() Fault status
printRegisterDebug() Decoded BQ25798 status registers
printComprehensiveBatteryStatus() Battery voltage and SOC detail

Build

pio run                  # build
pio run -t upload        # flash firmware
pio run -t uploadfs      # flash web interface
pio device monitor       # serial console, 115200 baud

The lolin_c3_mini board definition matches the ESP32-C3FH4 on this board (4 MB flash, native USB). The firmware uses about 92 % of the default 1.25 MB application partition.

Firmware status

Feature Status
BQ25798 charging, MPPT, dual input, telemetry Implemented
Web dashboard, settings, debug pages Implemented
Settings persistence, energy statistics Implemented
WIFI button, access point timeout, light sleep Implemented
Modbus RTU over RS485 Planned (hardware fitted)
I2C target (slave) telemetry on the Grove port Planned (hardware fitted)
Long-press WIFI button factory reset Planned
Bluetooth LE Not implemented (radio is turned off)
Last updated 2 hours ago
Was this helpful?
Thanks!