From d327f9b68a22711da91b189ac0cad66e86218670 Mon Sep 17 00:00:00 2001 From: acidburns Date: Tue, 17 Feb 2026 01:17:35 +0100 Subject: [PATCH] Document sender efficiency and reliability improvements --- README.md | 13 +++++++++++++ Requirements.md | 17 +++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8b6b0b1..90fa26d 100644 --- a/README.md +++ b/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`). - 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`). +- 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. - 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` - 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 ACK wait windows are adaptive (short first window, expanded second window on miss) ## 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` - ESP32 RX buffer enlarged to `8192` - 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: - `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) @@ -81,6 +86,14 @@ Timestamp derivation: 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 For decoded `BatchUp`: diff --git a/Requirements.md b/Requirements.md index e52db15..1e8e46d 100644 --- a/Requirements.md +++ b/Requirements.md @@ -32,6 +32,8 @@ Function names below are C++ references. Rust naming/layout may differ, but the - sender sample cadence 1 Hz. - sender batch cadence 30 s. - 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: - decode/reconstruct sparse timestamps. - 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 and display time rendering: - 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: - 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. @@ -56,6 +61,7 @@ Function names below are C++ references. Rust naming/layout may differ, but the - `AckDown` payload fixed length `7` bytes: - `[flags:1][batch_id_be:2][epoch_utc_be:4]` - `flags bit0 = time_valid` + - sender acceptance window is implementation-adaptive; payload format stays unchanged. - `BatchInput`: - 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` @@ -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. - `bool meter_read(MeterData&)` - 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: - - `parse_obis_ascii_value` + - `detect_obis_field` + - `parse_decimal_fixed` + - `parse_obis_ascii_payload_value` - `parse_obis_ascii_unit_scale` - `hex_nibble` - - `parse_obis_hex_u32` + - `parse_obis_hex_payload_u32` - `meter_debug_log` ## `src/power_manager.cpp` @@ -345,6 +355,7 @@ These functions define end-to-end firmware behavior and must have equivalents: - Queue and sample batching: - `batch_queue_drop_oldest` - `sender_note_rx_reject` + - `sender_log_diagnostics` - `batch_queue_peek` - `batch_queue_enqueue` - `reset_build_counters` @@ -384,6 +395,7 @@ These functions define end-to-end firmware behavior and must have equivalents: - LoRa TX pipeline: - `send_batch_payload` - `send_batch_ack` + - `invalidate_inflight_encode_cache` - `prepare_inflight_from_queue` - `send_inflight_batch` - `send_meter_batch` @@ -405,6 +417,7 @@ These functions define end-to-end firmware behavior and must have equivalents: - Preserve wire compatibility first: - LoRa frame byte layout, CRC16, ACK format, payload schema v3. + - sender optimization changes must not alter payload field meanings. - Preserve persistent storage keys: - Preferences keys (`ssid`, `pass`, `mqhost`, `mqport`, `mquser`, `mqpass`, `ntp1`, `ntp2`, `webuser`, `webpass`, `valid`). - Preserve timing constants and acceptance thresholds: