From 4335d711878eddd18f4c837da7709715c3fd725c Mon Sep 17 00:00:00 2001 From: sl-firmware Date: Sun, 19 Apr 2026 22:54:03 -0400 Subject: [PATCH] fix(balance): TWAI bus-off detection, auto-recovery, console off UART0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - vesc_can: poll twai_get_status_info() every 500ms; auto-recover from bus-off (twai_initiate_recovery) and stopped state (twai_start) - vesc_can: expose g_twai_bus_off / g_twai_tx_err_count / g_twai_rx_err_count - main: set flags bit2 when TWAI is bus-off (visible in TELEM_STATUS) - sdkconfig: switch console from UART0 (conflicts with binary protocol at 460800) to USB serial JTAG — eliminates log corruption on Orin Flags byte: bit0=estop, bit1=hb_timeout, bit2=twai_bus_off Co-Authored-By: Claude Sonnet 4.6 --- esp32s3/balance/main/main.c | 10 +++++---- esp32s3/balance/main/vesc_can.c | 36 +++++++++++++++++++++++++++++- esp32s3/balance/main/vesc_can.h | 5 +++++ esp32s3/balance/sdkconfig.defaults | 4 +--- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/esp32s3/balance/main/main.c b/esp32s3/balance/main/main.c index c382c04..d019475 100644 --- a/esp32s3/balance/main/main.c +++ b/esp32s3/balance/main/main.c @@ -41,9 +41,10 @@ static void telem_task(void *arg) state = BAL_ARMED; } - /* flags: bit0=estop_active, bit1=heartbeat_timeout */ + /* flags: bit0=estop_active, bit1=heartbeat_timeout, bit2=twai_bus_off */ uint8_t flags = (g_orin_ctrl.estop ? 0x01u : 0x00u) | - (hb_timeout ? 0x02u : 0x00u); + (hb_timeout ? 0x02u : 0x00u) | + (g_twai_bus_off ? 0x04u : 0x00u); /* Battery voltage from VESC_ID_A STATUS_5 (V×10 → mV) */ uint16_t vbat_mv = (uint16_t)((int32_t)g_vesc[0].voltage_x10 * 100); @@ -96,8 +97,9 @@ void app_main(void) s_orin_tx_q = xQueueCreate(ORIN_TX_QUEUE_DEPTH, sizeof(orin_tx_frame_t)); configASSERT(s_orin_tx_q); - /* Seed heartbeat timer so we don't immediately timeout */ - g_orin_ctrl.hb_last_ms = (uint32_t)(esp_timer_get_time() / 1000LL); + /* Seed timers so we don't immediately trip hb_timeout or drive_stale */ + g_orin_ctrl.hb_last_ms = (uint32_t)(esp_timer_get_time() / 1000LL); + g_orin_drive.updated_ms = g_orin_ctrl.hb_last_ms; /* Create tasks */ xTaskCreate(orin_serial_rx_task, "orin_rx", 4096, s_orin_tx_q, 10, NULL); diff --git a/esp32s3/balance/main/vesc_can.c b/esp32s3/balance/main/vesc_can.c index 866016a..bdcaca8 100644 --- a/esp32s3/balance/main/vesc_can.c +++ b/esp32s3/balance/main/vesc_can.c @@ -17,6 +17,9 @@ static const char *TAG = "vesc_can"; vesc_state_t g_vesc[2] = {0}; +volatile bool g_twai_bus_off = false; +volatile uint32_t g_twai_tx_err_count = 0; +volatile uint32_t g_twai_rx_err_count = 0; /* Index for a given VESC node ID: 0=VESC_ID_A, 1=VESC_ID_B */ static int vesc_idx(uint8_t id) @@ -55,15 +58,46 @@ void vesc_can_send_rpm(uint8_t vesc_id, int32_t erpm) msg.data[1] = (uint8_t)(u >> 16u); msg.data[2] = (uint8_t)(u >> 8u); msg.data[3] = (uint8_t)(u); - twai_transmit(&msg, pdMS_TO_TICKS(5)); + esp_err_t ret = twai_transmit(&msg, pdMS_TO_TICKS(5)); + if (ret != ESP_OK) { + ESP_LOGW(TAG, "twai_transmit vesc_id=%u erpm=%ld: %s", + vesc_id, (long)erpm, esp_err_to_name(ret)); + } } void vesc_can_rx_task(void *arg) { QueueHandle_t tx_q = (QueueHandle_t)arg; twai_message_t msg; + uint32_t status_tick = 0; for (;;) { + /* Poll TWAI health every 500 ms and recover from bus-off */ + uint32_t now_ms = (uint32_t)(esp_timer_get_time() / 1000LL); + if ((now_ms - status_tick) >= 500u) { + status_tick = now_ms; + twai_status_info_t si; + if (twai_get_status_info(&si) == ESP_OK) { + g_twai_bus_off = (si.state == TWAI_STATE_BUS_OFF); + g_twai_tx_err_count = si.tx_error_counter; + g_twai_rx_err_count = si.rx_error_counter; + if (si.state == TWAI_STATE_BUS_OFF) { + ESP_LOGW(TAG, "TWAI bus-off — initiating recovery"); + twai_initiate_recovery(); + } else if (si.state == TWAI_STATE_STOPPED) { + ESP_LOGW(TAG, "TWAI stopped — restarting"); + twai_start(); + } + if (si.tx_error_counter > 0 || si.rx_error_counter > 0) { + ESP_LOGW(TAG, "TWAI errs tx=%lu rx=%lu msgs_tx=%lu msgs_rx=%lu", + (unsigned long)si.tx_error_counter, + (unsigned long)si.rx_error_counter, + (unsigned long)si.msgs_to_tx, + (unsigned long)si.msgs_to_rx); + } + } + } + if (twai_receive(&msg, pdMS_TO_TICKS(50)) != ESP_OK) { continue; } diff --git a/esp32s3/balance/main/vesc_can.h b/esp32s3/balance/main/vesc_can.h index b5d82c7..21a96fb 100644 --- a/esp32s3/balance/main/vesc_can.h +++ b/esp32s3/balance/main/vesc_can.h @@ -28,6 +28,11 @@ typedef struct { /* ── Globals (two VESC nodes: index 0 = VESC_ID_A=56, 1 = VESC_ID_B=68) ── */ extern vesc_state_t g_vesc[2]; +/* TWAI bus health — set by vesc_can_rx_task, read by telem_task for flags bit2 */ +extern volatile bool g_twai_bus_off; +extern volatile uint32_t g_twai_tx_err_count; +extern volatile uint32_t g_twai_rx_err_count; + /* ── API ── */ void vesc_can_init(void); void vesc_can_send_rpm(uint8_t vesc_id, int32_t erpm); diff --git a/esp32s3/balance/sdkconfig.defaults b/esp32s3/balance/sdkconfig.defaults index fe9981c..6c2b31e 100644 --- a/esp32s3/balance/sdkconfig.defaults +++ b/esp32s3/balance/sdkconfig.defaults @@ -5,7 +5,5 @@ CONFIG_ESP_TASK_WDT_EN=y CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 CONFIG_TWAI_ISR_IN_IRAM=y CONFIG_UART_ISR_IN_IRAM=y -CONFIG_ESP_CONSOLE_UART_DEFAULT=y -CONFIG_ESP_CONSOLE_UART_NUM=0 -CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y CONFIG_LOG_DEFAULT_LEVEL_INFO=y