dashboard: expand CAN diagnostics

This commit is contained in:
2026-07-26 14:49:19 -06:00
parent 6d61078484
commit ed9611310e
2 changed files with 107 additions and 3 deletions
+11
View File
@@ -75,3 +75,14 @@ The dashboard uses field-filtered `/api/v1/status` calls:
- Slow poll: `/api/v1/status?fields=network,system,config` - Slow poll: `/api/v1/status?fields=network,system,config`
This keeps the endpoint model simple while reducing payload size, JSON parsing, and UI churn. This keeps the endpoint model simple while reducing payload size, JSON parsing, and UI churn.
## OBD-II / CAN Diagnostics
The hidden diagnostics overlay distinguishes raw CAN traffic from parsed
OBD-II coolant responses. It reports request successes and failures, total
raw frames received, the last frame identifier and DLC, TWAI queue depth,
controller error counters, missed or overrun frames, arbitration losses,
and bus errors.
Raw RX activity confirms that the display is receiving CAN frames even when
the vehicle does not return a matching Mode 01 PID 05 coolant response.
@@ -47,7 +47,13 @@ static unsigned long last_obd_poll_ms = 0;
static unsigned long last_obd_rx_ms = 0; static unsigned long last_obd_rx_ms = 0;
static unsigned long last_obd_tx_ms = 0; static unsigned long last_obd_tx_ms = 0;
static uint32_t obd_tx_count = 0; static uint32_t obd_tx_count = 0;
static uint32_t obd_tx_fail_count = 0;
static uint32_t obd_rx_count = 0; static uint32_t obd_rx_count = 0;
static uint32_t obd_raw_rx_count = 0;
static uint32_t obd_last_rx_identifier = 0;
static uint8_t obd_last_rx_dlc = 0;
static bool obd_last_rx_extended = false;
static unsigned long last_obd_raw_rx_ms = 0;
static int obd_coolant_temp_f = -999; static int obd_coolant_temp_f = -999;
static int last_rendered_obd_coolant_temp_f = -1000; static int last_rendered_obd_coolant_temp_f = -1000;
static String obd_last_error = "not initialized"; static String obd_last_error = "not initialized";
@@ -491,6 +497,7 @@ static void send_obd_coolant_request()
last_obd_tx_ms = millis(); last_obd_tx_ms = millis();
obd_last_error = "coolant request sent"; obd_last_error = "coolant request sent";
} else { } else {
obd_tx_fail_count++;
obd_last_error = "tx failed: " + String((int)result); obd_last_error = "tx failed: " + String((int)result);
} }
} }
@@ -539,6 +546,12 @@ static void process_obd_can()
twai_message_t message = {}; twai_message_t message = {};
while (twai_receive(&message, 0) == ESP_OK) { while (twai_receive(&message, 0) == ESP_OK) {
obd_raw_rx_count++;
obd_last_rx_identifier = message.identifier;
obd_last_rx_dlc = message.data_length_code;
obd_last_rx_extended = message.extd;
last_obd_raw_rx_ms = millis();
handle_obd_can_message(message); handle_obd_can_message(message);
} }
@@ -625,15 +638,95 @@ static void update_diagnostics_overlay()
text += "\n\nOBD-II / CAN\n"; text += "\n\nOBD-II / CAN\n";
text += " TWAI: "; text += " TWAI: ";
text += obd_can_started ? "started" : "offline"; text += obd_can_started ? "started" : "offline";
text += " @ 500 kbps";
text += "\n Pins: TX GPIO15 / RX GPIO16"; text += "\n Pins: TX GPIO15 / RX GPIO16";
text += "\n Coolant: "; text += "\n Coolant: ";
text += obd_coolant_temp_f > -100 ? String(obd_coolant_temp_f) + "°F" : String("--"); text += obd_coolant_temp_f > -100 ? String(obd_coolant_temp_f) + "°F" : String("--");
text += "\n TX/RX: ";
text += "\n Requests OK/fail: ";
text += String(obd_tx_count); text += String(obd_tx_count);
text += "/"; text += "/";
text += String(obd_tx_fail_count);
text += "\n Raw RX/coolant RX: ";
text += String(obd_raw_rx_count);
text += "/";
text += String(obd_rx_count); text += String(obd_rx_count);
text += "\n Last RX age: ";
text += last_obd_rx_ms > 0 ? String((millis() - last_obd_rx_ms) / 1000) + "s" : String("--"); text += "\n Last frame: ";
if (obd_raw_rx_count > 0) {
text += obd_last_rx_extended ? "EXT 0x" : "STD 0x";
text += String(obd_last_rx_identifier, HEX);
text += " DLC ";
text += String(obd_last_rx_dlc);
} else {
text += "--";
}
text += "\n Raw RX age: ";
text += last_obd_raw_rx_ms > 0
? String((millis() - last_obd_raw_rx_ms) / 1000) + "s"
: String("--");
text += "\n Coolant RX age: ";
text += last_obd_rx_ms > 0
? String((millis() - last_obd_rx_ms) / 1000) + "s"
: String("--");
if (obd_can_started) {
twai_status_info_t can_status = {};
esp_err_t status_result = twai_get_status_info(&can_status);
if (status_result == ESP_OK) {
const char *state_text = "unknown";
switch (can_status.state) {
case TWAI_STATE_STOPPED:
state_text = "stopped";
break;
case TWAI_STATE_RUNNING:
state_text = "running";
break;
case TWAI_STATE_BUS_OFF:
state_text = "bus-off";
break;
case TWAI_STATE_RECOVERING:
state_text = "recovering";
break;
default:
break;
}
text += "\n State: ";
text += state_text;
text += " TXq/RXq ";
text += String(can_status.msgs_to_tx);
text += "/";
text += String(can_status.msgs_to_rx);
text += "\n TEC/REC: ";
text += String(can_status.tx_error_counter);
text += "/";
text += String(can_status.rx_error_counter);
text += "\n Fail/miss/overrun: ";
text += String(can_status.tx_failed_count);
text += "/";
text += String(can_status.rx_missed_count);
text += "/";
text += String(can_status.rx_overrun_count);
text += "\n Arb lost/bus errors: ";
text += String(can_status.arb_lost_count);
text += "/";
text += String(can_status.bus_error_count);
} else {
text += "\n Driver status error: ";
text += String((int)status_result);
}
}
text += "\n Last status: "; text += "\n Last status: ";
text += obd_last_error; text += obd_last_error;