dashboard: add passive CAN capture markers

This commit is contained in:
2026-07-26 15:26:42 -06:00
parent ed9611310e
commit 8c2375d402
6 changed files with 227 additions and 56 deletions
@@ -40,6 +40,20 @@ static constexpr uint32_t OBD_REQUEST_ID = 0x7DF;
static constexpr uint32_t OBD_RESPONSE_ID_MIN = 0x7E8;
static constexpr uint32_t OBD_RESPONSE_ID_MAX = 0x7EF;
static constexpr uint8_t OBD_PID_COOLANT_TEMP = 0x05;
static constexpr size_t CAN_TRACKED_ID_COUNT = 96;
static constexpr unsigned long CAN_LOG_MIN_INTERVAL_MS = 100;
struct CanTrackedFrame {
bool used = false;
bool extended = false;
uint32_t identifier = 0;
uint8_t dlc = 0;
uint8_t data[8] = {};
unsigned long last_log_ms = 0;
};
static CanTrackedFrame can_tracked_frames[CAN_TRACKED_ID_COUNT];
static String can_serial_command;
static bool obd_can_ready = false;
static bool obd_can_started = false;
@@ -444,9 +458,9 @@ static bool init_obd_can()
return true;
}
twai_general_config_t general_config = TWAI_GENERAL_CONFIG_DEFAULT(OBD_CAN_TX_PIN, OBD_CAN_RX_PIN, TWAI_MODE_NORMAL);
general_config.tx_queue_len = 5;
general_config.rx_queue_len = 20;
twai_general_config_t general_config = TWAI_GENERAL_CONFIG_DEFAULT(OBD_CAN_TX_PIN, OBD_CAN_RX_PIN, TWAI_MODE_LISTEN_ONLY);
general_config.tx_queue_len = 0;
general_config.rx_queue_len = 64;
twai_timing_config_t timing_config = TWAI_TIMING_CONFIG_500KBITS();
twai_filter_config_t filter_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
@@ -471,37 +485,12 @@ static bool init_obd_can()
obd_can_started = true;
obd_last_error = "started";
Serial.println("OBD CAN: TWAI started on TX GPIO15 / RX GPIO16 at 500 kbps");
Serial.println("OBD CAN: passive logger started on RX GPIO16 at 500 kbps");
Serial.println("CAN CSV: CAN,ms,id,format,dlc,b0,b1,b2,b3,b4,b5,b6,b7");
Serial.println("Enter TEMP,<degrees F> when recording a scan-tool coolant reading.");
return true;
}
static void send_obd_coolant_request()
{
twai_message_t message = {};
message.identifier = OBD_REQUEST_ID;
message.extd = 0;
message.rtr = 0;
message.data_length_code = 8;
message.data[0] = 0x02;
message.data[1] = 0x01;
message.data[2] = OBD_PID_COOLANT_TEMP;
message.data[3] = 0x55;
message.data[4] = 0x55;
message.data[5] = 0x55;
message.data[6] = 0x55;
message.data[7] = 0x55;
esp_err_t result = twai_transmit(&message, 0);
if (result == ESP_OK) {
obd_tx_count++;
last_obd_tx_ms = millis();
obd_last_error = "coolant request sent";
} else {
obd_tx_fail_count++;
obd_last_error = "tx failed: " + String((int)result);
}
}
static void handle_obd_can_message(const twai_message_t &message)
{
if (message.extd || message.rtr) {
@@ -533,16 +522,97 @@ static void handle_obd_can_message(const twai_message_t &message)
}
}
static CanTrackedFrame *find_can_tracked_frame(const twai_message_t &message)
{
CanTrackedFrame *free_slot = nullptr;
for (size_t i = 0; i < CAN_TRACKED_ID_COUNT; i++) {
CanTrackedFrame &tracked = can_tracked_frames[i];
if (tracked.used && tracked.identifier == message.identifier && tracked.extended == message.extd) {
return &tracked;
}
if (!tracked.used && free_slot == nullptr) free_slot = &tracked;
}
return free_slot;
}
static void log_changed_can_frame(const twai_message_t &message)
{
if (message.rtr) return;
CanTrackedFrame *tracked = find_can_tracked_frame(message);
if (tracked == nullptr) return;
bool changed = !tracked->used || tracked->dlc != message.data_length_code;
for (uint8_t i = 0; i < message.data_length_code && i < 8; i++) {
changed = changed || tracked->data[i] != message.data[i];
}
unsigned long now = millis();
bool should_log = changed && (!tracked->used || now - tracked->last_log_ms >= CAN_LOG_MIN_INTERVAL_MS);
tracked->used = true;
tracked->extended = message.extd;
tracked->identifier = message.identifier;
tracked->dlc = message.data_length_code;
memcpy(tracked->data, message.data, sizeof(tracked->data));
if (!should_log) return;
tracked->last_log_ms = now;
Serial.print("CAN,");
Serial.print(now);
Serial.print(",");
Serial.print(message.identifier, HEX);
Serial.print(message.extd ? ",EXT," : ",STD,");
Serial.print(message.data_length_code);
for (uint8_t i = 0; i < 8; i++) {
Serial.print(",");
if (i < message.data_length_code) {
if (message.data[i] < 0x10) Serial.print("0");
Serial.print(message.data[i], HEX);
}
}
Serial.println();
}
static void process_can_serial_commands()
{
while (Serial.available() > 0) {
char value = (char)Serial.read();
if (value == '\r') continue;
if (value != '\n') {
if (can_serial_command.length() < 48) can_serial_command += value;
continue;
}
can_serial_command.trim();
if (can_serial_command.startsWith("TEMP,")) {
String temperature = can_serial_command.substring(5);
temperature.trim();
Serial.print("TEMP,");
Serial.print(millis());
Serial.print(",");
Serial.println(temperature);
} else if (can_serial_command.startsWith("MARK,")) {
String label = can_serial_command.substring(5);
label.trim();
Serial.print("MARK,");
Serial.print(millis());
Serial.print(",");
Serial.println(label);
} else if (can_serial_command.length() > 0) {
Serial.println("ERROR,expected TEMP,<degrees F> or MARK,<label>");
}
can_serial_command = "";
}
}
static void process_obd_can()
{
if (!init_obd_can()) {
return;
}
if (millis() - last_obd_poll_ms >= 1000) {
last_obd_poll_ms = millis();
send_obd_coolant_request();
}
process_can_serial_commands();
twai_message_t message = {};
while (twai_receive(&message, 0) == ESP_OK) {
@@ -552,6 +622,7 @@ static void process_obd_can()
obd_last_rx_extended = message.extd;
last_obd_raw_rx_ms = millis();
log_changed_can_frame(message);
handle_obd_can_message(message);
}
@@ -635,23 +706,14 @@ static void update_diagnostics_overlay()
text += String(status_model.cell_delta_mv);
text += " mV";
text += "\n\nOBD-II / CAN\n";
text += "\n\nPassive CAN Logger\n";
text += " TWAI: ";
text += obd_can_started ? "started" : "offline";
text += " @ 500 kbps";
text += "\n Pins: TX GPIO15 / RX GPIO16";
text += "\n Coolant: ";
text += obd_coolant_temp_f > -100 ? String(obd_coolant_temp_f) + "°F" : String("--");
text += "\n Requests OK/fail: ";
text += String(obd_tx_count);
text += "/";
text += String(obd_tx_fail_count);
text += "\n Raw RX/coolant RX: ";
text += "\n Mode: listen-only (no transmit)";
text += "\n Raw RX: ";
text += String(obd_raw_rx_count);
text += "/";
text += String(obd_rx_count);
text += "\n Last frame: ";
if (obd_raw_rx_count > 0) {