Add RX reject reasons to telemetry and UI
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
This commit is contained in:
70
src/main.cpp
70
src/main.cpp
@@ -105,6 +105,8 @@ static uint32_t g_sender_rx_window_ms = 0;
|
||||
static uint32_t g_sender_sleep_ms = 0;
|
||||
static uint32_t g_sender_power_log_ms = 0;
|
||||
static uint8_t g_sender_timesync_mode = 0;
|
||||
static RxRejectReason g_sender_rx_reject_reason = RxRejectReason::None;
|
||||
static uint32_t g_sender_rx_reject_log_ms = 0;
|
||||
static MeterData g_last_meter_data = {};
|
||||
static bool g_last_meter_valid = false;
|
||||
static uint32_t g_last_meter_rx_ms = 0;
|
||||
@@ -253,6 +255,18 @@ static void receiver_note_timesync_drift(uint8_t sender_idx, uint32_t sender_ts_
|
||||
}
|
||||
}
|
||||
|
||||
static void sender_note_rx_reject(RxRejectReason reason, const char *context) {
|
||||
if (reason == RxRejectReason::None) {
|
||||
return;
|
||||
}
|
||||
g_sender_rx_reject_reason = reason;
|
||||
uint32_t now_ms = millis();
|
||||
if (SERIAL_DEBUG_MODE && now_ms - g_sender_rx_reject_log_ms >= 1000) {
|
||||
g_sender_rx_reject_log_ms = now_ms;
|
||||
serial_debug_printf("rx_reject: %s reason=%s", context, rx_reject_reason_text(reason));
|
||||
}
|
||||
}
|
||||
|
||||
static BatchBuffer *batch_queue_peek() {
|
||||
if (g_batch_count == 0) {
|
||||
return nullptr;
|
||||
@@ -548,6 +562,7 @@ static bool send_inflight_batch(uint32_t ts_for_display) {
|
||||
input.err_d = g_sender_faults.decode_fail > 255 ? 255 : static_cast<uint8_t>(g_sender_faults.decode_fail);
|
||||
input.err_tx = g_sender_faults.lora_tx_fail > 255 ? 255 : static_cast<uint8_t>(g_sender_faults.lora_tx_fail);
|
||||
input.err_last = static_cast<uint8_t>(g_sender_last_error);
|
||||
input.err_rx_reject = static_cast<uint8_t>(g_sender_rx_reject_reason);
|
||||
for (uint8_t i = 0; i < g_inflight_count; ++i) {
|
||||
input.energy_wh[i] = kwh_to_wh_from_float(g_inflight_samples[i].energy_total_kwh);
|
||||
if (!float_to_i16_w(g_inflight_samples[i].phase_power_w[0], input.p1_w[i]) ||
|
||||
@@ -824,6 +839,7 @@ static void sender_loop() {
|
||||
}
|
||||
data.battery_voltage_v = g_last_battery_voltage_v;
|
||||
data.battery_percent = g_last_battery_percent;
|
||||
data.rx_reject_reason = static_cast<uint8_t>(g_sender_rx_reject_reason);
|
||||
|
||||
uint32_t now_utc = time_get_utc();
|
||||
data.ts_utc = now_utc > 0 ? now_utc : millis() / 1000;
|
||||
@@ -852,7 +868,15 @@ static void sender_loop() {
|
||||
if (SERIAL_DEBUG_MODE) {
|
||||
g_sender_rx_window_ms += rx_elapsed;
|
||||
}
|
||||
if (got_ack && ack_pkt.payload_type == PayloadType::Ack && ack_pkt.payload_len >= 6 && ack_pkt.role == DeviceRole::Receiver) {
|
||||
if (!got_ack) {
|
||||
sender_note_rx_reject(lora_get_last_rx_reject_reason(), "ack");
|
||||
} else if (ack_pkt.role != DeviceRole::Receiver) {
|
||||
sender_note_rx_reject(RxRejectReason::WrongRole, "ack");
|
||||
} else if (ack_pkt.payload_type != PayloadType::Ack) {
|
||||
sender_note_rx_reject(RxRejectReason::WrongPayloadType, "ack");
|
||||
} else if (ack_pkt.payload_len < 6) {
|
||||
sender_note_rx_reject(RxRejectReason::LengthMismatch, "ack");
|
||||
} else {
|
||||
uint16_t ack_id = read_u16_le(ack_pkt.payload);
|
||||
uint16_t ack_sender = read_u16_le(&ack_pkt.payload[2]);
|
||||
uint16_t ack_receiver = read_u16_le(&ack_pkt.payload[4]);
|
||||
@@ -861,9 +885,16 @@ static void sender_loop() {
|
||||
g_last_acked_batch_id = ack_id;
|
||||
serial_debug_printf("ack: rx ok batch_id=%u", ack_id);
|
||||
finish_inflight_batch();
|
||||
} else if (SERIAL_DEBUG_MODE) {
|
||||
serial_debug_printf("ack: reject batch_id=%u sender=%u receiver=%u exp_batch=%u exp_sender=%u",
|
||||
ack_id, ack_sender, ack_receiver, g_last_sent_batch_id, g_short_id);
|
||||
} else {
|
||||
if (ack_sender != g_short_id || ack_receiver != ack_pkt.device_id_short) {
|
||||
sender_note_rx_reject(RxRejectReason::DeviceIdMismatch, "ack");
|
||||
} else if (ack_id != g_last_sent_batch_id) {
|
||||
sender_note_rx_reject(RxRejectReason::BatchIdMismatch, "ack");
|
||||
}
|
||||
if (SERIAL_DEBUG_MODE) {
|
||||
serial_debug_printf("ack: reject batch_id=%u sender=%u receiver=%u exp_batch=%u exp_sender=%u",
|
||||
ack_id, ack_sender, ack_receiver, g_last_sent_batch_id, g_short_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -878,17 +909,27 @@ static void sender_loop() {
|
||||
if (SERIAL_DEBUG_MODE) {
|
||||
g_sender_rx_window_ms += rx_elapsed;
|
||||
}
|
||||
if (got && rx.payload_type == PayloadType::TimeSync) {
|
||||
if (time_handle_timesync_payload(rx.payload, rx.payload_len)) {
|
||||
g_sender_last_timesync_rx_ms = now_ms;
|
||||
if (g_sender_timesync_error) {
|
||||
g_sender_timesync_error = false;
|
||||
display_set_last_error(FaultType::None, 0, 0);
|
||||
}
|
||||
serial_debug_printf("timesync: rx ok window_ms=%lu", static_cast<unsigned long>(window_ms));
|
||||
if (!got) {
|
||||
sender_note_rx_reject(lora_get_last_rx_reject_reason(), "timesync");
|
||||
if (SERIAL_DEBUG_MODE) {
|
||||
serial_debug_printf("timesync: rx miss window_ms=%lu", static_cast<unsigned long>(window_ms));
|
||||
}
|
||||
} else if (rx.role != DeviceRole::Receiver) {
|
||||
sender_note_rx_reject(RxRejectReason::WrongRole, "timesync");
|
||||
} else if (rx.payload_type != PayloadType::TimeSync) {
|
||||
sender_note_rx_reject(RxRejectReason::WrongPayloadType, "timesync");
|
||||
} else if (time_handle_timesync_payload(rx.payload, rx.payload_len)) {
|
||||
g_sender_last_timesync_rx_ms = now_ms;
|
||||
if (g_sender_timesync_error) {
|
||||
g_sender_timesync_error = false;
|
||||
display_set_last_error(FaultType::None, 0, 0);
|
||||
}
|
||||
serial_debug_printf("timesync: rx ok window_ms=%lu", static_cast<unsigned long>(window_ms));
|
||||
} else {
|
||||
sender_note_rx_reject(RxRejectReason::LengthMismatch, "timesync");
|
||||
if (SERIAL_DEBUG_MODE) {
|
||||
serial_debug_printf("timesync: rx miss window_ms=%lu", static_cast<unsigned long>(window_ms));
|
||||
}
|
||||
} else if (SERIAL_DEBUG_MODE) {
|
||||
serial_debug_printf("timesync: rx miss window_ms=%lu", static_cast<unsigned long>(window_ms));
|
||||
}
|
||||
}
|
||||
uint32_t timesync_age_ms = (g_sender_last_timesync_rx_ms > 0) ? (now_ms - g_sender_last_timesync_rx_ms)
|
||||
@@ -1063,6 +1104,7 @@ static void receiver_loop() {
|
||||
data.err_decode = batch.err_d;
|
||||
data.err_lora_tx = batch.err_tx;
|
||||
data.last_error = static_cast<FaultType>(batch.err_last);
|
||||
data.rx_reject_reason = batch.err_rx_reject;
|
||||
sd_logger_log_sample(data, (s + 1 == count) && data.last_error != FaultType::None);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user