Zum Inhalt

ESP32 USB Serial Command Protocol

Übersicht

Die ESP32-Firmware kommuniziert ΓΌber USB Serial (115200 baud) mit dem Portal WebSerial Tool wΓ€hrend der Installation.

Protokoll: JSON-basierte Commands mit Responses


Command Format

Request (Portal β†’ ESP32)

{
  "cmd": "command_name",
  "data": { /* command-specific data */ }
}

Response (ESP32 β†’ Portal)

{
  "status": "ok" | "error",
  "data": { /* response data */ },
  "error": "error message (if status=error)"
}

Commands

1. Device Info (device_info)

Request:

{
  "cmd": "device_info"
}

Response:

{
  "status": "ok",
  "data": {
    "chip_type": "ESP32-S3",
    "mac_address": "AA:BB:CC:DD:EE:FF",
    "flash_size": 16777216,
    "firmware_version": "v1.0.0",
    "features": ["WiFi", "BLE", "PSRAM"]
  }
}


2. WiFi Configuration (wifi_config)

Request:

{
  "cmd": "wifi_config",
  "data": {
    "ssid": "HomeNetwork",
    "password": "secret123"
  }
}

Response:

{
  "status": "ok",
  "data": {
    "connected": true,
    "ip": "192.168.178.42",
    "rssi": -45
  }
}

Error Response:

{
  "status": "error",
  "error": "Connection timeout - check SSID and password"
}


3. WiFi Test (wifi_test)

Tests WiFi connection without saving configuration.

Request:

{
  "cmd": "wifi_test",
  "data": {
    "ssid": "TestNetwork",
    "password": "test123"
  }
}

Response:

{
  "status": "ok",
  "data": {
    "connected": true,
    "ip": "192.168.1.100",
    "rssi": -52,
    "gateway": "192.168.1.1"
  }
}


4. Inverter Configuration (inverter_config)

Request:

{
  "cmd": "inverter_config",
  "data": {
    "type": "SMA",
    "ip": "192.168.178.50",
    "port": 502,
    "modbus_unit_id": 3
  }
}

Response:

{
  "status": "ok",
  "data": {
    "test_read": true,
    "current_power": 3500.0,
    "model": "Sunny Boy 5.0"
  }
}


5. Inverter Test (inverter_test)

Tests inverter connection without saving configuration.

Request:

{
  "cmd": "inverter_test",
  "data": {
    "type": "SMA",
    "ip": "192.168.178.50",
    "port": 502,
    "modbus_unit_id": 3
  }
}

Response:

{
  "status": "ok",
  "data": {
    "reachable": true,
    "current_power": 4200.0,
    "daily_energy": 15.8,
    "model": "Sunny Boy 5.0",
    "serial_number": "1234567890"
  }
}


6. Device Credentials (device_credentials)

Stores device UUID and authentication token for Portal API access.

Request:

{
  "cmd": "device_credentials",
  "data": {
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "token": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
  }
}

Response:

{
  "status": "ok",
  "data": {
    "saved": true
  }
}


7. Device Settings (device_settings)

Configure device behavior.

Request:

{
  "cmd": "device_settings",
  "data": {
    "display_update_interval": 30,
    "api_upload_interval": 60,
    "timezone": "Europe/Berlin",
    "language": "de"
  }
}

Response:

{
  "status": "ok",
  "data": {
    "applied": true
  }
}


8. Save & Restart (save_restart)

Saves all configuration to SPIFFS and restarts ESP32.

Request:

{
  "cmd": "save_restart"
}

Response:

{
  "status": "ok",
  "data": {
    "message": "Configuration saved. Restarting in 3 seconds..."
  }
}


9. Factory Reset (factory_reset)

Erases all configuration and restarts ESP32.

Request:

{
  "cmd": "factory_reset"
}

Response:

{
  "status": "ok",
  "data": {
    "message": "All configuration erased. Restarting..."
  }
}


10. Get Status (get_status)

Returns current device status and configuration.

Request:

{
  "cmd": "get_status"
}

Response:

{
  "status": "ok",
  "data": {
    "firmware_version": "v1.0.0",
    "uptime": 12345,
    "wifi": {
      "connected": true,
      "ssid": "HomeNetwork",
      "rssi": -48,
      "ip": "192.168.178.42"
    },
    "inverter": {
      "type": "SMA",
      "connected": true,
      "current_power": 3800.0
    },
    "device": {
      "uuid": "550e8400-e29b-41d4-a716-446655440000",
      "registered": true
    },
    "memory": {
      "free_heap": 245760,
      "min_free_heap": 180224
    }
  }
}


Implementation Guidelines

ESP32 Firmware (C++)

#include <ArduinoJson.h>

void handleSerialCommand() {
  if (Serial.available()) {
    String json = Serial.readStringUntil('\n');

    StaticJsonDocument<1024> doc;
    DeserializationError error = deserializeJson(doc, json);

    if (error) {
      sendError("Invalid JSON");
      return;
    }

    const char* cmd = doc["cmd"];
    JsonObject data = doc["data"];

    if (strcmp(cmd, "wifi_config") == 0) {
      handleWiFiConfig(data);
    } else if (strcmp(cmd, "inverter_config") == 0) {
      handleInverterConfig(data);
    } else if (strcmp(cmd, "device_credentials") == 0) {
      handleDeviceCredentials(data);
    } else if (strcmp(cmd, "save_restart") == 0) {
      handleSaveRestart();
    } else {
      sendError("Unknown command");
    }
  }
}

void sendResponse(const char* status, JsonObject data) {
  StaticJsonDocument<1024> doc;
  doc["status"] = status;
  doc["data"] = data;

  serializeJson(doc, Serial);
  Serial.println();
}

void sendError(const char* message) {
  StaticJsonDocument<256> doc;
  doc["status"] = "error";
  doc["error"] = message;

  serializeJson(doc, Serial);
  Serial.println();
}

Portal WebSerial (TypeScript)

async sendCommand(command: string, data?: any): Promise<any> {
  const payload = JSON.stringify({
    cmd: command,
    data: data || {}
  });

  // Send via Serial
  const writer = this.port.writable?.getWriter();
  await writer.write(new TextEncoder().encode(payload + '\n'));
  writer.releaseLock();

  // Wait for response
  const reader = this.port.readable?.getReader();
  const { value } = await reader.read();
  reader.releaseLock();

  const response = JSON.parse(new TextDecoder().decode(value));

  if (response.status !== 'ok') {
    throw new Error(response.error);
  }

  return response.data;
}

Error Handling

Common Error Codes:

Error Description
invalid_json Malformed JSON request
unknown_command Command not recognized
missing_parameter Required parameter missing
wifi_timeout WiFi connection timeout
wifi_auth_failed Wrong password
inverter_unreachable Cannot connect to inverter
inverter_modbus_error Modbus communication failed
storage_error Cannot save to SPIFFS

Testing

Manual Testing (Arduino Serial Monitor)

{"cmd":"device_info"}
{"cmd":"wifi_config","data":{"ssid":"TestNet","password":"test123"}}
{"cmd":"inverter_test","data":{"type":"SMA","ip":"192.168.1.50","port":502,"modbus_unit_id":3}}
{"cmd":"get_status"}

Automated Testing (Portal)

// Test sequence
await esp32.sendCommand('device_info');
await esp32.sendCommand('wifi_test', { ssid: 'Test', password: 'pw' });
await esp32.sendCommand('inverter_test', { type: 'SMA', ip: '192.168.1.50', port: 502 });
await esp32.sendCommand('device_credentials', { uuid: '...', token: '...' });
await esp32.sendCommand('save_restart');

State Machine

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ BOOT            β”‚
β”‚ Wait for Serial β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     wifi_config     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ UNCONFIGURED    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚ WIFI_CONNECTED  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                 β”‚
                                                 β”‚ inverter_config
                                                 β–Ό
                                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                        β”‚ INVERTER_OK     β”‚
                                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                 β”‚
                                                 β”‚ device_credentials
                                                 β–Ό
                                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                        β”‚ CONFIGURED      β”‚
                                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                 β”‚
                                                 β”‚ save_restart
                                                 β–Ό
                                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                        β”‚ RUNNING         β”‚
                                        β”‚ (Normal Mode)   β”‚
                                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Security Considerations

  1. Token Transmission: Device token transmitted ONCE via USB (not WiFi)
  2. Storage: Token stored encrypted in SPIFFS with ESP32 chip ID as key
  3. Serial Access: USB Serial only accessible during installation (not in production)
  4. WiFi Password: Not logged, only stored encrypted
  5. Reset Protection: Factory reset requires physical button press

Configuration Storage (SPIFFS)

File: /config.json

{
  "version": "1.0.0",
  "wifi": {
    "ssid": "encrypted:ABC123...",
    "password": "encrypted:DEF456..."
  },
  "device": {
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "token": "encrypted:GHI789..."
  },
  "inverter": {
    "type": "SMA",
    "ip": "192.168.178.50",
    "port": 502,
    "modbus_unit_id": 3
  },
  "settings": {
    "display_update_interval": 30,
    "api_upload_interval": 60,
    "timezone": "Europe/Berlin",
    "language": "de"
  }
}

Firmware Implementation Checklist

Phase 1: USB Serial Protocol (Hardware benΓΆtigt) - [ ] ArduinoJson library integration - [ ] Serial command parser - [ ] Response formatter - [ ] Error handling

Phase 2: WiFi Management (Hardware benΓΆtigt) - [ ] WiFi connection logic - [ ] Network scanning - [ ] Connection testing - [ ] Status reporting

Phase 3: Inverter Communication (Hardware + Inverter benΓΆtigt) - [ ] Modbus TCP client (SMA, Kostal) - [ ] HTTP client (Delta Solivia) - [ ] Connection testing - [ ] Data reading

Phase 4: Configuration Storage (Hardware benΓΆtigt) - [ ] SPIFFS file system setup - [ ] Config JSON save/load - [ ] Encryption (AES with chip ID) - [ ] Factory reset

Phase 5: Portal API Integration (Hardware benΓΆtigt) - [ ] HTTPS client - [ ] JWT authentication - [ ] Data upload - [ ] Heartbeat

Phase 6: Display Integration (Hardware + Display benΓΆtigt) - [ ] E-Paper driver (LilyGO T5 4.7") - [ ] UI rendering - [ ] Power monitoring display - [ ] Status indicators


Status: Protokoll definiert, Portal-Implementation fertig, Firmware-Implementation wartet auf Hardware-Ankunft