'0' and 'false' are not valid values for a bool symbol. The parser emits a note and falls back to 'n', so the resulting configuration is correct but the build is noisy. Set the two remaining example symbols to 'default n' to make the value explicit and silence the note.
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
|---|
TWAI Console Example
This example demonstrates using the TWAI (Two-Wire Automotive Interface) driver through an interactive console interface. It provides comprehensive TWAI functionality including frame transmission/reception, message filtering, and bus monitoring. The example can be used for both standalone testing via loopback mode and real TWAI network communication.
Supported Commands:
| Command | Description | Linux can-utils Equivalent |
|---|---|---|
twai_init <controller> -t <tx> -r <rx> [opts] |
Initialize TWAI controller with GPIO and mode options | ip link set can0 up type can bitrate 500000 |
twai_deinit <controller> |
Deinitialize TWAI controller | ip link set can0 down |
twai_send <controller> <frame> |
Send TWAI frame (standard/extended/RTR/FD) | cansend can0 123#DEADBEEF |
twai_dump <controller>[,filter] [-t mode] / twai_dump <controller> --stop |
Monitor TWAI traffic with hardware filtering and timestamps | candump can0 |
twai_info <controller> |
Display controller configuration, status | ip -details link show can0 |
twai_recover <controller> [-t <ms>] |
Recover controller from Bus-Off state | N/A |
- Note:
twai_dumpruns continuously in the background. Usetwai_dump <controller> --stopto stop monitoring.
How to Use This Example
Hardware Required
- Any ESP development board with TWAI support.
- A TWAI transceiver (e.g., SN65HVD230, TJA1050).
- Jumper wires.
Hardware Setup
Connect the ESP board to a transceiver:
ESP32 Pin Transceiver TWAI Bus
--------- ----------- --------
GPIO4 (TX) --> CTX
GPIO5 (RX) <-- CRX
3.3V/5V --> VCC
GND --> GND
TWAI_H <--> TWAI_H
TWAI_L <--> TWAI_L
Note: The specific GPIO pins for TX and RX must be provided in the twai_init command.
Quick Start - No Transceiver Mode
For immediate testing without any external hardware, you can use the No Transceiver Mode by connecting a single GPIO pin to itself.
# Connect GPIO4 to itself (or leave it unconnected for self-test)
# Initialize with the same TX/RX GPIO, and enable loopback and self-test modes.
twai> twai_init twai0 -t 4 -r 4 --loopback --self-test
# Send a test frame
twai> twai_send twai0 123#DEADBEEF
# Check controller status
twai> twai_info twai0
This mode is ideal for learning the commands, testing application logic, and debugging frame formats without a physical bus.
Configure the project
idf.py menuconfig
Navigate to Example Configuration -> TWAI Configuration and configure:
- Default Arbitration Bitrate: Default arbitration bitrate in bits per second (bps).
- Default FD Data Bitrate: Default data bitrate for TWAI-FD in bits per second (bps).
- Enable TWAI-FD Support: Enable TWAI-FD (Flexible Data-rate) support (default: disabled)
- TX Queue Length: Length of the transmission queue for TWAI messages (default: 10)
Note: For every controller, you must specify the TX and RX pins explicitly with the -t and -r options when issuing twai_init. Failing to do so will make initialization return an error.
Build and Flash
idf.py -p PORT flash monitor
(To exit the serial monitor, type Ctrl-].)
Command Reference
twai_init
Initializes and starts the TWAI driver. TX and RX pins are required.
Usage:
twai_init <controller> -t <tx_gpio> -r <rx_gpio> [options]
Arguments:
<controller>: Controller ID (twai0,twai1).-t, --tx: TX GPIO pin number (required).-r, --rx: RX GPIO pin number (required).-b, --bitrate: Arbitration bitrate in bps (default: CONFIG_EXAMPLE_DEFAULT_BITRATE).-B, --fd-bitrate: Data bitrate for TWAI-FD (FD-capable chips only, default: CONFIG_EXAMPLE_DEFAULT_FD_BITRATE).--loopback: Enable loopback mode.--self-test: Enable self-test mode (internal loopback).--listen: Enable listen-only mode.-c, --clk-out: Clock output GPIO pin (optional).-o, --bus-off: Bus-off indicator GPIO pin (optional).
twai_deinit
Stops and de-initializes the TWAI driver.
Usage:
twai_deinit <controller>
twai_send
Sends a standard, extended, RTR, or TWAI-FD frame.
Usage:
twai_send <controller> <frame_str>
Frame Formats:
- Standard:
123#DEADBEEF(11-bit ID) - Extended:
12345678#CAFEBABE(29-bit ID) - RTR:
456#Ror456#R8(Remote Transmission Request) - TWAI-FD:
123##{flags}{data}(FD-capable chips only)- flags: single hex nibble
0..F- bit0 (
0x1) = BRS (Bit Rate Switch, accelerate data phase) - bit1 (
0x2) = ESI (Error State Indicator) - other bits reserved (set to 0)
- bit0 (
- data: up to 64 bytes (0..64) of hex pairs, optional
.separators allowed (e.g.11.22.33) - example:
123##1DEADBEEF(BRS enabled, data = DE AD BE EF)
- flags: single hex nibble
twai_dump
Monitors TWAI bus messages with filtering and candump-style output. This command runs in the background.
Usage:
-
twai_dump [-t <mode>] <controller>[,filter...] -
twai_dump <controller> --stop -
Options:
-
-t <mode>: Timestamp mode. Output format is(seconds.microseconds)with 6-digit microsecond precision, e.g.(1640995200.890123).a: Absolute time (esp_timer microseconds since boot)d: Delta time between frames (time since previous frame)z: Zero-relative time from start (time since dump started)n: No timestamp (default)
-
--stop: Stop monitoring the specified controller.
Filter Formats:
id:mask: Mask filter (e.g.,123:7FF).low-high: Range filter (e.g.,100-200, FD-capable chips only).- Multiple filters can be combined with commas (e.g.,
twai0,123:7FF,100-200).
twai_info
Displays the TWAI controller's configuration and real-time status.
Usage:
twai_info <controller>
Output Includes:
- Status (Stopped, Running, Bus-Off)
- Node State (Error Active, Error Passive, etc.)
- TX/RX Error Counters
- Bitrate (Arbitration and Data)
- Configured GPIOs and operational modes.
twai_recover
Initiates recovery for a controller that is in the Bus-Off state.
Usage:
twai_recover <controller> [-t <ms>]
Options:
-t <ms>: Recovery timeout.-1: Block indefinitely until recovery completes (default).0: Asynchronous recovery (returns immediately).>0: Timeout in milliseconds.
Notes:
- Recovery only works when the controller is in Bus-Off state
- Use
twai_info <controller>to check current node state - Recovery may fail if bus conditions are still problematic
- In async mode (timeout=0), use
twai_infoto monitor recovery progress
Typical Command Sequence:
twai_init- Initialize controllertwai_info- Check statustwai_dump- Start monitoring (optional)twai_send- Send framestwai_recover- Recover from errors (if needed)twai_deinit- Cleanup
Basic usage example:
# Initialize controller 0 (bitrate 500 kbps, specify TX/RX pins)
twai> twai_init twai0 -b 500000 -t 4 -r 5
# Display controller information
twai> twai_info twai0
TWAI0 Status: Running
Node State: Error Active
Error Counters: TX=0, RX=0
Bitrate: 500000 bps
GPIOs: TX=GPIO4, RX=GPIO5
# Send standard frame on controller 0
twai> twai_send twai0 123#DEADBEEF
# Start monitoring controller 0 (accept all frames)
twai> twai_dump twai0
# Example received frame display (with default no timestamps)
twai0 123 [4] DE AD BE EF
FD-Capable Chips Example
# Initialize controller 0 with TWAI-FD enabled
twai> twai_init twai0 -b 1000000 -t 4 -r 5 -B 2000000
twai> twai_info twai0
TWAI0 Status: Running
Node State: Error Active
Error Counters: TX=0, RX=0
Bitrate: 1000000 bps (FD: 2000000 bps)
GPIOs: TX=GPIO4, RX=GPIO5
# Send FD frame with BRS on controller 0
twai> twai_send twai0 456##1DEADBEEFCAFEBABE1122334455667788
PC Environment Setup (For Full Testing)
To test bidirectional communication between ESP32 and PC, set up a SocketCAN environment on Ubuntu:
Prerequisites
- Ubuntu 18.04 or later
- USB-to-CAN adapter (PEAK PCAN-USB recommended)
- sudo access for network interface configuration
Quick Setup
- Install CAN utilities:
sudo apt update
sudo apt install -y can-utils
# Verify installation
candump --help
cansend --help
- Configure CAN interface:
# Classic CAN setup (500 kbps)
sudo ip link set can0 up type can bitrate 500000
# CAN-FD setup (1M arbitration, 4M data) - requires FD-capable adapter
sudo ip link set can0 up type can bitrate 1000000 dbitrate 4000000 fd on
# Verify interface
ip -details link show can0
- Test PC setup:
# Terminal 1: Monitor
candump can0
# Terminal 2: Send test frame
cansend can0 123#DEADBEEF
# Send FD frame (if FD adapter available)
cansend can0 456##1DEADBEEFCAFEBABE
Bidirectional Testing
Once both PC and ESP32 are set up:
ESP32 to PC:
# PC: Start monitoring
candump can0
# ESP32: Initialize controller 0 and send frame
twai> twai_init twai0 -t 4 -r 5
twai> twai_send twai0 123#DEADBEEF
# PC shows: can0 123 [4] DE AD BE EF
# ESP32 shows: twai0 123 [4] DE AD BE EF
PC to ESP32:
# ESP32: Start monitoring controller 0
twai> twai_dump twai0
# PC: Send frame
cansend can0 456#CAFEBABE
# ESP32 shows: twai0 456 [4] CA FE BA BE
# Stop monitoring
twai> twai_dump twai0 --stop
Advanced Features
Frame Formats
- Standard frames:
123#DEADBEEF(11-bit ID) - Extended frames:
12345678#CAFEBABE(29-bit ID) - RTR frames:
456#R8(Remote Transmission Request) - TWAI-FD frames:
123##1DEADBEEF(FD with flags, FD-capable chips only) - Data separators:
123#DE.AD.BE.EF(dots ignored)
TWAI-FD Frame Format and Examples (FD-capable chips only)
TWAI-FD frames use two # characters: ID##{flags}{data}.
- Flags are a single hex nibble (
0..F). Bit meanings:0x1BRS: Enable Bit Rate Switch for the data phase0x2ESI: Error State Indicator- Other bits are reserved (set to 0)
- Data payload supports up to 64 bytes. The driver maps the payload length to the proper DLC per CAN FD rules automatically.
- Payload hex pairs may include
.separators for readability (ignored by the parser).
# FD frame without BRS (flags = 0) on controller 0
twai> twai_send twai0 123##0DEADBEEFCAFEBABE1122334455667788
# FD frame with BRS (flags = 1, higher data speed)
twai> twai_send twai0 456##1DEADBEEFCAFEBABE1122334455667788
# FD frame with ESI (flags = 2)
twai> twai_send twai0 789##2DEADBEEF
# FD frame with BRS + ESI (flags = 3)
twai> twai_send twai0 ABC##3DEADBEEF
# Large FD frame (up to 64 bytes)
twai> twai_send twai0 DEF##1000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F
Filtering and Monitoring
# Monitor controller 0 (accept all frames)
twai> twai_dump twai0
twai0 123 [4] DE AD BE EF
twai0 456 [2] CA FE
twai0 789 [8] 11 22 33 44 55 66 77 88
# Monitor with absolute timestamps
twai> twai_dump -t a twai0
(1640995200.890123) twai0 123 [4] DE AD BE EF
(1640995200.895555) twai0 456 [2] CA FE
(1640995200.901000) twai0 789 [8] 11 22 33 44 55 66 77 88
# Monitor with delta timestamps (time between frames)
twai> twai_dump -t d twai0
(0.000000) twai0 123 [4] DE AD BE EF
(0.005432) twai0 456 [2] CA FE
(0.005445) twai0 789 [8] 11 22 33 44 55 66 77 88
# Monitor with zero-relative timestamps (from start of monitoring)
twai> twai_dump -t z twai0
(0.000000) twai0 123 [4] DE AD BE EF
(0.005432) twai0 456 [2] CA FE
(0.010877) twai0 789 [8] 11 22 33 44 55 66 77 88
# Monitor without timestamps (default)
twai> twai_dump -t n twai0
twai0 123 [4] DE AD BE EF
twai0 456 [2] CA FE
twai0 789 [8] 11 22 33 44 55 66 77 88
# Monitor controller 0 with exact ID filter (only receive ID=0x123)
twai> twai_dump twai0,123:7FF
Mask Filter 0: ID=0x00000123, mask=0x000007FF, STD
twai0 123 [4] DE AD BE EF
# Monitor controller 0 with ID range 0x100-0x10F (mask filter approach)
twai> twai_dump twai0,100:7F0
Mask Filter 0: ID=0x00000100, mask=0x000007F0, STD
# Monitor controller 0 with range filter (0xa to 0x15) - FD-capable chips only
twai> twai_dump twai0,a-15
Range Filter 0: 0x0000000a - 0x00000015, STD
# Monitor controller 0 with range filter (0x000 to 0x666)
twai> twai_dump twai0,000-666
Range Filter 0: 0x00000000 - 0x00000666, STD
# Monitor controller 0 with mixed filters (mask + range)
twai> twai_dump twai0,123:7FF,a-15
Mask Filter 0: ID=0x00000123, mask=0x000007FF, STD
Range Filter 0: 0x0000000a - 0x00000015, STD
# Monitor controller 0 with dual filters
twai> twai_dump twai0,020:7F0,013:7F8
Mask Filter 0: ID=0x00000020, mask=0x000007F0, STD
Mask Filter 1: ID=0x00000013, mask=0x000007F8, STD
# Monitor controller 0 with multiple range filters
twai> twai_dump twai0,10-20,100-200
Range Filter 0: 0x00000010 - 0x00000020, STD
Range Filter 1: 0x00000100 - 0x00000200, STD
# Monitor all frames on controller 0 (no filter)
twai> twai_dump twai0
# Stop monitoring controller 0
twai> twai_dump twai0 --stop
Filter Types (FD-capable chips only):
- Mask filters:
id:maskformat - Uses bitwise matching with configurable mask - Range filters:
low-highformat - Hardware range filtering for ID ranges - Mixed filtering: Combine both types in one command for maximum flexibility
Testing Modes
No Transceiver Mode (Testing without external hardware):
# Use same GPIO for TX and RX with loopback and self-test
twai> twai_init twai0 -t 4 -r 4 --loopback --self-test
twai> twai_dump twai0
twai> twai_send twai0 123#DEADBEEF
# Frame appears immediately in dump output:
twai0 123 [4] DE AD BE EF
twai> twai_dump twai0 --stop
Note: This mode is perfect for testing TWAI functionality without external transceivers or wiring. The same GPIO is used for both TX and RX, and the combination of --loopback and --self-test flags ensures frames are properly transmitted and received internally.
Loopback mode (with external transceiver):
twai> twai_init twai0 -t 4 -r 5 --loopback
twai> twai_dump twai0
twai> twai_send twai0 123#54455354
# Frame appears immediately in dump output:
twai0 123 [4] 54 45 53 54
# Stop monitoring when done
twai> twai_dump twai0 --stop
# FD loopback test (FD-capable chips only)
twai> twai_init twai0 -t 4 -r 5 --loopback -B 2000000
twai> twai_dump twai0
twai> twai_send twai0 456##1DEADBEEFCAFEBABE1122334455667788
twai0 456 [16] DE AD BE EF CA FE BA BE 11 22 33 44 55 66 77 88 # FD frame (BRS)
# Stop monitoring
twai> twai_dump twai0 --stop
Listen-only mode:
twai> twai_init twai0 -t 4 -r 5 --listen
twai> twai_dump twai0
# Can receive but cannot send frames
# Stop with: twai_dump twai0 --stop
Error Recovery and Diagnostics
Bus-Off Recovery:
The TWAI controller can enter a Bus-Off state due to excessive error conditions. Use the twai_recover command to initiate recovery:
# Basic recovery (default: block until complete)
twai> twai_recover twai0
I (1234) cmd_twai_core: Starting recovery from Bus-Off state...
I (1345) cmd_twai_core: Waiting for recovery to complete...
I (1456) cmd_twai_core: Recovery completed successfully in 100 ms
# Recovery with custom timeout
twai> twai_recover twai0 -t 5000
I (1234) cmd_twai_core: Starting recovery from Bus-Off state...
I (1345) cmd_twai_core: Waiting for recovery to complete...
I (1456) cmd_twai_core: Recovery completed successfully in 150 ms
# Asynchronous recovery (return immediately)
twai> twai_recover twai0 -t 0
I (1234) cmd_twai_core: Starting recovery from Bus-Off state...
I (1245) cmd_twai_core: Recovery initiated (async mode)
# If node is not in Bus-Off state
twai> twai_recover twai0
I (1234) cmd_twai_core: Recovery not needed - node is Error Active
Enhanced Status Information:
The twai_info command now displays real-time dynamic status information:
twai> twai_info twai0
TWAI0 Status: Running
Node State: Error Active
Error Counters: TX=0, RX=0
Bitrate: 500000 bps
GPIOs: TX=GPIO4, RX=GPIO5
Status and Node State Interpretations:
- Status: Running: Driver initialized and operational (not Bus-Off)
- Status: Bus-Off: Controller offline due to excessive errors, requires recovery
- Status: Stopped: Driver not initialized
- Node State: Error Active: Normal operation, can transmit and receive freely
- Node State: Error Warning: Warning level reached (error counters ≥ 96)
- Node State: Error Passive: Passive mode (error counters ≥ 128, limited transmission)
- Node State: Bus Off: Controller offline (TX error counter ≥ 256, requires recovery)
Troubleshooting
Restoring Default Configuration:
Use twai_deinit <controller> followed by twai_init <controller> to reset the driver to default settings.
Bus-Off Recovery Issues:
- "Node is not in Bus-Off state": Recovery only works when the controller is in Bus-Off state. Use
twai_infoto check current node state. - Recovery timeout: If recovery takes longer than expected, try increasing the timeout with
-toption (e.g.,-t 15000for 15 seconds). - Recovery fails: Check physical bus conditions, ensure proper termination and that other nodes are present to acknowledge recovery frames.
TWAI-FD specific issues:
- "TWAI-FD frames not supported": Your chip doesn't support FD mode. Use chips like ESP32-C5
- FD bitrate validation: Ensure FD data bitrate is higher than arbitration bitrate
- PC FD compatibility: Ensure your PC CAN adapter supports FD mode
PC CAN issues:
# Reset PC interface
sudo ip link set can0 down
sudo ip link set can0 up type can bitrate 500000
# For FD mode
sudo ip link set can0 up type can bitrate 1000000 dbitrate 4000000 fd on
No communication:
- Verify bitrates match on both sides
- For FD: Ensure both sides support FD and have compatible transceivers
- Check physical connections and transceiver power
- GPIO pins must be specified in the twai_init command (common: TX=GPIO4, RX=GPIO5)
- For no-transceiver testing, use the same GPIO for both TX and RX with
--loopback --self-testflags - Ensure proper CAN bus termination (120Ω resistors)
- Use loopback mode to test ESP32 functionality independently
Quick diagnostic with no-transceiver mode:
# Test if basic TWAI functionality works
twai> twai_init twai0 -t 4 -r 4 --loopback --self-test
twai> twai_send twai0 123#DEADBEEF
# If this fails, check ESP-IDF installation and chip support
Common Error Messages:
# Controller ID missing
twai> twai_send 123#DEADBEEF
E (1234) cmd_twai_send: Controller ID is required
# Interface not initialized
twai> twai_send twai0 123#DEADBEEF
E (1234) cmd_twai_send: TWAI0 not initialized
# Invalid frame format
twai> twai_send twai0 123DEADBEEF
E (1456) cmd_twai_send: Frame string is required (format: 123#AABBCC or 12345678#AABBCC)
# Invalid controller ID
twai> twai_init twai5 -t 4 -r 5
E (1678) cmd_twai_core: Invalid controller ID