From 0577464ec5266300f36e36a9890f0432fc10d8e0 Mon Sep 17 00:00:00 2001 From: acidburns Date: Fri, 20 Feb 2026 23:29:50 +0100 Subject: [PATCH] refactor: stabilize legacy-core linking and header ownership - Make include/ the canonical declarations for data_model/html_util/json_codec and convert dd3_legacy_core header copies to thin forwarders. - Add stable public forwarders for app_context/receiver_pipeline/sender_state_machine and update refactor smoke test to stop using ../../src includes. - Force-link dd3_legacy_core from setup() to ensure deterministic PlatformIO LDF linking across firmware envs. - Refresh docs (README, Requirements, docs/TESTS.md) to reflect current module paths and smoke-test include strategy. --- README.md | 8 +-- Requirements.md | 9 +-- docs/TESTS.md | 2 +- include/app_context.h | 3 + include/receiver_pipeline.h | 3 + include/sender_state_machine.h | 3 + lib/dd3_legacy_core/include/data_model.h | 59 +------------------ lib/dd3_legacy_core/include/html_util.h | 6 +- lib/dd3_legacy_core/include/json_codec.h | 5 +- src/main.cpp | 2 + .../test_refactor_smoke.cpp | 6 +- 11 files changed, 27 insertions(+), 79 deletions(-) create mode 100644 include/app_context.h create mode 100644 include/receiver_pipeline.h create mode 100644 include/sender_state_machine.h diff --git a/README.md b/README.md index 79f5139..5a1e810 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ Firmware for LilyGO T3 v1.6.1 (`ESP32 + SX1276 + SSD1306`) that runs in two role - Single codebase, role selected at boot by `detect_role()` (`src/config.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`). -- Batch payload codec is schema `v3` with a 30-bit `present_mask` over `[t_last-29, t_last]` (`src/payload_codec.cpp`). +- Sender meter ingest is decoupled from LoRa waits via FreeRTOS meter reader task + queue on ESP32 (`src/sender_state_machine.cpp`). +- Batch payload codec is schema `v3` with a 30-bit `present_mask` over `[t_last-29, t_last]` (`lib/dd3_legacy_core/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 catch-up mode drains backlog with immediate extra sends when more than one batch is queued (still ACK-gated, single inflight batch). @@ -73,7 +73,7 @@ Timezone: ## Sender Meter Path -Implemented by `src/meter_driver.cpp` and sender loop in `src/main.cpp`: +Implemented by `src/meter_driver.cpp` and sender loop in `src/sender_state_machine.cpp`: - UART: `Serial2`, `GPIO34`, `9600 7E1` - ESP32 RX buffer enlarged to `8192` - Frame detection `/ ... !`, timeout `METER_FRAME_TIMEOUT_MS=3000` @@ -124,7 +124,7 @@ State topic: Fault topic (retained): - `smartmeter//faults` -State JSON (`src/json_codec.cpp`) includes: +State JSON (`lib/dd3_legacy_core/src/json_codec.cpp`) includes: - `id`, `ts`, `e_kwh` - `p_w`, `p1_w`, `p2_w`, `p3_w` - `bat_v`, `bat_pct` diff --git a/Requirements.md b/Requirements.md index 996dd51..1d2cb9f 100644 --- a/Requirements.md +++ b/Requirements.md @@ -118,14 +118,14 @@ Sender state machine invariants must remain behavior-equivalent: - `DeviceRole detect_role()` - configure role pin input pulldown and map to sender/receiver role. -## `src/data_model.cpp` +## `lib/dd3_legacy_core/src/data_model.cpp` - `void init_device_ids(uint16_t&, char*, size_t)` - read MAC, derive short ID, format canonical device ID. - `const char *rx_reject_reason_text(RxRejectReason)` - stable mapping for diagnostics and payloads. -## `src/html_util.cpp` +## `lib/dd3_legacy_core/src/html_util.cpp` - `String html_escape(const String&)` - escape `& < > " '`. @@ -218,7 +218,7 @@ Sender state machine invariants must remain behavior-equivalent: - `note_reject` - `crc16_ccitt` -## `src/payload_codec.cpp` +## `lib/dd3_legacy_core/src/payload_codec.cpp` - `bool encode_batch(const BatchInput&, uint8_t*, size_t, size_t*)` - schema v3 encoder with metadata, sparse present mask, delta coding. @@ -236,7 +236,7 @@ Sender state machine invariants must remain behavior-equivalent: - Optional self-test: - `payload_codec_self_test` (when `PAYLOAD_CODEC_TEST`). -## `src/json_codec.cpp` +## `lib/dd3_legacy_core/src/json_codec.cpp` - `bool meterDataToJson(const MeterData&, String&)` - create MQTT state JSON with stable field semantics. @@ -466,6 +466,7 @@ Behavior-critical internals (migrated from pre-refactor `main.cpp`) that must re Current core orchestration requirements: - `setup` - initialize shared subsystems once, + - force-link `dd3_legacy_core` before first legacy-core symbol use (`dd3_legacy_core_force_link()`), - instantiate role config and call role `begin`, - keep role-specific runtime out of this file. - `loop` diff --git a/docs/TESTS.md b/docs/TESTS.md index ca4031b..145b965 100644 --- a/docs/TESTS.md +++ b/docs/TESTS.md @@ -37,7 +37,7 @@ pio test -e lilygo-t3-v1-6-1-868-test - `test_payload_codec`: payload schema v3 roundtrip/reject paths and golden vectors. - `test_lora_transport`: CRC16, frame encode/decode integrity, and chunk reassembly behavior. - `test_json_codec`: state JSON key stability and Home Assistant discovery payload manufacturer/key stability. -- `test_refactor_smoke`: baseline include/type smoke and manufacturer constant guard. +- `test_refactor_smoke`: baseline include/type smoke and manufacturer constant guard, using stable public headers from `include/` (no `../../src` includes). ## Manufacturer Drift Guard diff --git a/include/app_context.h b/include/app_context.h new file mode 100644 index 0000000..e647134 --- /dev/null +++ b/include/app_context.h @@ -0,0 +1,3 @@ +#pragma once + +#include "../src/app_context.h" diff --git a/include/receiver_pipeline.h b/include/receiver_pipeline.h new file mode 100644 index 0000000..78e2aeb --- /dev/null +++ b/include/receiver_pipeline.h @@ -0,0 +1,3 @@ +#pragma once + +#include "../src/receiver_pipeline.h" diff --git a/include/sender_state_machine.h b/include/sender_state_machine.h new file mode 100644 index 0000000..0262466 --- /dev/null +++ b/include/sender_state_machine.h @@ -0,0 +1,3 @@ +#pragma once + +#include "../src/sender_state_machine.h" diff --git a/lib/dd3_legacy_core/include/data_model.h b/lib/dd3_legacy_core/include/data_model.h index eadf2d3..92e1b3b 100644 --- a/lib/dd3_legacy_core/include/data_model.h +++ b/lib/dd3_legacy_core/include/data_model.h @@ -1,60 +1,3 @@ #pragma once -#include - -enum class FaultType : uint8_t { - None = 0, - MeterRead = 1, - Decode = 2, - LoraTx = 3 -}; - -enum class RxRejectReason : uint8_t { - None = 0, - CrcFail = 1, - InvalidMsgKind = 2, - LengthMismatch = 3, - DeviceIdMismatch = 4, - BatchIdMismatch = 5, - UnknownSender = 6 -}; - -struct FaultCounters { - uint32_t meter_read_fail; - uint32_t decode_fail; - uint32_t lora_tx_fail; -}; - -struct MeterData { - uint32_t ts_utc; - uint32_t meter_seconds; - uint16_t short_id; - char device_id[16]; - float energy_total_kwh; - float phase_power_w[3]; - float total_power_w; - float battery_voltage_v; - uint8_t battery_percent; - bool meter_seconds_valid; - bool valid; - int16_t link_rssi_dbm; - float link_snr_db; - bool link_valid; - uint32_t err_meter_read; - uint32_t err_decode; - uint32_t err_lora_tx; - FaultType last_error; - uint8_t rx_reject_reason; -}; - -struct SenderStatus { - MeterData last_data; - uint32_t last_update_ts_utc; - uint32_t rx_batches_total; - uint32_t rx_batches_duplicate; - uint32_t rx_last_duplicate_ts_utc; - bool has_data; -}; - -void init_device_ids(uint16_t &short_id, char *device_id, size_t device_id_len); -const char *rx_reject_reason_text(RxRejectReason reason); +#include "../../../include/data_model.h" diff --git a/lib/dd3_legacy_core/include/html_util.h b/lib/dd3_legacy_core/include/html_util.h index 02c96b4..845f04b 100644 --- a/lib/dd3_legacy_core/include/html_util.h +++ b/lib/dd3_legacy_core/include/html_util.h @@ -1,7 +1,3 @@ #pragma once -#include - -String html_escape(const String &input); -String url_encode_component(const String &input); -bool sanitize_device_id(const String &input, String &out_device_id); +#include "../../../include/html_util.h" diff --git a/lib/dd3_legacy_core/include/json_codec.h b/lib/dd3_legacy_core/include/json_codec.h index fbfa3b7..475242b 100644 --- a/lib/dd3_legacy_core/include/json_codec.h +++ b/lib/dd3_legacy_core/include/json_codec.h @@ -1,6 +1,3 @@ #pragma once -#include -#include "data_model.h" - -bool meterDataToJson(const MeterData &data, String &out_json); +#include "../../../include/json_codec.h" diff --git a/src/main.cpp b/src/main.cpp index 11705dd..11dc8f9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include "app_context.h" #include "config.h" #include "data_model.h" +#include "dd3_legacy_core.h" #include "display_ui.h" #include "lora_transport.h" #include "mqtt_client.h" @@ -65,6 +66,7 @@ static void watchdog_kick() {} void setup() { Serial.begin(115200); delay(200); + dd3_legacy_core_force_link(); #ifdef PAYLOAD_CODEC_TEST payload_codec_self_test(); diff --git a/test/test_refactor_smoke/test_refactor_smoke.cpp b/test/test_refactor_smoke/test_refactor_smoke.cpp index 29e7a75..7bc22da 100644 --- a/test/test_refactor_smoke/test_refactor_smoke.cpp +++ b/test/test_refactor_smoke/test_refactor_smoke.cpp @@ -1,9 +1,9 @@ #include #include -#include "../../src/app_context.h" -#include "../../src/receiver_pipeline.h" -#include "../../src/sender_state_machine.h" +#include "app_context.h" +#include "receiver_pipeline.h" +#include "sender_state_machine.h" #include "config.h" static void test_refactor_headers_and_types() {