# Two-Wire Automotive Interface (TWAI)

[中文]

This document introduces the features of the Two-Wire Automotive Interface (TWAI) controller driver in ESP-IDF. The chapter structure is as follows:

## Overview

TWAI is a highly reliable, multi-master, real-time, serial asynchronous communication protocol designed for automotive and industrial applications. It is compatible with the frame structure defined in the ISO 11898-1 standard and supports both standard frames with 11-bit identifiers and extended frames with 29-bit identifiers. The protocol supports message prioritization with lossless arbitration, automatic retransmission, and fault confinement mechanisms. The ESP32-H4 includes 1 TWAI controllers, allowing for the creation of 1 driver instances.

The TWAI controllers on the ESP32-H4 also compatible with FD format (a.k.a. CAN FD) frames defined in ISO 11898-1, and can transmit and receive both classic and FD format frames.

Thanks to its hardware-based fault tolerance and multi-master architecture, the TWAI driver is ideal for scenarios such as:

- Serving as a robust communication bus in environments with significant electrical noise

- Enabling long-distance communication across multiple sensors/actuators with resilience to single-node failures

- Building decentralized distributed local networks that avoid the unpredictability of single-master designs

- Acting as a bridging node alongside other communication protocols

## Getting Started

This section provides a quick overview of how to use the TWAI driver. Through simple examples, it demonstrates how to create a TWAI node instance, transmit and receive messages on the bus, and safely stop and uninstall the driver. The general usage flow is as follows:

### Hardware Connection

The ESP32-H4 does not integrate an internal TWAI transceiver. Therefore, an external transceiver is required to connect to a TWAI bus. The model of the external transceiver depends on the physical layer standard used in your specific application. For example, a TJA105x transceiver can be used to comply with the ISO 11898-2 standard.

Specifically:

- For single-node testing, you can directly short the TX and RX pins to omit the transceiver.

- BUS_OFF (optional): Outputs a low logic level (0 V) when the TWAI controller enters the bus-off state. Otherwise, it remains at a high logic level (3.3 V).

- CLK_OUT (optional): Outputs the time quantum clock of the controller, which is a divided version of the source clock.

### Creating and Starting a TWAI Node

First, we need to create a TWAI instance. The following code demonstrates how to create a TWAI node with a baud rate of 200 kHz:

    #include "esp_twai.h"
    #include "esp_twai_onchip.h"

    twai_node_handle_t node_hdl = NULL;
    twai_onchip_node_config_t node_config = {
        .io_cfg.tx = 4,             // TWAI TX GPIO pin
        .io_cfg.rx = 5,             // TWAI RX GPIO pin
        .bit_timing.bitrate = 200000,  // 200 kbps bitrate
        .tx_queue_depth = 5,        // Transmit queue depth set to 5
    };
    // Create a new TWAI controller driver instance
    ESP_ERROR_CHECK(twai_new_node_onchip(&node_config, &node_hdl));
    // Start the TWAI controller
    ESP_ERROR_CHECK(twai_node_enable(node_hdl));

When creating a TWAI instance, you must configure parameters such as GPIO pins and baud rate using the `twai_onchip_node_config_t` structure. These parameters determine how the TWAI node operates. Then, you can call the `twai_new_node_onchip()` function to create a new TWAI instance. This function returns a handle to the newly created instance. A TWAI handle is essentially a pointer to an internal TWAI memory object of type `twai_node_handle_t`.

Below are additional configuration fields of the `twai_onchip_node_config_t` structure along with their descriptions:

- `twai_onchip_node_config_t::clk_src`: Specifies the clock source used by the controller. Supported sources are listed in `twai_clock_source_t`.

- `twai_onchip_node_config_t::bit_timing::sp_permill`: Specifies the location of the sample point. ssp_permill sets the location of the secondary sample point and can be used to fine-tune timing in low SNR conditions.

- `twai_onchip_node_config_t::data_timing`: Specifies the baud rate and sample point for the data phase in FD frames. This field is ignored if the controller does not support FD format.

- `twai_onchip_node_config_t::fail_retry_cnt`: Sets the number of retry attempts on transmission failure. -1 indicates infinite retries until success or bus-off; 0 disables retries (single-shot mode); 1 retries once, and so on.

- `twai_onchip_node_config_t::intr_priority`: Interrupt priority in the range [0:3], where higher values indicate higher priority.

- `twai_onchip_node_config_t::flags`: A set of flags for fine-tuning driver behavior. Options include:

  > - `twai_onchip_node_config_t::flags::enable_self_test`: Enables self-test mode. In this mode, ACK is not checked during transmission, which is useful for single-node testing.

  > - `twai_onchip_node_config_t::flags::enable_loopback`: Enables loopback mode. The node will receive its own transmitted messages (subject to filter configuration), while also transmitting them to the bus.

  > - `twai_onchip_node_config_t::flags::enable_listen_only`: Configures the node in listen-only mode. In this mode, the node only receives and does not transmit any dominant bits, including ACK and error frames.

  > - `twai_onchip_node_config_t::flags::no_receive_rtr`: When using filters, determines whether remote frames matching the ID pattern should be filtered out.

  > - `twai_onchip_node_config_t::flags::enable_scheduled_tx`: Enables scheduled transmission. This option requires a non-zero `twai_onchip_node_config_t::timestamp_resolution_hz`.

The `twai_node_enable()` function starts the TWAI controller. Once enabled, the controller is connected to the bus and can transmit messages. It also generates events upon receiving messages from other nodes on the bus or when bus errors are detected.

The corresponding function, `twai_node_disable()`, immediately stops the node and disconnects it from the bus. Any ongoing transmissions will be aborted. When the node is re-enabled later, if there are pending transmissions in the queue, the driver will immediately initiate a new transmission attempt.

### Transmitting Messages

TWAI messages come in various types, which are specified by their headers. A typical data frame consists primarily of a header and data payload, with a structure similar to the following:

To reduce performance overhead caused by memory copying, the TWAI driver uses pointers to pass messages. The driver is designed to operate in asynchronous mode, so the `twai_frame_t` structure and the memory pointed to by `twai_frame_t::buffer` must remain valid until the transmission is actually complete. You can determine when transmission is complete in the following ways:

- Call the `twai_node_transmit_wait_all_done()` function to wait for all transmissions to complete.

- Register the `twai_event_callbacks_t::on_tx_done` event callback function to receive a notification when transmission is complete.

The following code demonstrates how to transmit a typical data frame:

    uint8_t send_buff[8] = {0};
    twai_frame_t tx_msg = {
        .header.id = 0x1,           // Message ID
        .header.ide = true,         // Use 29-bit extended ID format
        .buffer = send_buff,        // Pointer to data to transmit
        .buffer_len = sizeof(send_buff),  // Length of data to transmit
    };
    ESP_ERROR_CHECK(twai_node_transmit(node_hdl, &tx_msg, 0));  // Timeout = 0: returns immediately if queue is full
    ESP_ERROR_CHECK(twai_node_transmit_wait_all_done(node_hdl, -1));  // Wait for transmission to finish

In this example, `twai_frame_t::header::id` specifies the ID of the message as 0x01. Message IDs are typically used to indicate the type of message in an application and also play a role in bus arbitration during transmission—lower values indicate higher priority on the bus. `twai_frame_t::buffer` points to the memory address where the data to be transmitted is stored, and `twai_frame_t::buffer_len` specifies the length of that data. The `twai_node_transmit()` function is thread-safe and can also be called from an ISR. When called from an ISR, the `timeout` parameter is ignored, and the function will not block.

Note that `twai_frame_t::header::dlc` can also specify the length of the data in the frame. The DLC (Data Length Code) is mapped to the actual data length as defined in ISO 11898-1. You can use either `twaifd_dlc2len()` or `twaifd_len2dlc()` for conversion. If both dlc and buffer_len are non-zero, they must represent the same length.

The `twai_frame_t` message structure also includes other configuration fields:

- `twai_frame_t::dlc`: Data Length Code. For classic frames, values [0:8] represent lengths [0:8]; for FD format, values [0:15] represent lengths up to 64 bytes.

- `twai_frame_t::header::ide`: Indicates use of a 29-bit extended ID format.

- `twai_frame_t::header::rtr`: Indicates the frame is a remote frame, which contains no data payload.

- `twai_frame_t::header::fdf`: Marks the frame as an FD format frame, supporting up to 64 bytes of data.

- `twai_frame_t::header::brs`: Enables use of a separate data-phase baud rate when transmitting.

- `twai_frame_t::header::esi`: For received frames, indicates the error state of the transmitting node.

- `twai_frame_t::tx_queue_priority`: Local transmit queue priority. See Transmit Queue Priority for details.

### Receiving Messages

Receiving messages must be done within a receive event callback. Therefore, to receive messages, you need to register a receive event callback via `twai_event_callbacks_t::on_rx_done` before starting the controller. This enables the controller to deliver received messages via the callback when events occur. The following code snippets demonstrate how to register the receive event callback and how to handle message reception inside the callback:

Registering the receive event callback (before starting the controller):

    twai_event_callbacks_t user_cbs = {
        .on_rx_done = twai_rx_cb,
    };
    ESP_ERROR_CHECK(twai_node_register_event_callbacks(node_hdl, &user_cbs, NULL));

Receiving messages inside the callback:

    static bool twai_rx_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx)
    {
        uint8_t recv_buff[8];
        twai_frame_t rx_frame = {
            .buffer = recv_buff,
            .buffer_len = sizeof(recv_buff),
        };
        if (ESP_OK == twai_node_receive_from_isr(handle, &rx_frame)) {
            // receive ok, do something here
        }
        return false;
    }

Similarly, since the driver uses pointers for message passing, you must configure the pointer `twai_frame_t::buffer` and its memory length `twai_frame_t::buffer_len` before receiving.

### Frame Timestamp

The TWAI driver supports creating a 64-bit timestamp for each successfully received frame, enabling this feature by configuring the `twai_onchip_node_config_t::timestamp_resolution_hz` field when creating the node. The timestamp is stored in the `twai_frame_t::header::timestamp` field of the received frame.

The node time inherits from the system time, i.e. the time starts from the power-on of the chip, and is not affected by the stop/restart/BUS_OFF state during the node's lifetime.

### Scheduled Transmission

The ESP32-H4 TWAI supports schedule a transmitted frame by trigger time. Enable `twai_onchip_node_config_t::flags::enable_scheduled_tx` and set `twai_onchip_node_config_t::timestamp_resolution_hz` when creating the node, then fill `twai_frame_t::header::trigger_time` before calling `twai_node_transmit()`. The trigger time uses the same timebase as received frame timestamps.

    twai_onchip_node_config_t node_config = {
        .io_cfg.tx = 4,
        .io_cfg.rx = 5,
        .bit_timing.bitrate = 500000,
        .timestamp_resolution_hz = 1000,  // 1 tick = 1 ms
        .tx_queue_depth = 4,
        .flags.enable_scheduled_tx = true,
    };

    twai_frame_t tx_msg = {
        .header.id = 0x10,
        .header.trigger_time = 2000,  // transmit when node timestamp reaches 2000 ticks
    };
    ESP_ERROR_CHECK(twai_node_transmit(node_hdl, &tx_msg, 0));

Note

If the frame's trigger time has already been reached when the frame is ready to transmit, the driver starts transmitting it immediately. When multiple scheduled frames are queued, the driver processes them in software submission order. Frames are not reordered by `twai_frame_t::header::trigger_time`, so a later-submitted frame with an earlier trigger time cannot overtake frames submitted before it.

### Stopping and Deleting the Node

When the TWAI node is no longer needed, you should call `twai_node_delete()` to release software and hardware resources. Make sure the TWAI controller is stopped before deleting the node.

Note: Due to the design of the driver, please do not call the delete function in the FreeRTOS timer task.

## Advanced Features

After understanding the basic usage, you can further explore more advanced capabilities of the TWAI driver. The driver supports more detailed controller configuration and error feedback features. The complete driver feature diagram is shown below:

### Transmit from ISR

The TWAI driver supports transmitting messages from an Interrupt Service Routine (ISR). This is particularly useful for applications requiring low-latency responses or periodic transmissions triggered by hardware timers. For example, you can trigger a new transmission from within the `on_tx_done` callback, which is executed in an ISR context.

    static bool twai_tx_done_cb(twai_node_handle_t handle, const twai_tx_done_event_data_t *edata, void *user_ctx)
    {
        // A frame has been successfully transmitted. Queue another one.
        // The frame and its data buffer must be valid until transmission is complete.
        static const uint8_t data_buffer[] = {1, 2, 3, 4};
        static const twai_frame_t tx_frame = {
            .header.id = 0x2,
            .buffer = (uint8_t *)data_buffer,
            .buffer_len = sizeof(data_buffer),
        };

        // The `twai_node_transmit` is safe to be called in an ISR context
        twai_node_transmit(handle, &tx_frame, 0);
        return false;
    }

Note

When calling `twai_node_transmit()` from an ISR, the `timeout` parameter is ignored, and the function will not block. If the transmit queue is full, the function will return immediately with an error. It is the application's responsibility to handle cases where the queue is full. Similarly, the `twai_frame_t` structure and the memory pointed to by `buffer` must remain valid until the transmission is complete. You can get the completed frame by the `twai_tx_done_event_data_t::done_tx_frame` pointer.

### Transmit Queue Priority

The TWAI driver supports local transmit queue prioritization through `twai_frame_t::tx_queue_priority`. When multiple frames are pending in the driver's transmit queue, frames with a higher `tx_queue_priority` value are dequeued and started transmitting first. Frames with the same priority keep their enqueue order.

This priority only affects the driver's local transmit queue. It is not transmitted on the TWAI bus and does not replace TWAI bus arbitration. If the controller has multiple hardware transmit buffers (for example, 4 hardware transmit buffers for esp32c5), the already cached frames will not be preempted by newly queued higher-priority frames. Once a frame reaches the bus, arbitration is still determined by the frame ID, where lower IDs have higher bus priority.

### Bit Timing Customization

Unlike other asynchronous communication protocols, the TWAI controller performs counting and sampling within one bit time in units of Time Quanta (Tq). The number of time quanta per bit determines the final baud rate and the sample point position. When signal quality is poor, you can manually fine-tune these timing segments to meet specific requirements. The time quanta within a bit time are divided into different segments, as illustrated below:

The synchronization segment (sync) is fixed at 1 Tq. The sample point lies between time segments tseg1 and tseg2. The Synchronization Jump Width (SJW) defines the maximum number of time quanta by which a bit time can be lengthened or shortened for synchronization purposes, ranging from [1 : tseg2]. The clock source divided by the baud rate prescaler (BRP) equals the time quantum. The total sum of all segments equals one bit time. Therefore, the following formula applies:

- Baud rate (bitrate):

\[\text{bitrate} = \frac{f_{\text{src}}}{\text{brp} \cdot (1 + \text{prop_seg} + \text{tseg}_1 + \text{tseg}_2)}\]

- Sample point:

\[\text{sample_point} = \frac{1 + \text{prop_seg} + \text{tseg}_1}{1 + \text{prop_seg} + \text{tseg}_1 + \text{tseg}_2}\]

The following code demonstrates how to configure a baud rate of 500 Kbit/s with a sample point at 75% when using an 80 MHz clock source:

    twai_timing_advanced_config_t timing_cfg = {
        .brp = 8,       // Prescaler set to 8, time quantum = 80M / 8 = 10 MHz (10M Tq)
        .prop_seg = 10, // Propagation segment
        .tseg_1 = 4,    // Phase segment 1
        .tseg_2 = 5,    // Phase segment 2
        .sjw = 3,       // Synchronization Jump Width
    };
    ESP_ERROR_CHECK(twai_node_reconfig_timing(node_hdl, &timing_cfg, NULL)); // Configure arbitration phase timing; NULL means FD data phase timing is not configured

When manually configuring these timing segments, it is important to pay attention to the supported range of each segment according to the specific hardware. The timing configuration function `twai_node_reconfig_timing()` can configure the timing parameters for both the arbitration phase and the FD data phase either simultaneously or separately. When the controller does not support FD format, the data phase configuration is ignored. The timing parameter struct `twai_timing_advanced_config_t` also includes the following additional configuration fields:

- `twai_timing_advanced_config_t::clk_src` — The clock source.

- `twai_timing_advanced_config_t::ssp_offset` — The number of time quanta by which the secondary sample point (SSP) is offset relative to the synchronization segment.

Note

Different combinations of `brp`, `prop_seg`, `tseg_1`, `tseg_2`, and `sjw` can achieve the same baud rate. Users should consider factors such as propagation delay, node processing time, and phase errors, and adjust the timing parameters based on the physical characteristics of the bus.

### Filter Configuration

#### Mask Filters

The TWAI controller hardware can filter messages based on their ID to reduce software and hardware overhead, thereby improving node efficiency. Nodes that filter out certain messages will not receive those messages, but will still send acknowledgments (ACKs).

ESP32-H4 includes 3 mask filters. A message passing through any one of these filters will be received by the node. A typical TWAI mask filter is configured with an ID and a MASK, where:

- ID: represents the expected message ID, either the standard 11-bit or extended 29-bit format.

- MASK: defines the filtering rules for each bit of the ID:

  > - '0' means the corresponding bit is ignored (any value passes).

  > - '1' means the corresponding bit must match exactly to pass.

  > - When both ID and MASK are 0, the filter ignores all bits and accepts all frames.

  > - When both ID and MASK are set to the maximum 0xFFFFFFFF, the filter accepts no frames.

The following code demonstrates how to calculate the MASK and configure a filter:

    twai_mask_filter_config_t mfilter_cfg = {
        .id = 0x10,         // 0b 000 0001 0000
        .mask = 0x7f0,      // 0b 111 1111 0000 — the upper 7 bits must match strictly, the lower 4 bits are ignored, accepts IDs of the form
                            // 0b 000 0001 xxxx (hex 0x01x)
        .is_ext = false,    // Accept only standard IDs, not extended IDs
    };
    ESP_ERROR_CHECK(twai_node_config_mask_filter(node_hdl, 0, &mfilter_cfg));   // Configure on filter 0

#### Range Filter

ESP32-H4 also includes 1 range filter, which exists alongside the mask filters. You can configure the desired ID reception range directly using the function `twai_node_config_range_filter()`. The details are as follows:

- Setting :cpp:member:twai_range_filter_config_t::range_low to the minimum value 0, and :cpp:member:twai_range_filter_config_t::range_high to the maximum value 0xFFFFFFFF means receiving all messages.

- Configuring an invalid range means no messages will be received.

### Bus Errors and Recovery

The TWAI controller can detect errors caused by bus interference or corrupted frames that do not conform to the frame format. It implements a fault isolation mechanism using transmit and receive error counters (TEC and REC). The values of these counters determine the node's error state: Error Active, Error Warning, Error Passive, and Bus Off. This mechanism ensures that nodes with persistent errors eventually disconnect themselves from the bus.

- Error Active: When both TEC and REC are less than 96, the node is in the active error state, meaning normal operation. The node participates in bus communication and sends active error flags when errors are detected to actively report them.

- Error Warning: When either TEC or REC is greater than or equal to 96 but both are less than 128, the node is in the warning error state. Errors may exist but the node behavior remains unchanged.

- Error Passive: When either TEC or REC is greater than or equal to 128, the node enters the passive error state. It can still communicate on the bus but sends only one passive error flag when detecting errors.

- Bus Off: When TEC is greater than or equal to 256, the node enters the bus off (offline) state. The node is effectively disconnected and does not affect the bus. It remains offline until recovery is triggered by software.

Software can retrieve the node status from tasks via the function `twai_node_get_info()`. When the controller detects errors, it triggers the `twai_event_callbacks_t::on_error` callback, where the error data provides detailed information.

When the node’s error state changes, the `twai_event_callbacks_t::on_state_change` callback is triggered, allowing the application to respond to the state transition. If the node is offline and needs recovery, call `twai_node_recover()` from a task context. Note that recovery is not immediate; the controller will automatically reconnect to the bus only after detecting 129 consecutive recessive bits (11 bits each).

When recovery completes, the `twai_event_callbacks_t::on_state_change` callback will be triggered again, the node changes its state from `TWAI_ERROR_BUS_OFF` to `TWAI_ERROR_ACTIVE`. A recovered node can immediately resume transmissions; if there are pending tasks in the transmit queue, the driver will start transmitting them right away.

### Power Management

When power management is enabled via CONFIG_PM_ENABLE, the system may adjust or disable clock sources before entering sleep mode, which could cause TWAI to malfunction. To prevent this, the driver manages a power management lock internally. This lock is acquired when calling `twai_node_enable()`, ensuring the system does not enter sleep mode and TWAI remains functional. To allow the system to enter a low-power state, call `twai_node_disable()` to release the lock. During sleep, the TWAI controller will also stop functioning.

#### About Sleep Retention

ESP32-H4 supports powering down the TWAI controller during Light Sleep to further reduce power consumption and automatically restore after waking up. This means the application does not need to reconfigure TWAI after Light Sleep wake up.

Enable the option CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP, and set `twai_onchip_node_config_t::flags::sleep_allow_pd` to `true` when initializing the TWAI node to enable this feature. Otherwise, the TWAI controller will remain powered during Light Sleep. This feature helps reduce power consumption during light sleep but requires additional storage to save register configurations.

### Cache Safety

During Flash write operations, the system temporarily disables cache to prevent instruction and data fetch errors from Flash. This can cause interrupt handlers stored in Flash to become unresponsive. If you want interrupt routines to remain operational during cache-disabled periods, enable the CONFIG_TWAI_ISR_CACHE_SAFE option.

Note

When this option is enabled, all interrupt callback functions and their context data must reside in internal memory, because the system cannot fetch instructions or data from Flash while the cache is disabled.

### Thread Safety

The driver guarantees thread safety for all public TWAI APIs. You can safely call these APIs from different RTOS tasks without requiring additional synchronization or locking mechanisms.

### Performance

To improve the real-time performance of interrupt handling, the driver provides the CONFIG_TWAI_ISR_IN_IRAM option. When enabled, the TWAI ISR (Interrupt Service Routine) and receive operations are placed in internal RAM, reducing latency caused by instruction fetching from Flash.

For applications that require high-performance transmit operations, the driver provides the CONFIG_TWAI_IO_FUNC_IN_IRAM option to place transmit functions in IRAM. This is particularly beneficial for time-critical applications that frequently call `twai_node_transmit()` from user tasks.

Note

However, user-defined callback functions and context data invoked by the ISR may still reside in Flash. To fully eliminate Flash latency, users must place these functions and data into internal RAM using macros such as `IRAM_ATTR` for functions and `DRAM_ATTR` for data.

### Resource Usage

You can inspect the Flash and memory usage of the TWAI driver using the IDF Size tool. Below are the test conditions (based on the ESP32-C6 as an example):

- Compiler optimization level is set to `-Os` to minimize code size.

- Default log level is set to `ESP_LOG_INFO` to balance debugging information and performance.

- The following driver optimization options are disabled:

  > - CONFIG_TWAI_ISR_IN_IRAM – ISR is not placed in IRAM.

  > - CONFIG_TWAI_ISR_CACHE_SAFE – Cache safety option is disabled.

The following resource usage data is for reference only. Actual values may vary across different target chips.

Component Layer | Total Size | DIRAM | .bss | .data | .text | Flash | .rodata | .text
--- | --- | --- | --- | --- | --- | --- | --- | ---
driver | 7262 | 12 | 12 | 0 | 0 | 7250 | 506 | 6744
hal | 1952 | 0 | 0 | 0 | 0 | 0 | 0 | 1952
soc | 64 | 0 | 0 | 0 | 0 | 64 | 64 | 0

Resource Usage with CONFIG_TWAI_ISR_IN_IRAM Enabled:

Component Layer | Total Size | DIRAM | .bss | .data | .text | Flash | .rodata | .text
--- | --- | --- | --- | --- | --- | --- | --- | ---
driver | 7248 | 692 | 12 | 0 | 680 | 6556 | 506 | 6050
hal | 1952 | 1030 | 0 | 0 | 1030 | 922 | 0 | 922
soc | 64 | 0 | 0 | 0 | 0 | 0 | 64 | 0

Additionally, each TWAI handle dynamically allocates approximately `168` + 4 * `twai_onchip_node_config_t::tx_queue_depth` bytes of memory from the heap.

### Other Kconfig Options

- CONFIG_TWAI_ENABLE_DEBUG_LOG: This option forces all debug logs of the TWAI driver to be enabled regardless of the global log level settings. Enabling this can help developers obtain more detailed log information during debugging, making it easier to locate and resolve issues.

## Application Examples

- peripherals/twai/twai_utils demonstrates how to use the TWAI (Two-Wire Automotive Interface) APIs to create a command-line interface for TWAI bus communication, supporting frame transmission/reception, filtering, monitoring, and both classic and FD formats for testing and debugging TWAI networks.

- peripherals/twai/twai_error_recovery demonstrates how to recover nodes from the bus-off state and resume communication, as well as bus error reporting, node state changes, and other event information.

- peripherals/twai/twai_network using 2 nodes with different roles: transmitting and listening, demonstrates how to use the driver for single and bulk data transmission, as well as configure filters to receive these data.

- peripherals/twai/cybergear demonstrates how to control XiaoMi CyberGear motors via TWAI interface.

- peripherals/twai/usb_twai_adapter demonstrates how to make an USB-CAN adapter and enumerate it to a socket can device.

## API Reference

### On-Chip TWAI APIs

#### Header File

- components/esp_driver_twai/include/esp_twai_onchip.h

- This header file can be included with:

  >     #include "esp_twai_onchip.h"

- This header file is a part of the API provided by the `esp_driver_twai` component. To declare that your component depends on `esp_driver_twai`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_twai

  > or

  >     PRIV_REQUIRES esp_driver_twai

#### Functions

esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twai_node_handle_t *node_ret)

  Allocate a TWAI hardware node by specific init config structure To delete/free the TWAI, call `twai_node_delete()`

  Parameters:

    - node_config -- [in] Init config structure

    - node_ret -- [out] Return driver handle

  Returns:

    ESP_OK Allocate success ESP_ERR_NO_MEM No enough free memory ESP_ERR_NOT_FOUND No free hardware controller ESP_ERR_INVALID_ARG Config argument not available ESP_ERR_INVALID_STATE State error, including hardware state error and driver state error ESP_FAIL Other reasons

static inline twai_mask_filter_config_t twai_make_dual_filter(uint32_t id1, uint32_t mask1, uint32_t id2, uint32_t mask2, bool is_ext)

  Helper function to configure a dual 16-bit acceptance filter.

  Note

  For 29bits Extended IDs, ONLY high 16bits id/mask is used for each filter.

  Parameters:

    - id1 -- First full 11/29 bits ID to filter.

    - mask1 -- Mask for first ID.

    - id2 -- Second full 11/29 bits ID to filter.

    - mask2 -- Mask for second ID.

    - is_ext -- True if using Extended (29-bit) IDs, false for Standard (11-bit) IDs.

  Returns:

    twai_mask_filter_config_t A filled filter configuration structure for dual filtering.

esp_err_t twai_node_onchip_get_timing_limits(bool is_fd, twai_timing_limits_t *timing_limits)

  Get the hardware-dependent timing limits.

  Parameters:

    - is_fd -- [in] True for FD data timing, false for classic timing

    - timing_limits -- [out] Pointer to timing limits structure

  Returns:

    ESP_OK if successful, ESP_ERR_INVALID_ARG if invalid argument

#### Structures

struct twai_onchip_node_config_t

  TWAI on-chip node initialization configuration structure.

  Public Members

  gpio_num_t tx

    GPIO pin for twai TX

  gpio_num_t rx

    GPIO pin for twai RX

  gpio_num_t quanta_clk_out

    GPIO pin for quanta clock output, Set -1 to not use

  gpio_num_t bus_off_indicator

    GPIO pin for bus-off indicator, Set -1 to not use

  struct twai_onchip_node_config_t io_cfg

    I/O configuration

  twai_clock_source_t clk_src

    Optional, clock source, remain 0 to using TWAI_CLK_SRC_DEFAULT by default

  twai_timing_basic_config_t bit_timing

    Timing configuration for classic twai and FD arbitration stage

  twai_timing_basic_config_t data_timing

    Optional, timing configuration for FD data stage

  uint32_t timestamp_resolution_hz

    Timebase frequency (in Hz), used for RX frame timestamps and scheduled TX trigger times, set 0 to disable the timestamp feature

  int8_t fail_retry_cnt

    Hardware retry limit if failed, range [-1:15], -1 for re-trans forever

  uint32_t tx_queue_depth

    Depth of the transmit queue

  int intr_priority

    Interrupt priority, [0:3]

  uint32_t enable_self_test

    Transmission does not require acknowledgment. Use this mode for self testing

  uint32_t enable_loopback

    The TWAI controller receives back frames that it sends out, but does not acknowledge them

  uint32_t enable_listen_only

    No transmissions or acknowledgements. The controller only monitors the bus without participating

  uint32_t no_receive_rtr

    Don't receive remote frames

  uint32_t sleep_allow_pd

    Allow power down during sleep to save power, driver will backup/restore the TWAI registers to guarantee the peripheral features.

  uint32_t enable_scheduled_tx

    Schedule TX mode, if enabled, the tx frame will actually send until `twai_frame_t::header.trigger_time` is reached, Feature depends on hardware support, and `timestamp_resolution_hz` must be set.

  struct twai_onchip_node_config_t flags

    Misc configuration flags

### TWAI Driver APIs

#### Header File

- components/esp_driver_twai/include/esp_twai.h

- This header file can be included with:

  >     #include "esp_twai.h"

- This header file is a part of the API provided by the `esp_driver_twai` component. To declare that your component depends on `esp_driver_twai`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_twai

  > or

  >     PRIV_REQUIRES esp_driver_twai

#### Functions

esp_err_t twai_node_enable(twai_node_handle_t node)

  Enable the TWAI node.

  Parameters:

    node -- Handle to the TWAI node

  Returns:

    - ESP_OK: Success

    - ESP_ERR_INVALID_STATE: Node already in enabled state

esp_err_t twai_node_disable(twai_node_handle_t node)

  Disable the TWAI node.

  Parameters:

    node -- Handle to the TWAI node

  Returns:

    - ESP_OK: Success

    - ESP_ERR_INVALID_STATE: Node not in enabled state

esp_err_t twai_node_recover(twai_node_handle_t node)

  Init the recover process for TWAI node which in bus-off.

  Note

  Follow `on_state_change` callback or `twai_node_get_info` to know recover finish or not

  Parameters:

    node -- Handle to the TWAI node

  Returns:

    - ESP_OK: Success

    - ESP_ERR_INVALID_STATE: Node not in bus-off state

esp_err_t twai_node_delete(twai_node_handle_t node)

  Delete the TWAI node and release resources.

  Note

  Do NOT call this function in the FreeRTOS timer task.

  Parameters:

    node -- Handle to the TWAI node

  Returns:

    - ESP_OK: Success

    - ESP_ERR_INVALID_STATE: Node not in disabled state

esp_err_t twai_node_register_event_callbacks(twai_node_handle_t node, const twai_event_callbacks_t *cbs, void *user_data)

  Register event callbacks for the TWAI node.

  Parameters:

    - node -- Handle to the TWAI node

    - cbs -- Pointer to a structure of event callback functions

    - user_data -- User-defined data passed to callback functions

  Returns:

    ESP_OK on success, error code otherwise

esp_err_t twai_node_reconfig_timing(twai_node_handle_t node, const twai_timing_advanced_config_t *bit_timing, const twai_timing_advanced_config_t *data_timing)

  Reconfigure the timing settings of the TWAI node.

  Note

  You can reconfigure the timing for the arbitration and data phase, separately or together.

  Parameters:

    - node -- Handle to the TWAI node

    - bit_timing -- Optional,pointer to new twai cc(classic) or arbitration phase of twai fd timing configuration

    - data_timing -- Optional, pointer to new twai fd timing configuration

  Returns:

    ESP_OK on success, error code otherwise

esp_err_t twai_node_config_mask_filter(twai_node_handle_t node, uint8_t filter_id, const twai_mask_filter_config_t *mask_cfg)

  Configure the mask filter of the TWAI node.

  Parameters:

    - node -- Handle to the TWAI node

    - filter_id -- Index of the filter to configure

    - mask_cfg -- Pointer to the mask filter configuration

  Returns:

    - ESP_OK: Success

    - ESP_ERR_INVALID_ARG: Invalid argument

    - ESP_ERR_INVALID_STATE: Node not in disabled state

esp_err_t twai_node_config_range_filter(twai_node_handle_t node, uint8_t filter_id, const twai_range_filter_config_t *range_cfg)

  Configure the range filter of the TWAI node.

  Parameters:

    - node -- Handle to the TWAI node

    - filter_id -- Index of the filter to configure

    - range_cfg -- Pointer to the range filter configuration

  Returns:

    - ESP_OK: Success

    - ESP_ERR_INVALID_ARG: Invalid argument

    - ESP_ERR_INVALID_STATE: Node not in disabled state

esp_err_t twai_node_get_info(twai_node_handle_t node, twai_node_status_t *status_ret, twai_node_record_t *statistics_ret)

  Get information about the TWAI node.

  Parameters:

    - node -- Handle to the TWAI node

    - status_ret -- Pointer to store the current node status

    - statistics_ret -- Pointer to store node statistics

  Returns:

    ESP_OK on success, error code otherwise

esp_err_t twai_node_transmit(twai_node_handle_t node, const twai_frame_t *frame, int timeout_ms)

  Transmit a TWAI frame.

  Parameters:

    - node -- [in] Handle to the TWAI node

    - frame -- [in] Pointer to the frame to transmit

    - timeout_ms -- [in] Maximum wait time if the transmission queue is full (milliseconds), -1 to wait forever

  Returns:

    - ESP_OK: Success

    - ESP_ERR_INVALID_ARG: Invalid argument

    - ESP_ERR_INVALID_STATE: Node already in bus-off state

    - ESP_ERR_NOT_SUPPORTED: Node is config as listen only

    - ESP_ERR_TIMEOUT: Timeout to wait for queue space

esp_err_t twai_node_transmit_wait_all_done(twai_node_handle_t node, int timeout_ms)

  Wait for all pending transfers to finish, if bus-off happens during waiting, wait until node recovered and tx is finished, or timeout.

  Parameters:

    - node -- [in] Handle to the TWAI node

    - timeout_ms -- [in] Maximum wait time for all pending transfers to finish (milliseconds), -1 to wait forever

  Returns:

    - ESP_OK: Success

    - ESP_ERR_INVALID_STATE: Node already in bus-off state

    - ESP_ERR_TIMEOUT: Timeout

esp_err_t twai_node_receive_from_isr(twai_node_handle_t node, twai_frame_t *rx_frame)

  Receive a TWAI frame from 'rx_done_cb'.

  Note

  This function can only be called from the `rx_done_cb` callback, you can't call it from a task.

  Note

  Please also provide `buffer` and `buffer_len` inside the rx_frame

  Note

  Can get original data length from `twaifd_dlc2len(rx_frame.header.dlc)`

  Parameters:

    - node -- [in] Handle to the TWAI node

    - rx_frame -- [out] Pointer to the frame store rx content

  Returns:

    - ESP_OK: Success

    - ESP_ERR_INVALID_STATE: Called from a task or from other callbacks except `rx_done_cb`

### TWAI Driver Types

#### Header File

- components/esp_driver_twai/include/esp_twai_types.h

- This header file can be included with:

  >     #include "esp_twai_types.h"

- This header file is a part of the API provided by the `esp_driver_twai` component. To declare that your component depends on `esp_driver_twai`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_twai

  > or

  >     PRIV_REQUIRES esp_driver_twai

#### Structures

struct twai_timing_basic_config_t

  TWAI bitrate timing config basic (simple) mode.

  Public Members

  uint32_t bitrate

    Expected TWAI bus baud_rate/bitrate in bits/second

  uint16_t sp_permill

    Optional, sampling point in permill (1/1000) of the entire bit time

  uint16_t ssp_permill

    Optional, secondary sample point(ssp) in permill (1/1000) of the entire bit time

struct twai_frame_t

  TWAI transaction frame param type.

  Public Members

  twai_frame_header_t header

    message attribute/metadata, exclude data buffer

  uint8_t *buffer

    buffer address for tx and rx message data

  size_t buffer_len

    buffer length of provided data buffer pointer, in bytes.

  uint8_t tx_queue_priority

    Frame priority, range [0, 255], a frame with higher priority value will be picked first from the queue

struct twai_node_status_t

  TWAI node's status.

  Public Members

  twai_error_state_t state

    Node's error state

  uint16_t tx_error_count

    Node's TX error count

  uint16_t rx_error_count

    Node's RX error count

  uint32_t tx_queue_remaining

    Node's TX queue remaining frame space (number of frames)

struct twai_node_record_t

  TWAI node's statistics/record type.

  This structure contains some statistics regarding a node's communication on the TWAI bus

  Public Members

  uint32_t bus_err_num

    Cumulative number (since `twai_node_enable()`) of bus errors

struct twai_tx_done_event_data_t

  TWAI "TX done" event data.

  Public Members

  bool is_tx_success

    Indicate if frame send successful, refer `on_error` callback for fail reason if send failed

  const twai_frame_t *done_tx_frame

    Pointer to the frame that has been transmitted

struct twai_rx_done_event_data_t

  TWAI "RX done" event data.

struct twai_state_change_event_data_t

  TWAI "state change" event data.

  Public Members

  twai_error_state_t old_sta

    Previous error state

  twai_error_state_t new_sta

    New error state after the change

struct twai_error_event_data_t

  TWAI "error" event data.

  Public Members

  twai_error_flags_t err_flags

    Error flags indicating the type of the error

struct twai_event_callbacks_t

  Group of supported TWAI callbacks.

  Note

  All of these callbacks is invoked from ISR context. Thus, the implementation of the callback function must adhere to the ISR restrictions such as not calling any blocking APIs.

  Note

  Set the particular event callback's entry to NULL to unregister it if not required.

  Note

  When TWAI_ISR_CACHE_SAFE is enabled, the callbacks must be placed in IRAM.

  Public Members

  bool (*on_tx_done)(twai_node_handle_t handle, const twai_tx_done_event_data_t *edata, void *user_ctx)

    TWAI "TX done" event callback prototype.

    Param handle:

      TWAI node handle

    Param edata:

      "TX done" event data (passed by the driver)

    Param user_ctx:

      User data, passed from `twai_node_register_event_callbacks()`

    Return:

      Whether a higher priority task has been unblocked by this function

  bool (*on_rx_done)(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx)

    TWAI "RX done" event callback prototype.

    Param handle:

      TWAI node handle

    Param edata:

      "RX done" event data (passed by the driver)

    Param user_ctx:

      User data, passed from `twai_node_register_event_callbacks()`

    Return:

      Whether a higher priority task has been unblocked by this function

  bool (*on_state_change)(twai_node_handle_t handle, const twai_state_change_event_data_t *edata, void *user_ctx)

    TWAI "state change" event callback prototype.

    Param handle:

      TWAI node handle

    Param edata:

      "state change" event data (passed by the driver)

    Param user_ctx:

      User data, passed from `twai_node_register_event_callbacks()`

    Return:

      Whether a higher priority task has been unblocked by this function

  bool (*on_error)(twai_node_handle_t handle, const twai_error_event_data_t *edata, void *user_ctx)

    TWAI "error" event callback prototype.

    Param handle:

      [in] TWAI node handle

    Param edata:

      [in] "error" event data (passed by the driver)

    Param user_ctx:

      [in] User data, passed from `twai_node_register_event_callbacks()`

    Return:

      Whether a higher priority task has been unblocked by this function

#### Type Definitions

typedef struct twai_node_base *twai_node_handle_t

  ESP TWAI controller handle.

### TWAI HAL Types

#### Header File

- components/esp_hal_twai/include/hal/twai_types.h

- This header file can be included with:

  >     #include "hal/twai_types.h"

- This header file is a part of the API provided by the `esp_hal_twai` component. To declare that your component depends on `esp_hal_twai`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_hal_twai

  > or

  >     PRIV_REQUIRES esp_hal_twai

#### Functions

static inline uint16_t twaifd_dlc2len(uint16_t dlc)

  Translate TWAIFD format DLC code to bytes length.

  Parameters:

    dlc -- [in] The frame DLC code follow the FD spec

  Returns:

    The byte length of DLC stand for

static inline uint16_t twaifd_len2dlc(uint16_t byte_len)

  Translate TWAIFD format bytes length to DLC code.

  Parameters:

    byte_len -- [in] The byte length of the message

  Returns:

    The FD adopted frame DLC code

#### Unions

union twai_error_flags_t
  #include <twai_types.h>

  TWAI transmit error type structure.

  Public Members

  uint32_t arb_lost

    Arbitration lost error (lost arbitration during transmission)

  uint32_t bit_err

    Bit error detected (dominant/recessive mismatch during transmission)

  uint32_t form_err

    Form error detected (frame fixed-form bit violation)

  uint32_t stuff_err

    Stuff error detected (e.g. dominant error frame received)

  uint32_t ack_err

    ACK error (no ack), transmission without acknowledge received

  struct twai_error_flags_t
  uint32_t val

    Integrated error flags

#### Structures

struct twai_timing_advanced_config_t

  TWAI bitrate timing advanced config structure.

  Public Members

  uint32_t brp

    Bitrate pre-divider, which decides the quanta time

  uint8_t prop_seg

    Prop_seg length, in quanta time

  uint8_t tseg_1

    Seg_1 length, in quanta time

  uint8_t tseg_2

    Seg_2 length, in quanta time

  uint8_t sjw

    Sync jump width, in quanta time

  uint8_t ssp_offset

    Secondary sample point offset refet to Sync seg, in quanta time, set 0 to disable ssp

struct twai_timing_limits_t

  TWAI hardware-dependent timing limits const.

  Used for calculating and checking bit-timing parameters

  Public Members

  uint32_t brp_min

    Bit-rate prescaler minimum value

  uint32_t brp_max

    Bit-rate prescaler maximum value

  uint32_t brp_inc

    Bit-rate prescaler increment step

  uint32_t prop_min

    Propagation segment minimum value

  uint32_t prop_max

    Propagation segment maximum value

  uint32_t tseg1_min

    Time segment 1 (phase_seg1) minimum value

  uint32_t tseg1_max

    Time segment 1 (phase_seg1) maximum value

  uint32_t tseg2_min

    Time segment 2 (phase_seg2) minimum value

  uint32_t tseg2_max

    Time segment 2 (phase_seg2) maximum value

  uint32_t sjw_max

    Synchronisation jump width maximum value

struct twai_mask_filter_config_t

  Configuration for TWAI mask filter.

  Public Members

  uint32_t id

    Single base ID for filtering

  uint32_t *id_list

    Base ID list array for filtering, which share the same `mask`

  uint32_t num_of_ids

    List length of `id_list`, remain empty to using single `id` instead of `id_list`

  uint32_t mask

    Mask to determine the matching bits (1 = match bit, 0 = any bit)

  uint32_t is_ext

    True for extended ID filtering, false for standard ID

  uint32_t no_classic

    If true, Classic TWAI frames are excluded (only TWAI FD allowed)

  uint32_t no_fd

    If true, TWAI FD frames are excluded (only Classic TWAI allowed)

  uint32_t dual_filter

    Set filter as dual-16bits filter mode, see `twai_make_dual_filter()` for easy config

struct twai_range_filter_config_t

  Range-based filter configuration structure.

  Public Members

  uint32_t range_low

    Lower bound of the ID filtering range, included

  uint32_t range_high

    Upper bound of the ID filtering range, included

  uint32_t is_ext

    True for extended ID filtering, false for standard ID

  uint32_t no_classic

    If true, Classic TWAI frames are excluded (only TWAI FD allowed)

  uint32_t no_fd

    If true, TWAI FD frames are excluded (only Classic TWAI allowed)

struct twai_frame_header_t

  TWAI frame header/format struct type.

  Public Members

  uint32_t id

    message arbitration identification

  uint16_t dlc

    message data length code

  uint32_t ide

    Extended Frame Format (29bit ID)

  uint32_t rtr

    Message is a Remote Frame

  uint32_t fdf

    Message is FD format, allow max 64 byte of data

  uint32_t brs

    Transmit message with Bit Rate Shift.

  uint32_t esi

    Transmit side error indicator for received frame

  uint64_t timestamp

    Timestamp for received message

  uint64_t trigger_time

    Trigger time for transmitting message in scheduled TX mode

#### Macros

TWAI_STD_ID_MASK

  Mask of the ID fields in a standard frame

TWAI_EXT_ID_MASK

  Mask of the ID fields in an extended frame

TWAI_FRAME_MAX_DLC

TWAI_FRAME_MAX_LEN

TWAIFD_FRAME_MAX_DLC

TWAIFD_FRAME_MAX_LEN

#### Type Definitions

typedef soc_periph_twai_clk_src_t twai_clock_source_t

  TWAI group clock source.

  Note

  User should select the clock source based on the power and resolution requirement

#### Enumerations

enum twai_error_state_t

  TWAI node error fsm states.

  Values:

  enumerator TWAI_ERROR_ACTIVE

    Error active state: TEC/REC < 96

  enumerator TWAI_ERROR_WARNING

    Error warning state: TEC/REC >= 96 and < 128

  enumerator TWAI_ERROR_PASSIVE

    Error passive state: TEC/REC >= 128 and < 256

  enumerator TWAI_ERROR_BUS_OFF

    Bus-off state: TEC >= 256 (node offline)

Was this page helpful?

- Thank you! We received your feedback.
- If you have any comments, fill in Espressif Documentation Feedback Form.

- We value your feedback.
- Let us know how we can improve this page by filling in Espressif Documentation Feedback Form.