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)
Response (ESP32 β Portal)
{
"status": "ok" | "error",
"data": { /* response data */ },
"error": "error message (if status=error)"
}
Commands
1. Device Info (device_info)
Request:
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:
Response:
Error Response:
3. WiFi Test (wifi_test)
Tests WiFi connection without saving configuration.
Request:
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:
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:
8. Save & Restart (save_restart)
Saves all configuration to SPIFFS and restarts ESP32.
Request:
Response:
9. Factory Reset (factory_reset)
Erases all configuration and restarts ESP32.
Request:
Response:
10. Get Status (get_status)
Returns current device status and configuration.
Request:
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
- Token Transmission: Device token transmitted ONCE via USB (not WiFi)
- Storage: Token stored encrypted in SPIFFS with ESP32 chip ID as key
- Serial Access: USB Serial only accessible during installation (not in production)
- WiFi Password: Not logged, only stored encrypted
- 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