Rework test mode to use normal LoRa batching and ACK flow

This commit is contained in:
2026-02-20 20:42:06 +01:00
parent 1169eab626
commit ae5b4a940a
3 changed files with 65 additions and 20 deletions

View File

@@ -11,7 +11,6 @@
#include "receiver_pipeline.h" #include "receiver_pipeline.h"
#include "sd_logger.h" #include "sd_logger.h"
#include "sender_state_machine.h" #include "sender_state_machine.h"
#include "test_mode.h"
#include "time_manager.h" #include "time_manager.h"
#include "web_server.h" #include "web_server.h"
#include "wifi_manager.h" #include "wifi_manager.h"
@@ -136,24 +135,6 @@ void setup() {
} }
void loop() { void loop() {
#ifdef ENABLE_TEST_MODE
if (g_role == DeviceRole::Sender) {
test_sender_loop(g_short_id, g_device_id);
display_tick();
watchdog_kick();
delay(50);
} else {
test_receiver_loop(g_receiver_shared.sender_statuses, NUM_SENDERS, g_short_id);
mqtt_loop();
web_server_loop();
display_set_receiver_status(g_receiver_shared.ap_mode, wifi_is_connected() ? wifi_get_ssid().c_str() : "AP", mqtt_is_connected());
display_tick();
watchdog_kick();
delay(50);
}
return;
#endif
if (g_role == DeviceRole::Sender) { if (g_role == DeviceRole::Sender) {
g_sender_state_machine.loop(); g_sender_state_machine.loop();
} else { } else {

View File

@@ -6,6 +6,7 @@
#include "config.h" #include "config.h"
#include "display_ui.h" #include "display_ui.h"
#include "json_codec.h"
#include "lora_transport.h" #include "lora_transport.h"
#include "mqtt_client.h" #include "mqtt_client.h"
#include "payload_codec.h" #include "payload_codec.h"
@@ -77,6 +78,18 @@ static uint8_t bit_count32(uint32_t value) {
return count; return count;
} }
static bool mqtt_publish_sample(const MeterData &data) {
#ifdef ENABLE_TEST_MODE
String payload;
if (!meterDataToJson(data, payload)) {
return false;
}
return mqtt_publish_test(data.device_id, payload);
#else
return mqtt_publish_state(data);
#endif
}
struct BatchRxState { struct BatchRxState {
bool active; bool active;
uint16_t batch_id; uint16_t batch_id;
@@ -470,7 +483,7 @@ static void receiver_loop() {
web_server_set_last_batch(static_cast<uint8_t>(sender_idx), samples, count); web_server_set_last_batch(static_cast<uint8_t>(sender_idx), samples, count);
for (size_t s = 0; s < count; ++s) { for (size_t s = 0; s < count; ++s) {
mqtt_publish_state(samples[s]); mqtt_publish_sample(samples[s]);
} }
g_sender_statuses[sender_idx].last_data = samples[count - 1]; g_sender_statuses[sender_idx].last_data = samples[count - 1];
g_sender_statuses[sender_idx].last_update_ts_utc = samples[count - 1].ts_utc; g_sender_statuses[sender_idx].last_update_ts_utc = samples[count - 1].ts_utc;

View File

@@ -145,6 +145,10 @@ static uint32_t g_build_invalid = 0;
static constexpr uint32_t METER_SAMPLE_MAX_AGE_MS = 15000; static constexpr uint32_t METER_SAMPLE_MAX_AGE_MS = 15000;
static constexpr uint32_t METER_TIME_DELTA_TOLERANCE_S = 2; static constexpr uint32_t METER_TIME_DELTA_TOLERANCE_S = 2;
static constexpr int64_t METER_TIME_ANCHOR_DRIFT_TOLERANCE_S = 2; static constexpr int64_t METER_TIME_ANCHOR_DRIFT_TOLERANCE_S = 2;
#ifdef ENABLE_TEST_MODE
static uint32_t g_test_meter_last_emit_ms = 0;
static uint32_t g_test_meter_tick = 0;
#endif
struct MeterSampleEvent { struct MeterSampleEvent {
MeterData data; MeterData data;
@@ -350,6 +354,27 @@ static bool parse_meter_frame_sample(const char *frame, size_t frame_len, MeterD
return meter_parse_frame(frame, frame_len, parsed); return meter_parse_frame(frame, frame_len, parsed);
} }
#ifdef ENABLE_TEST_MODE
static bool generate_test_meter_sample(uint32_t now_ms, MeterData &parsed) {
if (g_test_meter_last_emit_ms != 0 && now_ms - g_test_meter_last_emit_ms < METER_SAMPLE_INTERVAL_MS) {
return false;
}
g_test_meter_last_emit_ms = now_ms;
g_test_meter_tick++;
parsed = {};
parsed.valid = true;
parsed.meter_seconds_valid = true;
parsed.meter_seconds = MIN_ACCEPTED_EPOCH_UTC + g_test_meter_tick;
parsed.energy_total_kwh = static_cast<float>(g_test_meter_tick) / 1000.0f; // 1 Wh step per sample.
parsed.phase_power_w[0] = static_cast<float>(g_test_meter_tick);
parsed.phase_power_w[1] = static_cast<float>(g_test_meter_tick);
parsed.phase_power_w[2] = static_cast<float>(g_test_meter_tick);
parsed.total_power_w = parsed.phase_power_w[0] + parsed.phase_power_w[1] + parsed.phase_power_w[2];
return true;
}
#endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
static void meter_queue_push_latest(const MeterSampleEvent &event) { static void meter_queue_push_latest(const MeterSampleEvent &event) {
if (!g_meter_sample_queue) { if (!g_meter_sample_queue) {
@@ -374,6 +399,20 @@ static void meter_queue_push_latest(const MeterSampleEvent &event) {
static void meter_reader_task_entry(void *arg) { static void meter_reader_task_entry(void *arg) {
(void)arg; (void)arg;
for (;;) { for (;;) {
#ifdef ENABLE_TEST_MODE
MeterData test_sample = {};
uint32_t now_ms = millis();
if (!generate_test_meter_sample(now_ms, test_sample)) {
vTaskDelay(pdMS_TO_TICKS(5));
continue;
}
MeterSampleEvent event = {};
event.data = test_sample;
event.rx_ms = now_ms;
meter_queue_push_latest(event);
continue;
#endif
const char *frame = nullptr; const char *frame = nullptr;
size_t frame_len = 0; size_t frame_len = 0;
if (!meter_poll_frame(frame, frame_len)) { if (!meter_poll_frame(frame, frame_len)) {
@@ -438,6 +477,14 @@ static void meter_reader_pump(uint32_t now_ms) {
} }
#endif #endif
#ifdef ENABLE_TEST_MODE
MeterData test_sample = {};
if (generate_test_meter_sample(now_ms, test_sample)) {
set_last_meter_sample(test_sample, now_ms);
}
return;
#endif
const char *frame = nullptr; const char *frame = nullptr;
size_t frame_len = 0; size_t frame_len = 0;
if (!meter_poll_frame(frame, frame_len)) { if (!meter_poll_frame(frame, frame_len)) {
@@ -1498,6 +1545,10 @@ bool SenderStateMachine::begin(const SenderStateMachineConfig &config) {
g_time_acquired = false; g_time_acquired = false;
g_sender_faults_reset_after_first_sync = false; g_sender_faults_reset_after_first_sync = false;
g_sender_faults_reset_hour_utc = UINT32_MAX; g_sender_faults_reset_hour_utc = UINT32_MAX;
#ifdef ENABLE_TEST_MODE
g_test_meter_last_emit_ms = 0;
g_test_meter_tick = 0;
#endif
update_battery_cache(); update_battery_cache();
sender_transition(SenderPhase::Syncing, "begin"); sender_transition(SenderPhase::Syncing, "begin");
return true; return true;