Bus-Off Recovery
If the TWAI peripheral enters the bus-off state (too many errors), call twai_initiate_recovery() followed by twai_start() to bring it back online. Common causes include incorrect bit rate, missing termination, or wiring faults.
The board uses a TJA1051T/3 CAN transceiver to provide CAN 2.0 and CAN FD tolerant (ISO 11898-2:2016) connectivity. The “/3” suffix denotes the V_IO variant, which provides level-shifted TXD and RXD logic pins that directly match the ESP32-S3’s 3.3V GPIO levels without requiring an external level shifter. The ESP32-S3 includes a built-in TWAI (Two-Wire Automotive Interface) peripheral that handles the CAN protocol in hardware, including arbitration, error detection, and automatic retransmission. The TJA1051T/3 provides the physical layer, translating between the microcontroller’s logic-level signals and the differential CAN bus signaling. The interface is exposed on a 3-pin screw terminal carrying CANH, CANL, and GND.
CAN bus was designed by Bosch in the 1980s for automotive wiring harnesses, and its fundamental properties — deterministic arbitration, built-in error handling, and multi-master architecture — have made it the backbone of automotive, industrial, and robotics communication ever since.
| Signal | GPIO | Description |
|---|---|---|
| CAN TX | IO15 | TWAI TX to TJA1051T |
| CAN RX | IO16 | TWAI RX from TJA1051T |
Connect CANH to CANH and CANL to CANL between nodes. CAN bus requires 120 ohm termination at both physical ends of the bus. The board includes an onboard 120 ohm termination resistor with jumper selection at position 19 (default: OFF). Place the jumper cap to enable termination.
The CAN bus lines are protected by SM24CANB TVS diodes, providing protection against surge events and ESD discharge on the bus wiring.
The TJA1051T/3 is an AEC-Q100 qualified CAN transceiver from NXP, designed for automotive and industrial applications where reliability under extreme conditions is non-negotiable.
| Parameter | Value |
|---|---|
| Part number | TJA1051T/3/1J |
| Standard | ISO 11898-2:2016 (CAN FD tolerant) |
| Qualification | AEC-Q100 Grade 1 (-40 to +125 C) |
| Supply voltage (VCC) | 4.5V to 5.5V |
| I/O level voltage (V_IO) | 1.8V to 5.5V |
| Data rate | Up to 5 Mbit/s (CAN FD) |
| Bus pin voltage tolerance | -58V to +58V |
| Loop delay (TXD to RXD) | 200 ns typical |
| Dominant timeout | 2.5 ms to 7 ms |
| Operating modes | Normal, Silent |
| Standby current | < 10 uA |
The V_IO pin is what distinguishes the “/3” variant from the base TJA1051T. The base part requires 5V logic levels on TXD and RXD, which would necessitate a level shifter between the transceiver and the ESP32-S3’s 3.3V GPIOs. The “/3” variant accepts a separate V_IO supply (1.8V to 5.5V) that sets the logic-level reference for TXD and RXD. On this board, V_IO is connected to 3.3V, allowing direct connection to the ESP32-S3 without any additional circuitry.
The TJA1051T/3 supports two operating modes, selected by the S (silent) pin:
On this board, the S pin is tied to GND, permanently selecting Normal mode. To use Silent mode, the S pin trace would need to be cut and routed to a GPIO for software control.
The TJA1051T/3 incorporates several protection mechanisms that make it suitable for safety-critical applications in automotive and industrial environments:
| Bit Rate | Max Bus Length | ESP-IDF Timing Macro |
|---|---|---|
| 125 kbit/s | 500 m | TWAI_TIMING_CONFIG_125KBITS() |
| 250 kbit/s | 250 m | TWAI_TIMING_CONFIG_250KBITS() |
| 500 kbit/s | 100 m | TWAI_TIMING_CONFIG_500KBITS() |
| 1 Mbit/s | 25 m | TWAI_TIMING_CONFIG_1MBITS() |
The relationship between bit rate and maximum bus length is governed by the propagation delay of the electrical signal along the cable and the requirement that all nodes sample each bit at the same point in time. Higher bit rates demand shorter cables.
This example initializes the TWAI peripheral at 500 kbit/s and sends a standard CAN frame with ID 0x123 every second.
#include "driver/twai.h"
#define CAN_TX_PIN 15#define CAN_RX_PIN 16
void setup() { Serial.begin(115200);
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT( (gpio_num_t)CAN_TX_PIN, (gpio_num_t)CAN_RX_PIN, TWAI_MODE_NORMAL); twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS(); twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
if (twai_driver_install(&g_config, &t_config, &f_config) == ESP_OK) { Serial.println("TWAI driver installed"); } twai_start();}
void loop() { twai_message_t msg = { .identifier = 0x123, .data_length_code = 8, .data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} };
if (twai_transmit(&msg, pdMS_TO_TICKS(1000)) == ESP_OK) { Serial.println("Message sent"); } else { Serial.println("Send failed"); } delay(1000);}This example waits for incoming CAN frames and prints their ID, DLC, and payload bytes to the serial monitor.
#include "driver/twai.h"
#define CAN_TX_PIN 15#define CAN_RX_PIN 16
void setup() { Serial.begin(115200);
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT( (gpio_num_t)CAN_TX_PIN, (gpio_num_t)CAN_RX_PIN, TWAI_MODE_NORMAL); twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS(); twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
twai_driver_install(&g_config, &t_config, &f_config); twai_start(); Serial.println("TWAI receiver ready");}
void loop() { twai_message_t msg; if (twai_receive(&msg, pdMS_TO_TICKS(1000)) == ESP_OK) { Serial.printf("ID: 0x%03X DLC: %d Data:", msg.identifier, msg.data_length_code); for (int i = 0; i < msg.data_length_code; i++) { Serial.printf(" %02X", msg.data[i]); } Serial.println(); }}Bus-Off Recovery
If the TWAI peripheral enters the bus-off state (too many errors), call twai_initiate_recovery() followed by twai_start() to bring it back online. Common causes include incorrect bit rate, missing termination, or wiring faults.
Message Filtering
The TWAI_FILTER_CONFIG_ACCEPT_ALL() macro accepts every frame on the bus. For production systems, configure acceptance filters to reduce interrupt load by only accepting message IDs your node cares about.