Zum Inhalt

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:

  1. Primary Mission: Read solar inverter data β†’ POST to SolarLog API (Cloud)
  2. 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

  1. HTTPS Only: All API communication via TLS 1.2+
  2. API Key Storage: Store in NVS (encrypted)
  3. WiFi Credentials: Store in NVS (encrypted)
  4. OTA Updates: Signed firmware only (future)
  5. No Hardcoded Secrets: All credentials via config

Last Updated: 2025-10-23
Version: 0.1.0-alpha
Status: πŸ“ Design Phase