DD3-LoRa-Bridge-MultiSender

Firmware for LilyGO T3 v1.6.1 (ESP32 + SX1276 + SSD1306) that runs as either:

  • Sender (PIN GPIO14 HIGH): reads one IEC 62056-21 meter, batches samples, sends over LoRa.
  • Receiver (PIN GPIO14 LOW): receives/ACKs batches, publishes MQTT, serves web UI, logs to SD.

Current Architecture

  • Single codebase, role selected at boot via detect_role() (include/config.h, src/config.cpp).
  • LoRa link uses explicit CRC16 frame protection in firmware (src/lora_transport.cpp).
  • Sender batches up to 30 samples and retries on missing ACK (BATCH_MAX_RETRIES=2, retry policy Keep).
  • Receiver uses STA mode when config is valid, otherwise AP fallback with web config.

LoRa Frame Protocol (Current)

On-air frame format:

[msg_kind:1][device_short_id:2][payload...][crc16:2]

msg_kind:

  • 0 = BatchUp
  • 1 = AckDown

BatchUp

Transport is chunked (batch_id, chunk_index, chunk_count, total_len) and reassembled before payload decode.

Payload codec (src/payload_codec.cpp) currently uses:

  • kMagic=0xDDB3
  • kSchema=2
  • metadata: sender, batch, timestamp, interval, battery, fault counters
  • data arrays: energy_wh[], p1_w[], p2_w[], p3_w[]

n == 0 is valid and used for sync request packets.

AckDown (7 bytes)

[flags:1][batch_id_be:2][epoch_utc_be:4]

  • flags bit0: time_valid
  • Receiver repeats ACK (ACK_REPEAT_COUNT=3, ACK_REPEAT_DELAY_MS=200).
  • Sender accepts time only if time_valid=1 and epoch >= MIN_ACCEPTED_EPOCH_UTC (2026-02-01 00:00:00 UTC).

Time Bootstrap Guardrail

On sender boot:

  • g_time_acquired=false
  • no normal sampling/transmit yet
  • sync request every SYNC_REQUEST_INTERVAL_MS (15s)

Only after valid ACK time is received:

  • system time is set
  • normal 1 Hz sampling and periodic LoRa batch transmit start

This blocks pre-threshold timestamps from MQTT/SD paths.

Sender Meter Path

Implemented in src/meter_driver.cpp + sender loop in src/main.cpp:

  • UART: Serial2, RX pin GPIO34 (PIN_METER_RX), 9600 7E1
  • Frame detection: starts at '/', ends at '!', timeout protection included
  • Parsed OBIS values:
    • 1-0:1.8.0 (total energy)
    • 1-0:16.7.0 (total power)
    • 1-0:36.7.0, 56.7.0, 76.7.0 (phase powers)
  • 1-0:1.8.0*Wh is automatically scaled to kWh

Sender samples every second and transmits batches every 30 seconds.

Receiver Behavior

For valid BatchUp decode:

  1. Reassemble chunks and decode payload.
  2. Send AckDown immediately.
  3. Drop duplicate batches per sender (batch_id tracking).
  4. If n==0: treat as sync request only.
  5. Else convert samples, log to SD, update web UI, publish MQTT.

MQTT Topics and Payloads

State topic:

  • smartmeter/<device_id>/state

Fault topic (retained):

  • smartmeter/<device_id>/faults

State JSON fields (src/json_codec.cpp):

  • id, ts, e_kwh
  • p_w, p1_w, p2_w, p3_w
  • bat_v, bat_pct
  • optional link fields: rssi, snr
  • fault/reject fields: err_last, rx_reject, rx_reject_text (+ non-zero counters)

Home Assistant discovery is enabled (ENABLE_HA_DISCOVERY=true) and publishes config topics under:

  • homeassistant/sensor/<device_id>/<key>/config

Web UI, Wi-Fi, Storage

  • Wi-Fi/MQTT/NTP/web-auth config persists in Preferences (wifi_manager).
  • AP fallback SSID prefix: DD3-Bridge-.
  • Default web credentials: admin/admin.
  • SD logging enabled (ENABLE_SD_LOGGING=true).

Build Environments

From platformio.ini:

  • lilygo-t3-v1-6-1
  • lilygo-t3-v1-6-1-test
  • lilygo-t3-v1-6-1-868
  • lilygo-t3-v1-6-1-868-test
  • lilygo-t3-v1-6-1-payload-test
  • lilygo-t3-v1-6-1-868-payload-test
  • lilygo-t3-v1-6-1-prod
  • lilygo-t3-v1-6-1-868-prod

Example:

~/.platformio/penv/bin/pio run -e lilygo-t3-v1-6-1

Test Mode

ENABLE_TEST_MODE replaces normal sender/receiver loops with dedicated test loops (src/test_mode.cpp). It sends/receives JSON test frames and publishes to smartmeter/<device_id>/test.

Description
Unified Firmware for the LilyGO T3 v1.6.1 433MHz Version. Sender will read DD3 Smart Meter Data and send it to reciever as well as display on OLED. Reciever will publish to mqtt and show on a local website as well as OLED.
Readme 2.1 MiB
Languages
C++ 94.2%
C 5.8%