Compare commits

..

2 Commits

Author SHA1 Message Date
Sebastien Vayrette
06219afe69 fix: Move CAN TWAI to GPIO 43/44 where transceiver is actually wired
Diagnostic proved UART protocol works (ACKs received) but CAN has zero
communication. Root cause: ESP32 connects to Orin via USB Serial/JTAG
(CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y), NOT UART0 on GPIO 43/44.
The SN65HVD230 CAN transceiver is still physically on GPIO 43/44
(original pre-bd-66hx wiring was never changed).

Fix: Put TWAI on GPIO 43/44 where the transceiver actually is.
Move unused UART0 pin config to GPIO 17/18 to avoid conflict.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 11:38:44 -04:00
34a937628d fix: guard TWAI tx against bus-off and fix recovery state machine
Three bugs blocked CAN forwarding when UART commands were received:
1. vesc_can_send_rpm had no g_twai_bus_off guard, flooding failed
   twai_transmit calls during BUS_OFF/RECOVERING states.
2. Recovery only handled TWAI_STATE_BUS_OFF; RECOVERING and STOPPED
   states were unhandled, leaving g_twai_bus_off=false while TWAI
   was still unusable.
3. No startup delay after twai_start() — VESC not yet ready to ACK
   caused immediate TEC runup to BUS_OFF at boot.

Fix: bus-off guard in send_rpm, full state machine in rx_task
(BUS_OFF→initiate, STOPPED→start, RECOVERING→wait), 200ms post-
start delay in vesc_can_init().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 11:38:44 -04:00
2 changed files with 37 additions and 11 deletions

View File

@ -11,17 +11,21 @@
* to IO2/IO1 when deploying this firmware. See docs/SAUL-TEE-SYSTEM-REFERENCE.md. * to IO2/IO1 when deploying this firmware. See docs/SAUL-TEE-SYSTEM-REFERENCE.md.
*/ */
/* ── Orin serial (CH343 USB-to-UART, 1a86:55d3 on Orin side) ── */ /* ── Orin serial: USB Serial/JTAG (native USB, no GPIO needed) ──
* sdkconfig: CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
* The OrinESP32 link uses the built-in USB-CDC peripheral.
* UART0 on GPIO 43/44 is NOT used for Orin comms.
*/
#define ORIN_UART_PORT UART_NUM_0 #define ORIN_UART_PORT UART_NUM_0
#define ORIN_UART_BAUD 460800 #define ORIN_UART_BAUD 460800
#define ORIN_UART_TX_GPIO 43 /* ESP32→CH343 RXD */ #define ORIN_UART_TX_GPIO 17 /* unused — Orin uses USB-CDC */
#define ORIN_UART_RX_GPIO 44 /* CH343 TXD→ESP32 */ #define ORIN_UART_RX_GPIO 18 /* unused — Orin uses USB-CDC */
#define ORIN_UART_RX_BUF 1024 #define ORIN_UART_RX_BUF 1024
#define ORIN_TX_QUEUE_DEPTH 16 #define ORIN_TX_QUEUE_DEPTH 16
/* ── VESC CAN TWAI (SN65HVD230 transceiver, rewired for bd-66hx) ── */ /* ── VESC CAN TWAI (SN65HVD230 transceiver on original GPIO 43/44) ── */
#define VESC_CAN_TX_GPIO 2 /* ESP32 TWAI TX → SN65HVD230 TXD */ #define VESC_CAN_TX_GPIO 43 /* ESP32 TWAI TX → SN65HVD230 TXD */
#define VESC_CAN_RX_GPIO 1 /* SN65HVD230 RXD → ESP32 TWAI RX */ #define VESC_CAN_RX_GPIO 44 /* SN65HVD230 RXD → ESP32 TWAI RX */
#define VESC_CAN_RX_QUEUE 32 #define VESC_CAN_RX_QUEUE 32
/* VESC node IDs — matched to bd-wim1 TELEM_VESC_LEFT/RIGHT mapping */ /* VESC node IDs — matched to bd-wim1 TELEM_VESC_LEFT/RIGHT mapping */

View File

@ -39,17 +39,22 @@ void vesc_can_init(void)
twai_timing_config_t tcfg = TWAI_TIMING_CONFIG_500KBITS(); twai_timing_config_t tcfg = TWAI_TIMING_CONFIG_500KBITS();
twai_filter_config_t fcfg = TWAI_FILTER_CONFIG_ACCEPT_ALL(); twai_filter_config_t fcfg = TWAI_FILTER_CONFIG_ACCEPT_ALL();
ESP_LOGI(TAG, "TWAI: twai_driver_install tx=%d rx=%d 500kbps", VESC_CAN_TX_GPIO, VESC_CAN_RX_GPIO); gcfg.tx_queue_len = 5;
ESP_LOGI(TAG, "TWAI: installing driver tx=%d rx=%d 500kbps", VESC_CAN_TX_GPIO, VESC_CAN_RX_GPIO);
ESP_ERROR_CHECK(twai_driver_install(&gcfg, &tcfg, &fcfg)); ESP_ERROR_CHECK(twai_driver_install(&gcfg, &tcfg, &fcfg));
ESP_LOGI(TAG, "TWAI: driver installed OK"); ESP_LOGI(TAG, "TWAI: driver installed OK");
ESP_LOGI(TAG, "TWAI: twai_start");
ESP_ERROR_CHECK(twai_start()); ESP_ERROR_CHECK(twai_start());
/* Wait for VESC to join the bus before drive_task begins TX — prevents
* immediate TEC runup and BUS_OFF if VESC CAN isn't ready at ESP32 boot. */
vTaskDelay(pdMS_TO_TICKS(200));
ESP_LOGI(TAG, "TWAI: started OK — bus active"); ESP_LOGI(TAG, "TWAI: started OK — bus active");
} }
void vesc_can_send_rpm(uint8_t vesc_id, int32_t erpm) void vesc_can_send_rpm(uint8_t vesc_id, int32_t erpm)
{ {
if (g_twai_bus_off) { return; }
uint32_t ext_id = ((uint32_t)VESC_PKT_SET_RPM << 8u) | vesc_id; uint32_t ext_id = ((uint32_t)VESC_PKT_SET_RPM << 8u) | vesc_id;
twai_message_t msg = { twai_message_t msg = {
.extd = 1, .extd = 1,
@ -81,11 +86,28 @@ void vesc_can_rx_task(void *arg)
} }
twai_status_info_t si; twai_status_info_t si;
if (twai_get_status_info(&si) == ESP_OK) { if (twai_get_status_info(&si) == ESP_OK) {
g_twai_bus_off = (si.state == TWAI_STATE_BUS_OFF); /* Mark bus-off for ANY non-running state so vesc_can_send_rpm
if (g_twai_bus_off) { * won't flood with failed transmits during recovery. */
ESP_LOGE(TAG, "TWAI BUS OFF — tx_err=%lu rx_err=%lu", g_twai_bus_off = (si.state != TWAI_STATE_RUNNING);
if (si.state == TWAI_STATE_BUS_OFF) {
ESP_LOGE(TAG, "TWAI BUS OFF tx_err=%lu rx_err=%lu — recovering",
(unsigned long)si.tx_error_counter, (unsigned long)si.rx_error_counter); (unsigned long)si.tx_error_counter, (unsigned long)si.rx_error_counter);
twai_initiate_recovery();
/* Driver auto-transitions RECOVERING→STOPPED after 128 recessive
* bit occurrences (~3 ms min at 500kbps); 100 ms is safe headroom. */
vTaskDelay(pdMS_TO_TICKS(100));
} else if (si.state == TWAI_STATE_STOPPED) {
/* Recovery completed — restart the driver. */
esp_err_t serr = twai_start();
if (serr == ESP_OK) {
g_twai_bus_off = false;
ESP_LOGI(TAG, "TWAI recovered — bus active");
} else {
ESP_LOGE(TAG, "TWAI restart failed 0x%x", serr);
}
} }
/* TWAI_STATE_RECOVERING: initiation already called, just wait. */
} }
continue; continue;
} }