document batching updates and restore bat_v in batches
This commit is contained in:
20
README.md
20
README.md
@@ -59,7 +59,7 @@ void sender_loop() {
|
||||
read_battery(data); // VBAT + SoC
|
||||
|
||||
if (time_to_send_batch()) {
|
||||
json = meterBatchToJson(samples, batch_id);
|
||||
json = meterBatchToJson(samples, batch_id); // bat_v per batch, t_first/t_last included
|
||||
compressed = compressBuffer(json);
|
||||
lora_send(packet(MeterBatch, compressed));
|
||||
}
|
||||
@@ -92,7 +92,7 @@ bool lora_send(const LoraPacket &pkt); // add header + CRC16 and transmit
|
||||
- Web UI:
|
||||
- AP mode: status + WiFi/MQTT config.
|
||||
- STA mode: status + per-sender pages.
|
||||
- OLED cycles through receiver status and per-sender pages.
|
||||
- OLED cycles through receiver status and per-sender pages (receiver OLED never sleeps).
|
||||
|
||||
**Receiver loop (pseudo-code)**:
|
||||
```cpp
|
||||
@@ -106,7 +106,7 @@ void receiver_loop() {
|
||||
}
|
||||
} else if (pkt.type == MeterBatch) {
|
||||
json = reassemble_and_decompress_batch(pkt);
|
||||
for (sample in jsonToMeterBatch(json)) {
|
||||
for (sample in jsonToMeterBatch(json)) { // uses t_first/t_last for jittered timestamps
|
||||
update_sender_status(sample);
|
||||
mqtt_publish_state(sample);
|
||||
}
|
||||
@@ -176,7 +176,7 @@ Packet layout:
|
||||
|
||||
LoRa radio settings:
|
||||
- Frequency: **433 MHz** or **868 MHz** (set by build env via `LORA_FREQUENCY_HZ`)
|
||||
- SF12, BW 125 kHz, CR 4/5, CRC on, Sync Word 0x34
|
||||
- SF10, BW 125 kHz, CR 4/5, CRC on, Sync Word 0x34
|
||||
|
||||
## Data Format
|
||||
JSON payload (sender + MQTT):
|
||||
@@ -203,6 +203,8 @@ MeterBatch JSON (compressed over LoRa) uses per-field arrays with integer units
|
||||
"sender": "s01",
|
||||
"batch_id": 1842,
|
||||
"t0": 1738288000,
|
||||
"t_first": 1738288000,
|
||||
"t_last": 1738288030,
|
||||
"dt_s": 1,
|
||||
"n": 3,
|
||||
"energy_wh": [123456700, 123456701, 123456701],
|
||||
@@ -210,6 +212,7 @@ MeterBatch JSON (compressed over LoRa) uses per-field arrays with integer units
|
||||
"p1_w": [480, 490, 500],
|
||||
"p2_w": [450, 450, 450],
|
||||
"p3_w": [0, 0, 0],
|
||||
"bat_v": 3.92,
|
||||
"meta": {
|
||||
"rssi": -92,
|
||||
"snr": 7.5,
|
||||
@@ -221,6 +224,7 @@ MeterBatch JSON (compressed over LoRa) uses per-field arrays with integer units
|
||||
Notes:
|
||||
- `sender` maps to `EXPECTED_SENDER_IDS` order (`s01` = first sender).
|
||||
- `meta` is injected by the receiver after batch reassembly.
|
||||
- `bat_v` is a single batch-level value (percent is calculated locally).
|
||||
|
||||
## Device IDs
|
||||
- Derived from WiFi STA MAC.
|
||||
@@ -236,9 +240,7 @@ inline constexpr uint16_t EXPECTED_SENDER_IDS[NUM_SENDERS] = { 0xF19C };
|
||||
|
||||
## OLED Behavior
|
||||
- Sender: OLED stays **ON for 10 seconds** on each wake, then powers down for sleep.
|
||||
- Receiver: OLED follows the 10-minute auto-off behavior:
|
||||
- GPIO14 HIGH: OLED forced ON.
|
||||
- GPIO14 LOW: auto-off after 10 minutes.
|
||||
- Receiver: OLED is always on (no auto-off).
|
||||
- Pages rotate every 4s.
|
||||
|
||||
## Power & Battery
|
||||
@@ -283,6 +285,10 @@ Key timing settings in `include/config.h`:
|
||||
- `METER_SEND_INTERVAL_MS`
|
||||
- `BATCH_ACK_TIMEOUT_MS`
|
||||
- `BATCH_MAX_RETRIES`
|
||||
- `BATCH_QUEUE_DEPTH`
|
||||
- `BATCH_RETRY_POLICY` (keep or drop on retry exhaustion)
|
||||
- `SERIAL_DEBUG_MODE` / `SERIAL_DEBUG_DUMP_JSON`
|
||||
- `LORA_SEND_BYPASS` (debug only)
|
||||
|
||||
## Limits & Known Constraints
|
||||
- **Compression**: uses lightweight RLE (good for JSON but not optimal).
|
||||
|
||||
@@ -73,8 +73,8 @@ constexpr uint8_t BATCH_QUEUE_DEPTH = 10;
|
||||
constexpr BatchRetryPolicy BATCH_RETRY_POLICY = BatchRetryPolicy::Keep;
|
||||
constexpr uint32_t WATCHDOG_TIMEOUT_SEC = 120;
|
||||
constexpr bool ENABLE_HA_DISCOVERY = true;
|
||||
constexpr bool SERIAL_DEBUG_MODE = true;
|
||||
constexpr bool SERIAL_DEBUG_DUMP_JSON = true;
|
||||
constexpr bool SERIAL_DEBUG_MODE = false;
|
||||
constexpr bool SERIAL_DEBUG_DUMP_JSON = false;
|
||||
constexpr bool LORA_SEND_BYPASS = false;
|
||||
|
||||
constexpr uint8_t NUM_SENDERS = 1;
|
||||
|
||||
@@ -199,6 +199,11 @@ bool meterBatchToJson(const MeterData *samples, size_t count, uint16_t batch_id,
|
||||
if (last_error != FaultType::None) {
|
||||
doc["err_last"] = static_cast<uint8_t>(last_error);
|
||||
}
|
||||
if (!isnan(samples[count - 1].battery_voltage_v)) {
|
||||
char bat_buf[16];
|
||||
format_float_2(bat_buf, sizeof(bat_buf), samples[count - 1].battery_voltage_v);
|
||||
doc["bat_v"] = serialized(bat_buf);
|
||||
}
|
||||
|
||||
JsonArray energy = doc.createNestedArray("energy_wh");
|
||||
JsonArray p_w = doc.createNestedArray("p_w");
|
||||
@@ -235,6 +240,7 @@ bool jsonToMeterBatch(const String &json, MeterData *out_samples, size_t max_cou
|
||||
uint32_t err_m = doc["err_m"] | 0;
|
||||
uint32_t err_tx = doc["err_tx"] | 0;
|
||||
FaultType last_error = static_cast<FaultType>(doc["err_last"] | 0);
|
||||
float bat_v = doc["bat_v"] | NAN;
|
||||
|
||||
if (!doc["schema"].isNull()) {
|
||||
if ((doc["schema"] | 0) != 1) {
|
||||
@@ -284,8 +290,12 @@ bool jsonToMeterBatch(const String &json, MeterData *out_samples, size_t max_cou
|
||||
data.phase_power_w[0] = static_cast<float>(p1_w[idx] | 0);
|
||||
data.phase_power_w[1] = static_cast<float>(p2_w[idx] | 0);
|
||||
data.phase_power_w[2] = static_cast<float>(p3_w[idx] | 0);
|
||||
data.battery_voltage_v = NAN;
|
||||
data.battery_voltage_v = bat_v;
|
||||
if (!isnan(bat_v)) {
|
||||
data.battery_percent = battery_percent_from_voltage(bat_v);
|
||||
} else {
|
||||
data.battery_percent = 0;
|
||||
}
|
||||
data.valid = true;
|
||||
data.link_valid = false;
|
||||
data.link_rssi_dbm = 0;
|
||||
|
||||
Reference in New Issue
Block a user