ARCHITECTURE.md - INVERTER-ESP System Architecture
Complete system design and data flow for INVERTER-ESP project.
π― System Overview
INVERTER-ESP is a dual-component solar inverter monitoring system:
- Primary Mission: Read solar inverter data β POST to SolarLog API (Cloud)
- Secondary Mission: Display current production on local E-Paper display
Architecture Type: API-First Design (not web server)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β INVERTER-ESP System β
β β
β Solar Inverter (LAN) βββββ β
β β β
β βΌ β
β βββββββββββββββββββ β
β β ESP32-S3 β β
β β β β
β β β’ WiFi Client β β
β β β’ LVGL UI β β
β β β’ NVS Storage β β
β β β’ Deep Sleep β β
β βββββββββββββββββββ β
β β β β
β β β β
β E-Paper Display SolarLog API β
β (Local View) (Cloud Storage) β
β 296Γ128 px https://...karma.organic β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π¦ Component Architecture
Component Diagram
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ESP32-S3 β
β β
β βββββββββββββββββ βββββββββββββββββ ββββββββββββββββ β
β β Network β β Display β β Storage β β
β β Layer β β Layer β β Layer β β
β βββββββββββββββββ€ βββββββββββββββββ€ ββββββββββββββββ€ β
β β β’ WiFi Mgr β β β’ LVGL Core β β β’ NVS Config β β
β β β’ API Client β β β’ E-Paper Drv β β β’ Data Cache β β
β β β’ Inverter β β β’ UI Screens β β β’ Preferencesβ β
β β Client β β β’ Button Inputβ β β β
β βββββββββββββββββ βββββββββββββββββ ββββββββββββββββ β
β β β β β
β ββββββββββββββββββββ΄βββββββββββββββββββ β
β β β
β βββββββββββΌββββββββββ β
β β Main Control β β
β β Loop & Power β β
β β Management β β
β βββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Module Breakdown
1. Network Layer (firmware/src/network/)
Files:
- wifi_manager.h/.cpp - WiFi connection & reconnection logic
- solarlog_api.h/.cpp - SolarLog API HTTP client (HTTPS POST)
- inverter_client.h/.cpp - Solar inverter data reader (protocol TBD)
Responsibilities: - Establish WiFi connection - Handle WiFi disconnections & auto-reconnect - POST data to SolarLog API (with retry logic) - Read power/energy data from solar inverter - Handle network errors (timeout, auth, server errors)
Key Functions:
bool wifi_connect(const char* ssid, const char* password);
bool wifi_is_connected();
void wifi_disconnect();
bool api_post_data(float power_w, float energy_kwh, const char* status);
bool api_post_with_retry(float power, float energy, int retries);
float inverter_get_power(); // Returns current power in watts
float inverter_get_energy(); // Returns today's energy in kWh
bool inverter_is_online();
2. Display Layer (firmware/src/display/ & firmware/src/ui/)
Files:
- epaper_driver.h/.cpp - E-Paper LVGL display driver
- screen_dashboard.h/.cpp - Main dashboard UI
- screen_history.h/.cpp - 7-day history chart
- screen_sysinfo.h/.cpp - System info & diagnostics
- screen_settings.h/.cpp - WiFi & API configuration
- button_input.h/.cpp - Physical button handler
Responsibilities: - Initialize E-Paper display (296Γ128, monochrome) - Render LVGL UI on E-Paper - Handle button input (3 buttons: Next, Prev, Action) - Update display with latest data (every 30-60s) - Optimize E-Paper refresh (partial updates)
Key Functions:
void epaper_init();
void epaper_refresh_full(); // Full screen refresh (2-15s)
void epaper_refresh_partial(); // Partial update (<1s)
void screen_dashboard_create();
void screen_dashboard_update(float power, float energy);
void button_init();
button_event_t button_read(); // Returns BUTTON_NEXT, BUTTON_PREV, BUTTON_ACTION
3. Storage Layer (firmware/src/storage/)
Files:
- nvs_config.h/.cpp - NVS (Non-Volatile Storage) wrapper
- data_cache.h/.cpp - Cache failed API uploads
Responsibilities: - Store WiFi credentials - Store API key - Store configuration (update interval, display settings) - Cache data points when API upload fails - Provide config getter/setter functions
Key Functions:
bool config_load();
bool config_save();
String config_get_wifi_ssid();
void config_set_wifi_ssid(const char* ssid);
String config_get_api_key();
void config_set_api_key(const char* key);
void cache_add_data_point(float power, float energy);
int cache_get_count();
void cache_flush_all(); // Upload cached data when online
4. Main Control (firmware/src/main.cpp)
Responsibilities: - Initialize all subsystems - Run main update loop - Handle deep sleep power management - Coordinate data flow
Pseudocode:
void setup() {
Serial.begin(115200);
// 1. Initialize subsystems
lv_init();
epaper_init();
button_init();
config_load();
// 2. Connect WiFi
String ssid = config_get_wifi_ssid();
String pass = config_get_wifi_password();
wifi_connect(ssid.c_str(), pass.c_str());
// 3. Create UI
screen_dashboard_create();
// 4. Sync NTP time
configTime(0, 0, "pool.ntp.org");
}
void loop() {
// LVGL task handler
lv_timer_handler();
// Check if update needed (every 5 min)
static unsigned long last_update = 0;
if (millis() - last_update > UPDATE_INTERVAL) {
update_data_and_display();
last_update = millis();
}
// Handle button input
button_event_t event = button_read();
if (event == BUTTON_NEXT) {
screen_next();
}
delay(10);
}
void update_data_and_display() {
// 1. Read inverter data
float power = inverter_get_power();
float energy = inverter_get_energy();
// 2. POST to API
if (api_post_with_retry(power, energy, 3)) {
Serial.println("β
Data uploaded");
cache_flush_all(); // Upload cached data
} else {
Serial.println("β Upload failed, caching");
cache_add_data_point(power, energy);
}
// 3. Update display
screen_dashboard_update(power, energy);
epaper_refresh_partial(); // Fast update
}
π Data Flow
Primary Data Flow (API Upload)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. Solar Inverter β
β (HTTP/Modbus/MQTT) β
β β’ Current Power: 3500W β
β β’ Today's Energy: 12.5kWh β
β β’ Status: Online β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββ
β
β READ (inverter_client.cpp)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. ESP32-S3 Processing β
β β’ Parse inverter response β
β β’ Add timestamp (NTP) β
β β’ Format JSON payload β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββ
β
β HTTPS POST (solarlog_api.cpp)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. SolarLog API β
β https://solarlog-api.karma.organic/api/v1/... β
β β’ Receive JSON payload β
β β’ Store in database β
β β’ Return 200 OK β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β (Optional) GET /history for display
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 4. Data Storage & Processing β
β β’ Historical graphs β
β β’ Analytics dashboard β
β β’ Alerts & notifications β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Secondary Data Flow (Local Display)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. ESP32-S3 (Cached Data) β
β β’ power_w = 3500 β
β β’ energy_kwh = 12.5 β
β β’ wifi_status = Connected β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββ
β
β RENDER (screen_dashboard.cpp)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. LVGL Rendering Engine β
β β’ Create labels: "3.5 kW", "12.5 kWh" β
β β’ Draw icons: WiFi, Cloud, Battery β
β β’ Render to framebuffer (296Γ128Γ1bit) β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββ
β
β FLUSH (epaper_driver.cpp)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. E-Paper Display Driver β
β β’ Send framebuffer via SPI β
β β’ Trigger partial/full refresh β
β β’ Wait for BUSY pin LOW β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββ
β
β DISPLAY (E-Paper Hardware)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 4. E-Paper Display (296Γ128) β
β βββββββββββββββββββββββββββββββββββ β
β β SOLAR PRODUCTION β β
β β β β
β β 3.5 kW β β
β β β β
β β Today: 12.5 kWh β β
β β Status: βββ WiFi OK β β
β βββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π UI State Machine
Screen Navigation
ββββββββββββββββ Button 1 ββββββββββββββββ
β Dashboard βββββββ(Next)ββββββββΆβ History β
β Screen 1 βββββββ(Prev)βββββββββ€ Screen 2 β
ββββββββ¬ββββββββ Button 2 ββββββββ¬ββββββββ
β β
Button 1 β Button 1 β
(Next) β (Next) β
βΌ βΌ
ββββββββββββββββ ββββββββββββββββ
β Settings ββββββ(Next)ββββββββββ SysInfo β
β Screen 4 ββββββ(Prev)βββββββββΆβ Screen 3 β
ββββββββββββββββ ββββββββββββββββ
β
Button 3 β
(Action)β
βΌ
ββββββββββββββββ
β WiFi Setup β
β (Subscreen) β
ββββββββββββββββ
Button Event Handling
typedef enum {
BUTTON_NONE,
BUTTON_NEXT, // GPIO 0
BUTTON_PREV, // GPIO 14
BUTTON_ACTION // GPIO 21
} button_event_t;
void handle_button_event(button_event_t event) {
static uint8_t current_screen = 0;
switch (event) {
case BUTTON_NEXT:
current_screen = (current_screen + 1) % 4;
screen_switch(current_screen);
break;
case BUTTON_PREV:
current_screen = (current_screen + 3) % 4; // +3 = -1 mod 4
screen_switch(current_screen);
break;
case BUTTON_ACTION:
screen_action(current_screen); // Screen-specific action
break;
case BUTTON_NONE:
default:
break;
}
}
β‘ Power Management
Deep Sleep Strategy
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Power State Timeline (5-minute update interval) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
0s 30s 300s
βββββββββββββββββββββΌβββββββββββββββββββββββββββββββ€
β ACTIVE β DEEP SLEEP β
β ~150mA β ~10Β΅A β
β β β
β β’ WiFi Connect β β’ CPU off β
β β’ Read Inverter β β’ WiFi off β
β β’ POST API β β’ Display retains image β
β β’ Update Display β β’ RTC alarm timer active β
βββββββββββββββββββββ΄βββββββββββββββββββββββββββββββ
β²
β
Wakeup after 270s
Implementation
#define UPDATE_INTERVAL_S (5 * 60) // 5 minutes
#define ACTIVE_TIME_S 30 // 30 seconds active
void enter_deep_sleep() {
Serial.printf("π€ Entering deep sleep for %d seconds...\n", UPDATE_INTERVAL_S - ACTIVE_TIME_S);
// Configure wake timer
esp_sleep_enable_timer_wakeup((UPDATE_INTERVAL_S - ACTIVE_TIME_S) * 1000000ULL);
// Power down peripherals
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
// Enter deep sleep
esp_deep_sleep_start();
// Execution resumes in setup() after wake
}
void loop() {
static unsigned long loop_start = millis();
// Update data & display
if (!data_updated) {
update_data_and_display();
data_updated = true;
}
// LVGL task handler
lv_timer_handler();
// After 30 seconds, enter deep sleep
if (millis() - loop_start > ACTIVE_TIME_S * 1000) {
enter_deep_sleep();
}
delay(10);
}
π§ͺ Testing Strategy
Unit Tests (tests/)
// test_wifi_manager.cpp
TEST_CASE("WiFi connects successfully") {
REQUIRE(wifi_connect("TestSSID", "TestPass") == true);
REQUIRE(wifi_is_connected() == true);
}
// test_api_client.cpp
TEST_CASE("API POST returns success") {
bool result = api_post_data(3500, 12.5, "online");
REQUIRE(result == true);
}
// test_inverter_client.cpp
TEST_CASE("Inverter data parsing") {
float power = inverter_get_power();
REQUIRE(power > 0);
REQUIRE(power < 10000); // Sanity check
}
Integration Tests
// Test full data flow
void test_full_data_flow() {
// 1. Read inverter
float power = inverter_get_power();
float energy = inverter_get_energy();
// 2. POST to API
bool api_ok = api_post_data(power, energy, "online");
assert(api_ok);
// 3. Update display
screen_dashboard_update(power, energy);
epaper_refresh_full();
Serial.println("β
Full data flow test PASSED");
}
π Performance Requirements
| Metric | Target | Notes |
|---|---|---|
| WiFi Connect Time | < 5s | First boot, WPA2 |
| API Upload Time | < 3s | HTTPS POST with retry |
| E-Paper Refresh | 2-15s | Full refresh (hardware limit) |
| Partial Refresh | < 1s | Value updates only |
| Deep Sleep Current | < 20Β΅A | With RTC timer enabled |
| Active Current | < 200mA | WiFi TX peak |
| Memory Usage (RAM) | < 100KB | LVGL + network buffers |
| Flash Usage | < 1.5MB | With OTA partition |
π Security Considerations
- HTTPS Only: All API communication via TLS 1.2+
- API Key Storage: Store in NVS (encrypted)
- WiFi Credentials: Store in NVS (encrypted)
- OTA Updates: Signed firmware only (future)
- No Hardcoded Secrets: All credentials via config
Last Updated: 2025-10-23
Version: 0.1.0-alpha
Status: π Design Phase