Bump the batch payload codec to schema v4 with separate meter-time and UTC anchors, then use meter seconds for sparse batch slotting and receiver reconstruction.
Update the current 868 MHz bench configuration, allow ACKs from configured receiver short IDs, improve AP-to-STA recovery, quiet the test build, and document the changed protocol in the README.
- Replace 11 per-frequency build environments with 3 role-based targets
(production, debug, test) using shared [env] base section
- Move LoRa frequency and sender-ID config from build flags into config.h
so all variants build from the same source
- Add -fstack-protector-strong, -D_FORTIFY_SOURCE=2, -Wformat-security
- Add Unity test framework to lib_deps for pio test support
- Add __pycache__/ to .gitignore
- Implement periodic WiFi reconnection attempts when stuck in AP mode
- Add WIFI_RECONNECT_INTERVAL_MS config (default 60s) for configurable retry frequency
- Prevent data loss by automatically attempting to switch back to STA mode
- Maintain AP mode for manual configuration if reconnection fails
- Track WiFi config and last reconnection attempt time in shared state
- Add wifi_try_reconnect_sta() and wifi_restore_ap_mode() helper functions
- Log reconnection attempts and results for debugging
- 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.
BACKWARD-INCOMPATIBLE: MeterBatch schema bumped to v2 with err_rx_reject.
- Track and log RX reject reasons (CRC/protocol/role/payload/length/id/batch)
- Include rx_reject in sender telemetry JSON and receiver web UI
- Add lora_receive reject reason logging under SERIAL_DEBUG_MODE
- Add TimeSync fault code and labels in UI/SD/web docs
- Trigger receiver beacon bursts on sender drift, but keep errors sender-local
- Sender flags TimeSync only after TIME_SYNC_ERROR_TIMEOUT_MS
- Add BATTERY_CAL config and debug logging for raw ADC samples
- Use LiPo voltage curve (4.2V full, 2.9V empty) for % mapping
- Document battery calibration, curve, and debug output in README
- Track last OLED activity to avoid double timeout; keep power gating on transitions
- Copy TZ before setenv() in timegm_fallback to avoid invalid pointer reuse
- Add BATTERY_SAMPLE_INTERVAL_MS and only refresh cache at batch start when due
- Keep battery sampling to a single ADC read (Arduino core lacks explicit ADC power gating)
- Add lora_receive_continuous() helper and use it after init and TX (ACK/time sync)
- Ensure receiver returns to RX immediately after lora_send
- Document continuous RX behavior in README
- Add RX state machine with frame buffer, timeouts, and debug counters
- Expose meter_poll_frame/meter_parse_frame and reuse existing OBIS parsing
- Use cached last-valid frame at 1 Hz sampling to avoid blocking
- Document non-blocking meter handling in README
- Add LoRa idle/sleep/receive-window helpers and use short RX windows for ACK/time sync
- Schedule sender time-sync windows (fast/slow) and track RX vs sleep time in debug
- Lower sender power (80 MHz CPU, WiFi/BT off, reduced ADC sampling, unused pins pulldown)
- Make SERIAL_DEBUG_MODE a build flag, add prod envs with debug off, and document changes
- Add SD history chart + download listing to web UI
- Use HSPI for SD and fix SD pin mapping
- Swap role/OLED control pins and update role detection
- Update README pin mapping and SD/history docs
- Add optional microSD CSV logging per sender/day on receiver
- Wire logger into receiver packet handling
- Document new batch header fields, build envs, and SD logging
- Make sender links open in a new tab