Document sender efficiency and reliability improvements
This commit is contained in:
13
README.md
13
README.md
@@ -10,6 +10,8 @@ Firmware for LilyGO T3 v1.6.1 (`ESP32 + SX1276 + SSD1306`) that runs in two role
|
|||||||
- LoRa transport is wrapped with firmware-level CRC16-CCITT (`src/lora_transport.cpp`).
|
- LoRa transport is wrapped with firmware-level CRC16-CCITT (`src/lora_transport.cpp`).
|
||||||
- Sender meter ingest is decoupled from LoRa waits via FreeRTOS meter reader task + queue on ESP32 (`src/main.cpp`).
|
- Sender meter ingest is decoupled from LoRa waits via FreeRTOS meter reader task + queue on ESP32 (`src/main.cpp`).
|
||||||
- Batch payload codec is schema `v3` with a 30-bit `present_mask` over `[t_last-29, t_last]` (`src/payload_codec.cpp`).
|
- Batch payload codec is schema `v3` with a 30-bit `present_mask` over `[t_last-29, t_last]` (`src/payload_codec.cpp`).
|
||||||
|
- Sender retries reuse cached encoded payload bytes (no re-encode on retry path).
|
||||||
|
- Sender ACK receive windows adapt from observed ACK RTT + miss streak.
|
||||||
- Sender only starts normal metering/transmit flow after valid time bootstrap from receiver ACK.
|
- Sender only starts normal metering/transmit flow after valid time bootstrap from receiver ACK.
|
||||||
- Receiver runs STA mode if stored config is valid and connects, otherwise AP fallback.
|
- Receiver runs STA mode if stored config is valid and connects, otherwise AP fallback.
|
||||||
|
|
||||||
@@ -44,6 +46,7 @@ Payload codec (`schema=3`, magic `0xDDB3`) carries:
|
|||||||
- `flags bit0`: `time_valid`
|
- `flags bit0`: `time_valid`
|
||||||
- ACK is repeated (`ACK_REPEAT_COUNT=3`, `ACK_REPEAT_DELAY_MS=200`)
|
- ACK is repeated (`ACK_REPEAT_COUNT=3`, `ACK_REPEAT_DELAY_MS=200`)
|
||||||
- Sender sets local time only if `time_valid=1` and `epoch >= MIN_ACCEPTED_EPOCH_UTC` (`2026-02-01 00:00:00 UTC`)
|
- Sender sets local time only if `time_valid=1` and `epoch >= MIN_ACCEPTED_EPOCH_UTC` (`2026-02-01 00:00:00 UTC`)
|
||||||
|
- Sender ACK wait windows are adaptive (short first window, expanded second window on miss)
|
||||||
|
|
||||||
## Time Bootstrap and Timezone
|
## Time Bootstrap and Timezone
|
||||||
|
|
||||||
@@ -68,6 +71,8 @@ Implemented by `src/meter_driver.cpp` and sender loop in `src/main.cpp`:
|
|||||||
- UART: `Serial2`, `GPIO34`, `9600 7E1`
|
- UART: `Serial2`, `GPIO34`, `9600 7E1`
|
||||||
- ESP32 RX buffer enlarged to `8192`
|
- ESP32 RX buffer enlarged to `8192`
|
||||||
- Frame detection `/ ... !`, timeout `METER_FRAME_TIMEOUT_MS=3000`
|
- Frame detection `/ ... !`, timeout `METER_FRAME_TIMEOUT_MS=3000`
|
||||||
|
- Single-pass OBIS line dispatch (no repeated multi-key scans per line)
|
||||||
|
- Fixed-point decimal parser (dot/comma decimals), with early-exit once all required OBIS fields are captured
|
||||||
- Parsed OBIS fields:
|
- Parsed OBIS fields:
|
||||||
- `0-0:96.8.0*255` meter Sekundenindex (hex u32)
|
- `0-0:96.8.0*255` meter Sekundenindex (hex u32)
|
||||||
- `1-0:1.8.0` total energy (auto scales Wh -> kWh when unit is Wh)
|
- `1-0:1.8.0` total energy (auto scales Wh -> kWh when unit is Wh)
|
||||||
@@ -81,6 +86,14 @@ Timestamp derivation:
|
|||||||
|
|
||||||
Sender builds sparse 30-slot windows and sends every `METER_SEND_INTERVAL_MS` (`30s`).
|
Sender builds sparse 30-slot windows and sends every `METER_SEND_INTERVAL_MS` (`30s`).
|
||||||
|
|
||||||
|
Sender diagnostics (serial debug mode):
|
||||||
|
- periodic structured `diag:` line with:
|
||||||
|
- meter parser counters (`ok/parse_fail/overflow/timeout`)
|
||||||
|
- meter queue stats (`depth/high-watermark/drops`)
|
||||||
|
- ACK stats (`last RTT`, `EWMA RTT`, `miss streak`, timeout/retry totals)
|
||||||
|
- sender runtime totals (`rx window ms`, `sleep ms`)
|
||||||
|
- diagnostics are local-only (serial); LoRa payload schema/fields are unchanged.
|
||||||
|
|
||||||
## Receiver Behavior
|
## Receiver Behavior
|
||||||
|
|
||||||
For decoded `BatchUp`:
|
For decoded `BatchUp`:
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ Function names below are C++ references. Rust naming/layout may differ, but the
|
|||||||
- sender sample cadence 1 Hz.
|
- sender sample cadence 1 Hz.
|
||||||
- sender batch cadence 30 s.
|
- sender batch cadence 30 s.
|
||||||
- sync-request cadence 15 s while unsynced.
|
- sync-request cadence 15 s while unsynced.
|
||||||
|
- sender retransmits reuse cached encoded payload bytes for same inflight batch.
|
||||||
|
- sender ACK receive window is adaptive from airtime + observed ACK RTT, with expanded second window on miss.
|
||||||
- Receiver behavior:
|
- Receiver behavior:
|
||||||
- decode/reconstruct sparse timestamps.
|
- decode/reconstruct sparse timestamps.
|
||||||
- ACK accepted batches promptly.
|
- ACK accepted batches promptly.
|
||||||
@@ -44,6 +46,9 @@ Function names below are C++ references. Rust naming/layout may differ, but the
|
|||||||
- `WEB_AUTH_REQUIRE_AP=true`
|
- `WEB_AUTH_REQUIRE_AP=true`
|
||||||
- Web and display time rendering:
|
- Web and display time rendering:
|
||||||
- local timezone from `TIMEZONE_TZ`.
|
- local timezone from `TIMEZONE_TZ`.
|
||||||
|
- Sender diagnostics:
|
||||||
|
- structured sender diagnostics are emitted to serial debug output only.
|
||||||
|
- diagnostics do not change LoRa payload schema or remap payload fields.
|
||||||
- SD logging:
|
- SD logging:
|
||||||
- CSV columns include both `ts_utc` and `ts_hms_local`.
|
- CSV columns include both `ts_utc` and `ts_hms_local`.
|
||||||
- history parser supports both current (`ts_utc,ts_hms_local,p_w,...`) and legacy (`ts_utc,p_w,...`) layouts.
|
- history parser supports both current (`ts_utc,ts_hms_local,p_w,...`) and legacy (`ts_utc,p_w,...`) layouts.
|
||||||
@@ -56,6 +61,7 @@ Function names below are C++ references. Rust naming/layout may differ, but the
|
|||||||
- `AckDown` payload fixed length `7` bytes:
|
- `AckDown` payload fixed length `7` bytes:
|
||||||
- `[flags:1][batch_id_be:2][epoch_utc_be:4]`
|
- `[flags:1][batch_id_be:2][epoch_utc_be:4]`
|
||||||
- `flags bit0 = time_valid`
|
- `flags bit0 = time_valid`
|
||||||
|
- sender acceptance window is implementation-adaptive; payload format stays unchanged.
|
||||||
- `BatchInput`:
|
- `BatchInput`:
|
||||||
- fixed arrays length `30` (`energy_wh`, `p1_w`, `p2_w`, `p3_w`)
|
- fixed arrays length `30` (`energy_wh`, `p1_w`, `p2_w`, `p3_w`)
|
||||||
- `present_mask` must satisfy: only low 30 bits used and `bit_count == n`
|
- `present_mask` must satisfy: only low 30 bits used and `bit_count == n`
|
||||||
@@ -107,11 +113,15 @@ Function names below are C++ references. Rust naming/layout may differ, but the
|
|||||||
- parse OBIS values and set meter data fields.
|
- parse OBIS values and set meter data fields.
|
||||||
- `bool meter_read(MeterData&)`
|
- `bool meter_read(MeterData&)`
|
||||||
- compatibility wrapper around poll+parse.
|
- compatibility wrapper around poll+parse.
|
||||||
|
- `void meter_get_stats(MeterDriverStats&)`
|
||||||
|
- expose parser/UART counters for sender-local diagnostics.
|
||||||
- Internal parse helpers to preserve numeric behavior:
|
- Internal parse helpers to preserve numeric behavior:
|
||||||
- `parse_obis_ascii_value`
|
- `detect_obis_field`
|
||||||
|
- `parse_decimal_fixed`
|
||||||
|
- `parse_obis_ascii_payload_value`
|
||||||
- `parse_obis_ascii_unit_scale`
|
- `parse_obis_ascii_unit_scale`
|
||||||
- `hex_nibble`
|
- `hex_nibble`
|
||||||
- `parse_obis_hex_u32`
|
- `parse_obis_hex_payload_u32`
|
||||||
- `meter_debug_log`
|
- `meter_debug_log`
|
||||||
|
|
||||||
## `src/power_manager.cpp`
|
## `src/power_manager.cpp`
|
||||||
@@ -345,6 +355,7 @@ These functions define end-to-end firmware behavior and must have equivalents:
|
|||||||
- Queue and sample batching:
|
- Queue and sample batching:
|
||||||
- `batch_queue_drop_oldest`
|
- `batch_queue_drop_oldest`
|
||||||
- `sender_note_rx_reject`
|
- `sender_note_rx_reject`
|
||||||
|
- `sender_log_diagnostics`
|
||||||
- `batch_queue_peek`
|
- `batch_queue_peek`
|
||||||
- `batch_queue_enqueue`
|
- `batch_queue_enqueue`
|
||||||
- `reset_build_counters`
|
- `reset_build_counters`
|
||||||
@@ -384,6 +395,7 @@ These functions define end-to-end firmware behavior and must have equivalents:
|
|||||||
- LoRa TX pipeline:
|
- LoRa TX pipeline:
|
||||||
- `send_batch_payload`
|
- `send_batch_payload`
|
||||||
- `send_batch_ack`
|
- `send_batch_ack`
|
||||||
|
- `invalidate_inflight_encode_cache`
|
||||||
- `prepare_inflight_from_queue`
|
- `prepare_inflight_from_queue`
|
||||||
- `send_inflight_batch`
|
- `send_inflight_batch`
|
||||||
- `send_meter_batch`
|
- `send_meter_batch`
|
||||||
@@ -405,6 +417,7 @@ These functions define end-to-end firmware behavior and must have equivalents:
|
|||||||
|
|
||||||
- Preserve wire compatibility first:
|
- Preserve wire compatibility first:
|
||||||
- LoRa frame byte layout, CRC16, ACK format, payload schema v3.
|
- LoRa frame byte layout, CRC16, ACK format, payload schema v3.
|
||||||
|
- sender optimization changes must not alter payload field meanings.
|
||||||
- Preserve persistent storage keys:
|
- Preserve persistent storage keys:
|
||||||
- Preferences keys (`ssid`, `pass`, `mqhost`, `mqport`, `mquser`, `mqpass`, `ntp1`, `ntp2`, `webuser`, `webpass`, `valid`).
|
- Preferences keys (`ssid`, `pass`, `mqhost`, `mqport`, `mquser`, `mqpass`, `ntp1`, `ntp2`, `webuser`, `webpass`, `valid`).
|
||||||
- Preserve timing constants and acceptance thresholds:
|
- Preserve timing constants and acceptance thresholds:
|
||||||
|
|||||||
Reference in New Issue
Block a user