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.
This commit is contained in:
@@ -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/<device_id>/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`
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
3
include/app_context.h
Normal file
3
include/app_context.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "../src/app_context.h"
|
||||
3
include/receiver_pipeline.h
Normal file
3
include/receiver_pipeline.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "../src/receiver_pipeline.h"
|
||||
3
include/sender_state_machine.h
Normal file
3
include/sender_state_machine.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "../src/sender_state_machine.h"
|
||||
@@ -1,60 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
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"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
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"
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "data_model.h"
|
||||
|
||||
bool meterDataToJson(const MeterData &data, String &out_json);
|
||||
#include "../../../include/json_codec.h"
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include <Arduino.h>
|
||||
#include <unity.h>
|
||||
|
||||
#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() {
|
||||
|
||||
Reference in New Issue
Block a user