diff --git a/components/esp_driver_twai/esp_twai_onchip.c b/components/esp_driver_twai/esp_twai_onchip.c index 9ebf2ef9c6..383bd8f1c3 100644 --- a/components/esp_driver_twai/esp_twai_onchip.c +++ b/components/esp_driver_twai/esp_twai_onchip.c @@ -122,16 +122,19 @@ static void _ctrlr_release(int ctrlr_id) static esp_err_t _node_config_io(twai_onchip_ctx_t *node, const twai_onchip_node_config_t *node_config) { - ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(node_config->io_cfg.tx), ESP_ERR_INVALID_ARG, TAG, "Invalid tx gpio num"); + ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(node_config->io_cfg.tx) || (node_config->flags.enable_listen_only && (node_config->io_cfg.tx == -1)), ESP_ERR_INVALID_ARG, TAG, "Invalid tx gpio num"); ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(node_config->io_cfg.rx), ESP_ERR_INVALID_ARG, TAG, "Invalid rx gpio num"); + uint64_t reserve_mask = 0; // Set RX pin gpio_set_pull_mode(node_config->io_cfg.rx, GPIO_PULLUP_ONLY); // pullup to avoid noise if no connection to transceiver gpio_matrix_input(node_config->io_cfg.rx, twai_periph_signals[node->ctrlr_id].rx_sig, false); // Set TX pin - uint64_t reserve_mask = BIT64(node_config->io_cfg.tx); - gpio_matrix_output(node_config->io_cfg.tx, twai_periph_signals[node->ctrlr_id].tx_sig, false, false); + if (node_config->io_cfg.tx != -1) { // listen only node is able to not have TX pin + reserve_mask |= BIT64(node_config->io_cfg.tx); + gpio_matrix_output(node_config->io_cfg.tx, twai_periph_signals[node->ctrlr_id].tx_sig, false, false); + } //Configure output clock pin (Optional) if (GPIO_IS_VALID_OUTPUT_GPIO(node_config->io_cfg.quanta_clk_out)) { @@ -611,7 +614,7 @@ static esp_err_t _node_parse_rx(twai_node_handle_t node, twai_frame_t *rx_frame) esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twai_node_handle_t *node_ret) { esp_err_t ret = ESP_OK; - ESP_RETURN_ON_FALSE(node_config->tx_queue_depth > 0, ESP_ERR_INVALID_ARG, TAG, "tx_queue_depth at least 1"); + ESP_RETURN_ON_FALSE((node_config->tx_queue_depth > 0) || node_config->flags.enable_listen_only, ESP_ERR_INVALID_ARG, TAG, "tx_queue_depth at least 1"); ESP_RETURN_ON_FALSE(!node_config->intr_priority || (BIT(node_config->intr_priority) & ESP_INTR_FLAG_LOWMED), ESP_ERR_INVALID_ARG, TAG, "Invalid intr_priority level"); // Allocate TWAI node from internal memory because it contains atomic variable @@ -627,7 +630,7 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa // state is in bus_off before enabled atomic_store(&node->state, TWAI_ERROR_BUS_OFF); node->tx_mount_queue = xQueueCreateWithCaps(node_config->tx_queue_depth, sizeof(twai_frame_t *), TWAI_MALLOC_CAPS); - ESP_GOTO_ON_FALSE(node->tx_mount_queue, ESP_ERR_NO_MEM, err, TAG, "no_mem"); + ESP_GOTO_ON_FALSE(node->tx_mount_queue || node_config->flags.enable_listen_only, ESP_ERR_NO_MEM, err, TAG, "no_mem"); uint32_t intr_flags = TWAI_INTR_ALLOC_FLAGS; intr_flags |= (node_config->intr_priority > 0) ? BIT(node_config->intr_priority) : ESP_INTR_FLAG_LOWMED; ESP_GOTO_ON_ERROR(esp_intr_alloc(twai_periph_signals[ctrlr_id].irq_id, intr_flags, _node_isr_main, (void *)node, &node->intr_hdl), diff --git a/components/esp_driver_twai/test_apps/test_twai/main/CMakeLists.txt b/components/esp_driver_twai/test_apps/test_twai/main/CMakeLists.txt index eb5ac2cbaf..a667ea09ec 100644 --- a/components/esp_driver_twai/test_apps/test_twai/main/CMakeLists.txt +++ b/components/esp_driver_twai/test_apps/test_twai/main/CMakeLists.txt @@ -10,6 +10,6 @@ endif() idf_component_register( SRCS ${srcs} - PRIV_REQUIRES esp_driver_twai esp_timer esp_driver_uart esp_psram + PRIV_REQUIRES esp_driver_twai esp_timer esp_driver_uart esp_psram esp_driver_gpio WHOLE_ARCHIVE ) diff --git a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.cpp b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.cpp index 69b068be5d..0fd7e8b1d2 100644 --- a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.cpp +++ b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.cpp @@ -16,6 +16,8 @@ #include "freertos/FreeRTOS.h" #include "esp_twai.h" #include "esp_twai_onchip.h" +#include "soc/twai_periph.h" +#include "esp_private/gpio.h" #include "driver/uart.h" // for baudrate detection #define TEST_TX_GPIO GPIO_NUM_4 @@ -477,3 +479,55 @@ TEST_CASE("twai driver cache safe (loopback)", "[twai]") TEST_ESP_OK(twai_node_delete(node_hdl)); } #endif //CONFIG_TWAI_ISR_CACHE_SAFE + +TEST_CASE("twai bus off recovery (loopback)", "[twai]") +{ + twai_node_handle_t node_hdl; + twai_onchip_node_config_t node_config = { + .io_cfg.tx = TEST_TX_GPIO, + .io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver + .bit_timing.bitrate = 50000, //slow bitrate to ensure soft error trigger + .tx_queue_depth = 1, + .flags.enable_self_test = true, + }; + TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl)); + TEST_ESP_OK(twai_node_enable(node_hdl)); + + twai_node_status_t node_status; + twai_frame_t tx_frame = { + .buffer = (uint8_t *)"hello\n", + .buffer_len = 6, + }; + + // send frames and trigger error, must become bus off before 50 frames + while ((node_status.state != TWAI_ERROR_BUS_OFF) && (tx_frame.header.id < 50)) { + printf("sending frame %ld\n", tx_frame.header.id ++); + TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_frame, 500)); + if (tx_frame.header.id > 3) { // trigger error after 3 frames + printf("trigger bit_error now!\n"); + esp_rom_delay_us(30 * (1000000 / node_config.bit_timing.bitrate)); // trigger error at 30 bits after frame start + gpio_matrix_output(TEST_TX_GPIO, twai_periph_signals[0].tx_sig, true, false); + esp_rom_delay_us(2 * (1000000 / node_config.bit_timing.bitrate)); // trigger error for 2 bits + gpio_matrix_output(TEST_TX_GPIO, twai_periph_signals[0].tx_sig, false, false); + } + vTaskDelay(pdMS_TO_TICKS(100)); // some time for hardware report errors + twai_node_get_info(node_hdl, &node_status, NULL); + } + + // recover node + TEST_ASSERT_EQUAL(TWAI_ERROR_BUS_OFF, node_status.state); + printf("node offline, start recover ...\n"); + TEST_ESP_OK(twai_node_recover(node_hdl)); + + // wait node recovered + while (node_status.state != TWAI_ERROR_ACTIVE) { + printf("waiting ...\n"); + vTaskDelay(pdMS_TO_TICKS(1000)); + twai_node_get_info(node_hdl, &node_status, NULL); + } + printf("node recovered! continue\n"); + TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_frame, 500)); + + TEST_ESP_OK(twai_node_disable(node_hdl)); + TEST_ESP_OK(twai_node_delete(node_hdl)); +} diff --git a/components/hal/twai_hal_sja1000.c b/components/hal/twai_hal_sja1000.c index 737e588851..4c5c32ab09 100644 --- a/components/hal/twai_hal_sja1000.c +++ b/components/hal/twai_hal_sja1000.c @@ -202,8 +202,9 @@ static inline uint32_t twai_hal_decode_interrupt(twai_hal_context_t *hal_ctx) //Error Warning Interrupt set whenever Error or Bus Status bit changes if (interrupts & TWAI_LL_INTR_EI) { if (status & TWAI_LL_STATUS_BS) { //Currently in BUS OFF state - if (status & TWAI_LL_STATUS_ES) { //EWL is exceeded, thus must have entered BUS OFF - TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_OFF); + if (status & TWAI_LL_STATUS_ES) { //EWL is exceeded, thus must have entered BUS OFF + //The last error which trigger bus_off, hardware no longer fire TWAI_HAL_EVENT_BUS_ERR, but error reason still need to be read/clear and report + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_OFF | TWAI_HAL_EVENT_BUS_ERR); TWAI_HAL_SET_BITS(state_flags, TWAI_HAL_STATE_FLAG_BUS_OFF); //Any TX would have been halted by entering bus off. Reset its flag TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_RUNNING | TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED); diff --git a/docs/en/api-reference/peripherals/twai.rst b/docs/en/api-reference/peripherals/twai.rst index f10bf89c02..396683acb1 100644 --- a/docs/en/api-reference/peripherals/twai.rst +++ b/docs/en/api-reference/peripherals/twai.rst @@ -372,6 +372,8 @@ Application Examples .. list:: - :example:`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. + - :example:`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. + - :example:`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. API Reference ------------- diff --git a/docs/zh_CN/api-reference/peripherals/twai.rst b/docs/zh_CN/api-reference/peripherals/twai.rst index c6050e23ae..2c4a5ed622 100644 --- a/docs/zh_CN/api-reference/peripherals/twai.rst +++ b/docs/zh_CN/api-reference/peripherals/twai.rst @@ -372,6 +372,8 @@ TWAI控制器能够检测由于总线干扰产生的/损坏的不符合帧格式 .. list:: - :example:`peripherals/twai/twai_utils` 演示了如何使用 TWAI(Two-Wire Automotive Interface,双线汽车接口)API 创建一个命令行工具,用于 TWAI 总线通信,支持帧的发送/接收、过滤、监控,以及经典和 FD 格式,以便测试和调试 TWAI 网络。 + - :example:`peripherals/twai/twai_error_recovery` 演示了总线错误上报,节点状态变化等事件信息,以及如何从离线状态恢复节点并重新进行通信。 + - :example:`peripherals/twai/twai_network` 通过发送、监听, 2 个不同角色的节点,演示了如何使用驱动程序进行单次的和大量的数据发送,以及配置过滤器以接收这些数据。 API 参考 -------- diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 9f401658b1..108219a239 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -503,32 +503,20 @@ examples/peripherals/touch_sensor/touch_sens_sleep: depends_components: - esp_driver_touch_sens -examples/peripherals/twai/twai_alert_and_recovery: +examples/peripherals/twai/twai_error_recovery: disable: - - if: SOC_TWAI_SUPPORTED != 1 or SOC_TWAI_SUPPORT_FD == 1 - reason: This example not support FD - disable_test: - - if: IDF_TARGET not in ["esp32"] - temporary: true - reason: lack of runners + - if: SOC_TWAI_SUPPORTED != 1 + depends_components: + - esp_driver_twai examples/peripherals/twai/twai_network: disable: - - if: SOC_TWAI_SUPPORTED != 1 or SOC_TWAI_SUPPORT_FD == 1 - reason: This example not support FD + - if: SOC_TWAI_SUPPORTED != 1 disable_test: - - if: 1 == 1 - temporary: true - reason: Test is flakey, TODO IDF-2939 - -examples/peripherals/twai/twai_self_test: - disable: - - if: SOC_TWAI_SUPPORTED != 1 or SOC_TWAI_SUPPORT_FD == 1 - reason: This example not support FD - disable_test: - - if: IDF_TARGET not in ["esp32"] - temporary: true - reason: lack of runners + - if: IDF_TARGET == "esp32" + reason: esp32c5,esp32 test has been disabled, because C5 twai don't support listen only in network test, see errata issue 5 + depends_components: + - esp_driver_twai examples/peripherals/twai/twai_utils: disable: diff --git a/examples/peripherals/twai/twai_alert_and_recovery/CMakeLists.txt b/examples/peripherals/twai/twai_alert_and_recovery/CMakeLists.txt deleted file mode 100644 index e3213669f4..0000000000 --- a/examples/peripherals/twai/twai_alert_and_recovery/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# The following lines of boilerplate have to be in your project's CMakeLists -# in this exact order for cmake to work correctly -cmake_minimum_required(VERSION 3.16) - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -# "Trim" the build. Include the minimal set of components, main, and anything it depends on. -idf_build_set_property(MINIMAL_BUILD ON) -project(can_alert_and_recovery_example) diff --git a/examples/peripherals/twai/twai_alert_and_recovery/README.md b/examples/peripherals/twai/twai_alert_and_recovery/README.md deleted file mode 100644 index f53817ca00..0000000000 --- a/examples/peripherals/twai/twai_alert_and_recovery/README.md +++ /dev/null @@ -1,94 +0,0 @@ -| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | - -# TWAI Alert and Recovery Example - -(See the README.md file in the upper level 'examples' directory for more information about examples.) - -This example demonstrates how to use the alert and bus recovery features of the TWAI driver. The alert feature allows the TWAI driver to notify the application layer of certain TWAI driver or bus events. The bus recovery feature is used to recover the TWAI driver after it has entered the Bus-Off state. See the TWAI driver reference for more details. - -## How to use example - -### Hardware Required - -This example requires only a single target (e.g., an ESP32 or ESP32-S2). The target must be connected to an external transceiver (e.g., a SN65HVD23X transceiver). This connection usually consists of a TX and an RX signal. - -Note: If you don't have an external transceiver, this example can still be run by simply connecting the TX GPIO and RX GPIO with a jumper. - -### Configure the project - -* Set the target of the build (where `{IDF_TARGET}` stands for the target chip such as `esp32`, `esp32c3`). -* Then run `menuconfig` to configure the example. - -```sh -idf.py set-target {IDF_TARGET} -idf.py menuconfig -``` - -* Under `Example Configuration`, configure the pin assignments using the options `TX GPIO Number` and `RX GPIO Number` according to how the target was connected to the transceiver. By default, `TX GPIO Number` and `RX GPIO Number` are set to the following values: - * On the ESP32, `TX GPIO Number` and `RX GPIO Number` default to `21` and `22` respectively - * On other chips, `TX GPIO Number` and `RX GPIO Number` default to `0` and `2` respectively - -### Build and Flash - -Build the project and flash it to the board, then run monitor tool to view serial output: - -```sh -idf.py -p PORT flash monitor -``` - -(Replace PORT with the name of the serial port to use.) - -(To exit the serial monitor, type ``Ctrl-]``.) - -See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. - -## Example Output - -```text -I (330) TWAI Alert and Recovery: Driver installed -I (340) TWAI Alert and Recovery: Driver started -I (340) TWAI Alert and Recovery: Starting transmissions -W (350) TWAI Alert and Recovery: Trigger TX errors in 3 -W (1350) TWAI Alert and Recovery: Trigger TX errors in 2 -W (2350) TWAI Alert and Recovery: Trigger TX errors in 1 -I (3350) TWAI Alert and Recovery: Trigger errors -I (3650) TWAI Alert and Recovery: Surpassed Error Warning Limit -I (3650) TWAI Alert and Recovery: Entered Error Passive state -I (4300) TWAI Alert and Recovery: Bus Off state -W (4300) TWAI Alert and Recovery: Initiate bus recovery in 3 -W (5300) TWAI Alert and Recovery: Initiate bus recovery in 2 -W (6300) TWAI Alert and Recovery: Initiate bus recovery in 1 -I (7300) TWAI Alert and Recovery: Initiate bus recovery -I (7350) TWAI Alert and Recovery: Bus Recovered -I (7350) TWAI Alert and Recovery: Driver uninstalled -``` - -## Troubleshooting - -```text -I (3350) TWAI Alert and Recovery: Trigger errors -``` - -If the example does not progress pass triggering errors, check that the target is correctly connected to the transceiver. - -```text -I (3350) TWAI Alert and Recovery: Trigger errors -I (3650) TWAI Alert and Recovery: Surpassed Error Warning Limit -I (3650) TWAI Alert and Recovery: Entered Error Passive state -``` - -If the example is able to trigger errors but does not enter the bus off state (i.e., stays in the error passive state), check that the triggering of the bit error is properly set to the examples operating bit rate. By default, the example runs at a bit rate of 25kbits/sec, and the bit error should be triggered after the arbitration phase of each transmitted message. - -## Example Breakdown - -The TWAI Alert and Recovery Example will do the following... - -1. Initialize the TWAI driver in No Acknowledgement mode (so that another node is not required). -2. Create a transmit task to handle message transmission, and a control task to handle alerts. -3. Control task starts the TWAI driver, then reconfigures the alerts to trigger when the error passive or bus off state is entered. The control task then waits for those alerts. -4. The transmit repeatedly transmits single shot messages (i.e., message won't be retried if an error occurs). -5. When a message is being transmitted, the transmit task will purposely invert the TX pin to trigger a bit error. **Note that the triggering of the bit error is timed to occur after the arbitration phase of the transmitted message**. -6. The triggering of a bit error on each transmitted message eventually puts the TWAI driver into the Bus-Off state. -7. Control tasks detects the Bus-Off state via an alert, and triggers the Bus-Off recovery process after a short delay. Alerts are also reconfigured to trigger on the completion of Bus-Off recovery. -8. Once the Bus-Off recovery completion alert is detected by the control task, the TWAI driver is stopped and uninstalled. diff --git a/examples/peripherals/twai/twai_alert_and_recovery/main/CMakeLists.txt b/examples/peripherals/twai/twai_alert_and_recovery/main/CMakeLists.txt deleted file mode 100644 index b800800bf1..0000000000 --- a/examples/peripherals/twai/twai_alert_and_recovery/main/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "twai_alert_and_recovery_example_main.c" - REQUIRES driver - INCLUDE_DIRS ".") diff --git a/examples/peripherals/twai/twai_alert_and_recovery/main/Kconfig.projbuild b/examples/peripherals/twai/twai_alert_and_recovery/main/Kconfig.projbuild deleted file mode 100644 index 1688672cec..0000000000 --- a/examples/peripherals/twai/twai_alert_and_recovery/main/Kconfig.projbuild +++ /dev/null @@ -1,23 +0,0 @@ -menu "Example Configuration" - - orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps" - - config EXAMPLE_TX_GPIO_NUM - int "TX GPIO number" - range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX - default 21 if IDF_TARGET_ESP32 - default 0 - help - This option selects the GPIO pin used for the TX signal. Connect the - TX signal to your transceiver. - - config EXAMPLE_RX_GPIO_NUM - int "RX GPIO number" - range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX - default 22 if IDF_TARGET_ESP32 - default 2 - help - This option selects the GPIO pin used for the RX signal. Connect the - RX signal to your transceiver. - -endmenu diff --git a/examples/peripherals/twai/twai_alert_and_recovery/main/twai_alert_and_recovery_example_main.c b/examples/peripherals/twai/twai_alert_and_recovery/main/twai_alert_and_recovery_example_main.c deleted file mode 100644 index cfd6610a11..0000000000 --- a/examples/peripherals/twai/twai_alert_and_recovery/main/twai_alert_and_recovery_example_main.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2010-2025 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -/* - * The following example demonstrates how to use the alert and bus recovery - * features of the TWAI driver. The example will do the following: - * 1) Install and start the TWAI driver - * 2) Have the TX task periodically broadcast messages expecting no ACK - * 3) Reconfigure alerts to detect bus-off state - * 4) Trigger bus errors by inverting TX GPIO - * 5) Initiate bus-off recovery and wait for completion - * 6) Uninstall TWAI driver - */ - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "esp_err.h" -#include "esp_log.h" -#include "driver/gpio.h" -#include "driver/twai.h" -#include "esp_rom_gpio.h" -#include "esp_rom_sys.h" -#include "soc/twai_periph.h" // For GPIO matrix signal index - -/* --------------------- Definitions and static variables ------------------ */ -//Example Configuration -#define TX_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM -#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM -#define TX_TASK_PRIO 9 -#define CTRL_TASK_PRIO 10 -#define ERR_DELAY_US 800 //Approximate time for arbitration phase at 25KBPS -#define ERR_PERIOD_US 80 //Approximate time for two bits at 25KBPS -#define EXAMPLE_TAG "TWAI Alert and Recovery" - -static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); -static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS(); -static const twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, TWAI_MODE_NO_ACK); - -static const twai_message_t tx_msg = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 0, // Not single shot - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = 0, - .data_length_code = 0, - .data = {0}, -}; - -static SemaphoreHandle_t tx_task_sem; -static SemaphoreHandle_t ctrl_task_sem; -static bool trigger_tx_error = false; - -/* --------------------------- Tasks and Functions -------------------------- */ -extern const twai_signal_conn_t twai_periph_signals[SOC_TWAI_CONTROLLER_NUM]; -static void invert_tx_bits(bool enable) -{ - if (enable) { - //Inverts output of TX to trigger errors - esp_rom_gpio_connect_out_signal(TX_GPIO_NUM, twai_periph_signals[0].tx_sig, true, false); - } else { - //Returns TX to default settings - esp_rom_gpio_connect_out_signal(TX_GPIO_NUM, twai_periph_signals[0].tx_sig, false, false); - } -} - -static void tx_task(void *arg) -{ - xSemaphoreTake(tx_task_sem, portMAX_DELAY); - while (1) { - if (twai_transmit(&tx_msg, 0) == ESP_ERR_INVALID_STATE) { - break; //Exit TX task when bus-off state is reached - } - if (trigger_tx_error) { - //Trigger a bit error in transmission by inverting GPIO - esp_rom_delay_us(ERR_DELAY_US); //Wait until arbitration phase is over - invert_tx_bits(true); //Trigger bit error for a few bits - esp_rom_delay_us(ERR_PERIOD_US); - invert_tx_bits(false); - } - vTaskDelay(pdMS_TO_TICKS(50)); - } - vTaskDelete(NULL); -} - -static void ctrl_task(void *arg) -{ - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - ESP_ERROR_CHECK(twai_start()); - ESP_LOGI(EXAMPLE_TAG, "Driver started"); - ESP_LOGI(EXAMPLE_TAG, "Starting transmissions"); - xSemaphoreGive(tx_task_sem); //Start transmit task - - //Prepare to trigger errors, reconfigure alerts to detect change in error state - twai_reconfigure_alerts(TWAI_ALERT_ABOVE_ERR_WARN | TWAI_ALERT_ERR_PASS | TWAI_ALERT_BUS_OFF, NULL); - for (int i = 3; i > 0; i--) { - ESP_LOGW(EXAMPLE_TAG, "Trigger TX errors in %d", i); - vTaskDelay(pdMS_TO_TICKS(1000)); - } - ESP_LOGI(EXAMPLE_TAG, "Trigger errors"); - trigger_tx_error = true; - - while (1) { - uint32_t alerts; - twai_read_alerts(&alerts, portMAX_DELAY); - if (alerts & TWAI_ALERT_ABOVE_ERR_WARN) { - ESP_LOGI(EXAMPLE_TAG, "Surpassed Error Warning Limit"); - } - if (alerts & TWAI_ALERT_ERR_PASS) { - ESP_LOGI(EXAMPLE_TAG, "Entered Error Passive state"); - } - if (alerts & TWAI_ALERT_BUS_OFF) { - ESP_LOGI(EXAMPLE_TAG, "Bus Off state"); - //Prepare to initiate bus recovery, reconfigure alerts to detect bus recovery completion - twai_reconfigure_alerts(TWAI_ALERT_BUS_RECOVERED, NULL); - for (int i = 3; i > 0; i--) { - ESP_LOGW(EXAMPLE_TAG, "Initiate bus recovery in %d", i); - vTaskDelay(pdMS_TO_TICKS(1000)); - } - twai_initiate_recovery(); //Needs 128 occurrences of bus free signal - ESP_LOGI(EXAMPLE_TAG, "Initiate bus recovery"); - } - if (alerts & TWAI_ALERT_BUS_RECOVERED) { - //Bus recovery was successful, exit control task to uninstall driver - ESP_LOGI(EXAMPLE_TAG, "Bus Recovered"); - break; - } - } - //No need call twai_stop(), bus recovery will return to stopped state - xSemaphoreGive(ctrl_task_sem); - vTaskDelete(NULL); -} - -void app_main(void) -{ - tx_task_sem = xSemaphoreCreateBinary(); - ctrl_task_sem = xSemaphoreCreateBinary(); - - xTaskCreatePinnedToCore(tx_task, "TWAI_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY); - xTaskCreatePinnedToCore(ctrl_task, "TWAI_ctrl", 4096, NULL, CTRL_TASK_PRIO, NULL, tskNO_AFFINITY); - - //Install TWAI driver - ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, & f_config)); - ESP_LOGI(EXAMPLE_TAG, "Driver installed"); - - xSemaphoreGive(ctrl_task_sem); //Start control task - vTaskDelay(pdMS_TO_TICKS(100)); - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); //Wait for completion - - //Uninstall TWAI driver - ESP_ERROR_CHECK(twai_driver_uninstall()); - ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled"); - - //Cleanup - vSemaphoreDelete(tx_task_sem); - vSemaphoreDelete(ctrl_task_sem); -} diff --git a/examples/peripherals/twai/twai_alert_and_recovery/pytest_twai_alert_recovery_example.py b/examples/peripherals/twai/twai_alert_and_recovery/pytest_twai_alert_recovery_example.py deleted file mode 100644 index e86b71c064..0000000000 --- a/examples/peripherals/twai/twai_alert_and_recovery/pytest_twai_alert_recovery_example.py +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD -# SPDX-License-Identifier: CC0-1.0 -import pytest -from pytest_embedded import Dut -from pytest_embedded_idf.utils import idf_parametrize - - -@pytest.mark.twai_transceiver -@idf_parametrize('target', ['esp32'], indirect=['target']) -def test_twai_alert_recovery_example(dut: Dut) -> None: - dut.expect_exact('TWAI Alert and Recovery: Driver installed') - dut.expect_exact('TWAI Alert and Recovery: Driver uninstalled') diff --git a/examples/peripherals/twai/twai_self_test/CMakeLists.txt b/examples/peripherals/twai/twai_error_recovery/CMakeLists.txt similarity index 91% rename from examples/peripherals/twai/twai_self_test/CMakeLists.txt rename to examples/peripherals/twai/twai_error_recovery/CMakeLists.txt index 1eb3683c9c..7c3f931b0c 100644 --- a/examples/peripherals/twai/twai_self_test/CMakeLists.txt +++ b/examples/peripherals/twai/twai_error_recovery/CMakeLists.txt @@ -5,4 +5,5 @@ cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) # "Trim" the build. Include the minimal set of components, main, and anything it depends on. idf_build_set_property(MINIMAL_BUILD ON) -project(twai_self_test_example) + +project(twai_recovery_example) diff --git a/examples/peripherals/twai/twai_error_recovery/README.md b/examples/peripherals/twai/twai_error_recovery/README.md new file mode 100644 index 0000000000..c3f0b547b8 --- /dev/null +++ b/examples/peripherals/twai/twai_error_recovery/README.md @@ -0,0 +1,84 @@ +| Supported Targets | ESP32 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | + +# TWAI Bus-Off Recovery Example +This example demonstrates how to recover a TWAI node from a Bus-Off error condition and resume communication. The recovery is triggered by physically inducing bus errors and handled using the ESP TWAI on-chip driver with callback support. + +## How It Works +1. Initialize and start the TWAI node using esp_twai_onchip. +2. Transmit a few TWAI frames in normal operation. +3. **Manually** trigger bit errors by either: + - Briefly disconnecting the TX or RX connection + - Briefly short-circuiting the TWAI H and TWAI L lines +4. When the node enters the Bus-Off state, an error recovery process will automatically initiated. +5. Once recovered, resume normal transmission. + +## Hardware Requirements +- An ESP32xx chip with TWAI peripheral support +- External TWAI transceiver (e.g., TJA1050, MCP2551, or similar) +- TWAI bus setup with proper termination resistors + +## Pin Configuration +Modify TX_GPIO_NUM and RX_GPIO_NUM in the `twai_recovery_main.c` if needed. Connect these pins to your TWAI transceiver's TX and RX pins respectively. +```c +#define TX_GPIO_NUM GPIO_NUM_4 +#define RX_GPIO_NUM GPIO_NUM_5 +``` + +## Manual Error Triggering +To trigger bus errors and test the recovery mechanism, you can use either of these methods: + +1. **Connection Disruption**: Briefly disconnect the TX or RX wire between the ESP32 and the TWAI transceiver while frames are being transmitted. + +2. **Bus Short Circuit**: Briefly short-circuit the TWAI H and TWAI L lines on the bus. This simulates a bus fault condition. + +⚠️ **Important**: The disconnection or short-circuit should be brief enough to trigger bus error messages in the log output. Too long may cause permanent damage to the transceiver or affect other nodes on the bus. + +## Build and Flash +```sh +idf.py set-target esp32 build +idf.py -p PORT flash monitor +``` + +## Example Output +``` +install twai success +node started + +sending frame 0 +sending frame 1 + +... +sending frame 4 +// Manually trigger error here (disconnect TX/RX or short H/L) +bus error: 0x2 +state changed: error_active -> error_warning + +... +sending frame 9 +// Trigger error again +bus error: 0x2 +state changed: error_passive -> bus_off + +node offline, start recover ... +waiting ... 0 +... +state changed: bus_off -> error_active +node recovered! continue + +sending frame 0 +``` + +## Troubleshooting + +If the example doesn't enter the `error_warning`/`error_passive`/`bus_off` states after manual error triggering: + +1. **Check Hardware Connections**: Ensure the TWAI transceiver is properly connected and powered. +2. **Verify Bus Termination**: Make sure the TWAI bus has proper 120Ω termination resistors at both ends. +3. **Timing of Error Injection**: The manual error should be triggered while frames are actively being transmitted. +4. **Transceiver Type**: Different TWAI transceivers may have different fault detection sensitivities. + +If errors are not being detected: +- Try holding the disconnection/short-circuit for a slightly longer duration +- Ensure the error is introduced during active frame transmission +- Check that the TWAI H and TWAI L lines are properly connected to the transceiver diff --git a/examples/peripherals/twai/twai_error_recovery/main/CMakeLists.txt b/examples/peripherals/twai/twai_error_recovery/main/CMakeLists.txt new file mode 100644 index 0000000000..e3e15a7333 --- /dev/null +++ b/examples/peripherals/twai/twai_error_recovery/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "twai_recovery_main.c" + REQUIRES esp_driver_twai + INCLUDE_DIRS "." +) diff --git a/examples/peripherals/twai/twai_error_recovery/main/twai_recovery_main.c b/examples/peripherals/twai/twai_error_recovery/main/twai_recovery_main.c new file mode 100644 index 0000000000..88ece2aa76 --- /dev/null +++ b/examples/peripherals/twai/twai_error_recovery/main/twai_recovery_main.c @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief This example shows how to recover twai node from bus_off and continue communication + * + * 1) Install and start the TWAI driver + * 2) Sending some frames + * 3) Manually trigger bit_error by disconnecting the TX / RX or short-circuiting the H & L + * 4) Initiate bus-off recovery and wait for completion through `twai_node_get_info` + * 5) Back to step (2) + */ + +#include "esp_twai.h" +#include "esp_log.h" +#include "esp_twai_onchip.h" +#include "freertos/FreeRTOS.h" + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////// Please update the following configuration according to your HardWare spec ///////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define TX_GPIO_NUM GPIO_NUM_4 +#define RX_GPIO_NUM GPIO_NUM_5 // Using same pin to test without transceiver +#define TWAI_BITRATE 100000 // use low bitrate to ensure soft trigger error is effective + +static const char *TAG = "twai_recovery"; + +// bus errors +static bool example_error_cb(twai_node_handle_t handle, const twai_error_event_data_t *edata, void *user_ctx) +{ + ESP_EARLY_LOGI(TAG, "bus error: 0x%lx", edata->err_flags.val); + + return false; +} + +// node state +static bool example_state_change_cb(twai_node_handle_t handle, const twai_state_change_event_data_t *edata, void *user_ctx) +{ + const char *twai_state_name[] = {"error_active", "error_warning", "error_passive", "bus_off"}; + ESP_EARLY_LOGI(TAG, "state changed: %s -> %s", twai_state_name[edata->old_sta], twai_state_name[edata->new_sta]); + + return false; +} + +void app_main(void) +{ + twai_node_handle_t node_hdl; + twai_onchip_node_config_t node_config = { + .io_cfg.tx = TX_GPIO_NUM, + .io_cfg.rx = RX_GPIO_NUM, + .bit_timing.bitrate = TWAI_BITRATE, + .tx_queue_depth = 1, + .flags.enable_self_test = true, + }; + ESP_ERROR_CHECK(twai_new_node_onchip(&node_config, &node_hdl)); + ESP_LOGI(TAG, "install twai success, bitrate: %ld, bittime: %d us", node_config.bit_timing.bitrate, (1000000 / TWAI_BITRATE)); + + // register callbacks + twai_event_callbacks_t user_cbs = { + .on_error = example_error_cb, + .on_state_change = example_state_change_cb, + }; + ESP_ERROR_CHECK(twai_node_register_event_callbacks(node_hdl, &user_cbs, NULL)); + ESP_ERROR_CHECK(twai_node_enable(node_hdl)); + ESP_LOGI(TAG, "node started"); + + twai_frame_t tx_frame = { + .buffer = (uint8_t *)"hello\n", + .buffer_len = 6, + }; + for (uint8_t i = 0; i < 100; i++) { + twai_node_status_t node_status; + twai_node_get_info(node_hdl, &node_status, NULL); + if (node_status.state == TWAI_ERROR_BUS_OFF) { + i = 0; + ESP_LOGI(TAG, "node offline, start recover ..."); + ESP_ERROR_CHECK(twai_node_recover(node_hdl)); + for (uint8_t i = 0; i < 100; i++) { + ESP_LOGI(TAG, "waiting ... %d", i); + vTaskDelay(pdMS_TO_TICKS(1000)); + twai_node_get_info(node_hdl, &node_status, NULL); + + if (node_status.state == TWAI_ERROR_ACTIVE) { + ESP_LOGI(TAG, "node recovered! continue"); + break; + } + } + } else { + ESP_LOGI(TAG, "sending frame %d", i); + tx_frame.header.id = i; + ESP_ERROR_CHECK(twai_node_transmit(node_hdl, &tx_frame, 500)); + } + + vTaskDelay(pdMS_TO_TICKS(500)); + } + + ESP_ERROR_CHECK(twai_node_disable(node_hdl)); + ESP_ERROR_CHECK(twai_node_delete(node_hdl)); +} diff --git a/examples/peripherals/twai/twai_network/README.md b/examples/peripherals/twai/twai_network/README.md index 9d5878a880..a0c7f8a64f 100644 --- a/examples/peripherals/twai/twai_network/README.md +++ b/examples/peripherals/twai/twai_network/README.md @@ -1,189 +1,163 @@ -| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | # TWAI Network Example -(See the README.md file in the upper level 'examples' directory for more information about examples.) +This example demonstrates TWAI (Two-Wire Automotive Interface) network communication using the ESP-IDF TWAI driver. It consists of two programs that showcase different aspects of TWAI bus communication. -This example demonstrates how to use the TWAI driver to program a target (ESP32, ESP32-S2, ESP32-S3 or ESP32-C3) as a TWAI node, and have the two nodes (Network Master and Network Slave) communicate on a TWAI network. The Listen Only node is optional and acts as a network monitor meaning that it only receives messages and does not influence the bus in any way (i.e. doesn't not acknowledge or send error frames). +## Overview -Note that concept of master/slave in this example refers to which node initiates -and stops the transfer of a stream of data messages. +### Programs -## How to use example +- **twai_sender**: Sends periodic heartbeat messages and large data packets +- **twai_listen_only**: Monitors specific message ID using a filter -### Hardware Required +### Key Features -This example requires at least two targets (e.g., an ESP32 or ESP32-S2) to act as the Network Master and Network Slave. The third target (Listen Only) is optional. Each target must be connected to an external transceiver (e.g., a SN65HVD23X transceiver). The transceivers must then be interconnected to form a TWAI network. +- Event-driven message handling with callbacks +- Message filtering using acceptance filters in listen-only mode +- Single/Burst data transmission and reception +- Real-time bus error and node status reporting -The following diagram illustrates an example network: +## Hardware Setup -```text - ---------- ---------- -------------- - | Master | | Slave | | Listen Only | - | | | | | | - | TX RX | | TX RX | | TX RX | - ---------- ---------- -------------- - | | | | | | - | | | | | | - ---------- ---------- ---------- - | D R | | D R | | D R | - | | | | | | - | VP230 | | VP230 | | VP230 | - | | | | | | - | H L | | H L | | H L | - ---------- ---------- ---------- - | | | | | | - | | | | | | - |--x------|-----x------|-----x------|--| H - | | | - |---------x------------x------------x--| L +### Wiring -``` +For multi-device testing: +1. Connect TWAI transceivers to each ESP32xx +2. Wire TWAI_H and TWAI_L between all devices using line topology +3. Add 120Ω termination resistors at both ends of the bus -Note: If you don't have an external transceiver, you can still run the [TWAI Self Test example](../twai_self_test/README.md) +For single-device testing, enable self_test mode in the sender. -### Configure the project +### GPIO Configuration -For each node in the TWAI network (i.e., Master, Slave, Listen Only)... +The GPIO pins can be configured using menuconfig: -* Set the target of the build (where `{IDF_TARGET}` stands for the target chip such as `esp32` or `esp32s2`). -* Then run `menuconfig` to configure the example. - -```sh -idf.py set-target {IDF_TARGET} +```bash idf.py menuconfig ``` -* Under `Example Configuration`, configure the pin assignments using the options `TX GPIO Number` and `RX GPIO Number` according to how the target was connected to the transceiver. By default, `TX GPIO Number` and `RX GPIO Number` are set to the following values: - * On the ESP32, `TX GPIO Number` and `RX GPIO Number` default to `21` and `22` respectively - * On other chips, `TX GPIO Number` and `RX GPIO Number` default to `0` and `2` respectively +Navigate to: `Example Configuration` → Configure the following: +- **TWAI TX GPIO Num**: GPIO pin for TWAI TX +- **TWAI RX GPIO Num**: GPIO pin for TWAI RX -### Build and Flash - -For each node, build the project and flash it to the board, then run monitor tool to view serial output: - -```sh -idf.py -p PORT flash monitor +**Default configuration:** +```c +#define TWAI_TX_GPIO GPIO_NUM_4 +#define TWAI_RX_GPIO GPIO_NUM_5 +#define TWAI_BITRATE 1000000 // 1 Mbps ``` -(Replace PORT with the name of the serial port to use.) +## Message Types -(To exit the serial monitor, type ``Ctrl-]``.) +| ID | Type | Frequency | Size | Description | +|----|------|-----------|------|-------------| +| 0x7FF | Heartbeat | 1 Hz | 8 bytes | Timestamp data | +| 0x100 | Data | Every 10s | 1000 bytes | Test data (125 frames) | -See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. +## Building and Running -## Example Output +### Build each program (sender, listen_only): -Network Master +**Enter the sub-application directory and run:** -```text -I (345) TWAI Master: Driver installed -I (345) TWAI Master: Driver started -I (345) TWAI Master: Transmitting ping -I (3105) TWAI Master: Transmitted start command -I (3105) TWAI Master: Received data value 339 -... -I (5545) TWAI Master: Received data value 584 -I (5545) TWAI Master: Transmitted stop command -I (5595) TWAI Master: Driver stopped -I (6595) TWAI Master: Driver started -I (6595) TWAI Master: Transmitting ping -I (7095) TWAI Master: Transmitted start command -I (7095) TWAI Master: Received data value 738 -... -I (9535) TWAI Master: Received data value 983 -I (9535) TWAI Master: Transmitted stop command -I (9585) TWAI Master: Driver stopped -I (10585) TWAI Master: Driver started -I (10585) TWAI Master: Transmitting ping -I (11085) TWAI Master: Transmitted start command -I (11085) TWAI Master: Received data value 1137 -... -I (13525) TWAI Master: Received data value 1382 -I (13525) TWAI Master: Transmitted stop command -I (13575) TWAI Master: Driver stopped -I (14575) TWAI Master: Driver uninstalled +```bash +idf.py set-target esp32 build flash monitor ``` -Network Slave +## Expected Output -```text -Slave starting in 3 -Slave starting in 2 -Slave starting in 1 -I (6322) TWAI Slave: Driver installed -I (6322) TWAI Slave: Driver started -I (6462) TWAI Slave: Transmitted ping response -I (6712) TWAI Slave: Start transmitting data -I (6712) TWAI Slave: Transmitted data value 339 -... -I (9162) TWAI Slave: Transmitted data value 584 -I (9212) TWAI Slave: Transmitted stop response -I (9312) TWAI Slave: Driver stopped -I (10312) TWAI Slave: Driver started -I (10452) TWAI Slave: Transmitted ping response -I (10702) TWAI Slave: Start transmitting data -I (10702) TWAI Slave: Transmitted data value 738 -... -I (13152) TWAI Slave: Transmitted data value 983 -I (13202) TWAI Slave: Transmitted stop response -I (13302) TWAI Slave: Driver stopped -I (14302) TWAI Slave: Driver started -I (14442) TWAI Slave: Transmitted ping response -I (14692) TWAI Slave: Start transmitting data -I (14692) TWAI Slave: Transmitted data value 1137 -... -I (17142) TWAI Slave: Transmitted data value 1382 -I (17192) TWAI Slave: Transmitted stop response -I (17292) TWAI Slave: Driver stopped -I (18292) TWAI Slave: Driver uninstalled +### Sender +``` +===================TWAI Sender Example Starting...=================== +I (xxx) twai_sender: TWAI Sender started successfully +I (xxx) twai_sender: Sending messages on IDs: 0x100 (data), 0x7FF (heartbeat) +I (xxx) twai_sender: Sending heartbeat message: 1234567890 +I (xxx) twai_sender: Sending packet of 1000 bytes in 125 frames ``` -Network Listen Only - -```text -I (326) TWAI Listen Only: Driver installed -I (326) TWAI Listen Only: Driver started -I (366) TWAI Listen Only: Received master ping -... -I (1866) TWAI Listen Only: Received master ping -I (1866) TWAI Listen Only: Received slave ping response -I (2116) TWAI Listen Only: Received master start command -I (2116) TWAI Listen Only: Received data value 329 -... -I (4566) TWAI Listen Only: Received data value 574 -I (4566) TWAI Listen Only: Received master stop command -I (4606) TWAI Listen Only: Received slave stop response -I (5606) TWAI Listen Only: Received master ping -I (5856) TWAI Listen Only: Received master ping -I (5856) TWAI Listen Only: Received slave ping response -I (6106) TWAI Listen Only: Received master start command -I (6106) TWAI Listen Only: Received data value 728 -... -I (8556) TWAI Listen Only: Received data value 973 -I (8556) TWAI Listen Only: Received master stop command -I (8596) TWAI Listen Only: Received slave stop response -I (9596) TWAI Listen Only: Received master ping -I (9846) TWAI Listen Only: Received master ping -I (9846) TWAI Listen Only: Received slave ping response -I (10096) TWAI Listen Only: Received master start command -I (10096) TWAI Listen Only: Received data value 1127 -... -I (12546) TWAI Listen Only: Received data value 1372 -I (12546) TWAI Listen Only: Received master stop command -I (12586) TWAI Listen Only: Received slave stop response -I (12586) TWAI Listen Only: Driver stopped -I (12586) TWAI Listen Only: Driver uninstalled - +### Listen-Only Monitor +``` +===================TWAI Listen Only Example Starting...=================== +I (xxx) twai_listen: Buffer initialized: 200 slots for burst data +I (xxx) twai_listen: TWAI node created +I (xxx) twai_listen: Filter enabled for ID: 0x100 Mask: 0x7F0 +I (xxx) twai_listen: TWAI start listening... +I (xxx) twai_listen: RX: 100 [8] 0 0 0 0 0 0 0 0 +I (xxx) twai_listen: RX: 100 [8] 1 1 1 1 1 1 1 1 ``` -## Example Breakdown +## Implementation Details -The communication between the Network Master and Network Slave execute the following steps over multiple iterations: +### Message Buffering -1. Both master and slave go through install and start their TWAI drivers independently. -2. The master repeatedly sends **PING** messages until it receives a **PING_RESP** (ping response message) from the slave. The slave will only send a **PING_RESP** message when it receives a **PING** message from the master. -3. Once the master has received the **PING_RESP** from the slave, it will send a **START_CMD** message to the slave. -4. Upon receiving the **START_CMD** message, the slave will start transmitting **DATA** messages until the master sends a **STOP_CMD**. The master will send the **STOP_CMD** after receiving N **DATA** messages from the slave (N = 50 by default). -5. When the slave receives the **STOP_CMD**, it will confirm that it has stopped by sending a **STOP_RESP** message to the master. +Each program uses a buffer pool to handle incoming messages efficiently: + +- **Sender**: Small buffer for transmission completion tracking +- **Listen-Only**: 100-slot buffer for monitoring filtered traffic + +### Operating Modes + +- **Normal Mode** (Sender): Participates in bus communication, sends ACK frames +- **Listen-Only Mode** (Monitor): Receives filtered messages without transmitting anything + +### Message Filtering + +The listen-only monitor uses hardware acceptance filters to receive only specific message IDs: + +```c +twai_mask_filter_config_t data_filter = { + .id = TWAI_DATA_ID, + .mask = 0x7F0, // Match high 7 bits of the ID, ignore low 4 bits + .is_ext = false, // Receive only standard ID +}; +``` + +### Error Handling + +- Bus error logging and status monitoring + +## Configuration + +### Customizing Buffer Sizes + +Adjust buffer sizes in each program as needed: +```c +#define POLL_DEPTH 200 // Listen-only buffer size +``` + +### Changing Message IDs + +Update the message ID definitions: +```c +#define TWAI_DATA_ID 0x100 +#define TWAI_HEARTBEAT_ID 0x7FF +``` + +## Use Cases + +This example is suitable for: + +- Learning TWAI bus communication +- Testing TWAI network setups +- Developing custom TWAI protocols +- Bus monitoring and debugging +- Prototyping automotive communication systems + +## Troubleshooting + +### No Communication +- Check GPIO pin connections +- Verify bitrate settings match between devices +- Ensure proper bus termination + +### Buffer Overflows +- Increase buffer size (`POLL_DEPTH`) +- Reduce bus message transmission rate +- Optimize message processing code + +### Bus Errors +- Check physical bus wiring +- Verify termination resistors (120Ω at each end) +- Monitor error counters with `twai_node_get_info()` diff --git a/examples/peripherals/twai/twai_network/pytest_twai_network.py b/examples/peripherals/twai/twai_network/pytest_twai_network.py new file mode 100644 index 0000000000..ea0cfb56a5 --- /dev/null +++ b/examples/peripherals/twai/twai_network/pytest_twai_network.py @@ -0,0 +1,107 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import os.path +import subprocess + +import pytest +from can import Bus +from can import Message +from pytest_embedded_idf import IdfDut +from pytest_embedded_idf.utils import soc_filtered_targets + +TWAI_SUPPORTED_TARGETS = soc_filtered_targets('SOC_TWAI_SUPPORTED == 1') + + +# Socket CAN fixture +@pytest.fixture(name='socket_can') +def fixture_create_socket_can() -> Bus: + start_command = 'sudo ip link set can0 up type can bitrate 1000000' + stop_command = 'sudo ip link set can0 down' + try: + subprocess.run(start_command, shell=True, capture_output=True, text=True) + except Exception as e: + print(f'Open bus Error: {e}') + bus = Bus(interface='socketcan', channel='can0', bitrate=1000000) + yield bus # test invoked here + bus.shutdown() + subprocess.run(stop_command, shell=True, capture_output=True, text=True) + + +# Generate minimal combinations that each target appears in each app +def generate_target_combinations(target_list: list, count: int = 2) -> list: + combinations = [] + num_targets = len(target_list) + for round_num in range(num_targets): + selected_targets = [target_list[(round_num + i) % num_targets] for i in range(count)] + combinations.append('|'.join(selected_targets)) + + return combinations + + +@pytest.mark.twai_std +@pytest.mark.parametrize('count', [2], indirect=True) +@pytest.mark.timeout(120) +@pytest.mark.parametrize( + 'app_path,target', + [ + ( + f'{os.path.join(os.path.dirname(__file__), "twai_listen_only")}|' + f'{os.path.join(os.path.dirname(__file__), "twai_sender")}', + target_combo, + ) + for target_combo in generate_target_combinations(TWAI_SUPPORTED_TARGETS) + ], + indirect=True, +) +@pytest.mark.temp_skip_ci( + targets=['esp32c5,esp32'], reason="C5 twai don't support listen only in network test, see errata issue 5" +) +def test_twai_network_multi(dut: tuple[IdfDut, IdfDut], socket_can: Bus) -> None: + """ + Test TWAI network communication between two nodes: + - dut[0]: listener (first chip) - uses twai_listen_only + - dut[1]: sender (second chip) - uses twai_sender + """ + + # Print chip information for debugging + print(f'===> Pytest testing with chips: {dut[0].app.target} (listener), {dut[1].app.target} (sender)') + + # Initialize listener node first + dut[0].expect('===================TWAI Listen Only Example Starting...===================') + dut[0].expect('TWAI start listening...') + + # Initialize sender node and start communication + dut[1].expect('===================TWAI Sender Example Starting...===================') + dut[1].expect('TWAI Sender started successfully') + + # Verify communication is working + # Wait for sender to send messages + dut[1].expect('Sending heartbeat message:', timeout=10) + + # Check that listener is receiving data + dut[0].expect('RX:', timeout=15) # Listener should see filtered messages + + # Check if socket receive any messages + socket_rcv_cnt = 0 + for i in range(100): + msg = socket_can.recv(timeout=1) + if msg is not None: + socket_rcv_cnt += 1 + print(f'Socket receive {socket_rcv_cnt} messages') + assert socket_rcv_cnt > 50, 'Socket NO messages' + + # Wait a bit more to ensure stable communication + dut[1].expect('Sending packet of', timeout=10) + dut[0].expect('RX:', timeout=10) + + # Check if esp32 receive messages from usb can + message = Message( + arbitration_id=0x10A, + is_extended_id=False, + data=b'Hi ESP32', + ) + print('USB CAN Send:', message) + socket_can.send(message, timeout=0.2) + dut[0].expect_exact('RX: 10a [8] 48 69 20 45 53 50 33 32', timeout=10) # ASCII: Hi ESP32 + + print('===> TWAI network communication test completed successfully') diff --git a/examples/peripherals/twai/twai_network/pytest_twai_network_example.py b/examples/peripherals/twai/twai_network/pytest_twai_network_example.py deleted file mode 100644 index 5f7042e940..0000000000 --- a/examples/peripherals/twai/twai_network/pytest_twai_network_example.py +++ /dev/null @@ -1,90 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD -# SPDX-License-Identifier: CC0-1.0 -import os.path -from threading import Thread -from typing import Tuple - -import pytest -from pytest_embedded import Dut - -# Define tuple of strings to expect for each DUT. -master_expect = ('TWAI Master: Driver installed', 'TWAI Master: Driver uninstalled') -slave_expect = ('TWAI Slave: Driver installed', 'TWAI Slave: Driver uninstalled') -listen_only_expect = ( - 'TWAI Listen Only: Driver installed', - 'TWAI Listen Only: Driver uninstalled', -) - - -def dut_thread_callback(**kwargs) -> None: # type: ignore - # Parse keyword arguments - dut = kwargs['dut'] # Get DUT from kwargs - expected = kwargs['expected'] - result = kwargs[ - 'result' - ] # Get result[out] from kwargs. MUST be of mutable type e.g. list - - # Must reset again as flashing during start_app will reset multiple times, causing unexpected results - dut.reset() - - for string in expected: - dut.expect(string, 20) - - # Mark thread has run to completion without any exceptions - result[0] = True - - -@pytest.mark.skip(reason="there's not a good approach to sync multiple DUTs") -@pytest.mark.twai_network -@pytest.mark.parametrize( - 'count, app_path', - [ - ( - 3, - f'{os.path.join(os.path.dirname(__file__), "twai_network_master")}|' - f'{os.path.join(os.path.dirname(__file__), "twai_network_slave")}|' - f'{os.path.join(os.path.dirname(__file__), "twai_network_listen_only")}', - ), - ], - indirect=True, -) -def test_twai_network_example(dut: Tuple[Dut, Dut, Dut]) -> None: - dut_master = dut[0] - dut_slave = dut[1] - dut_listen_only = dut[2] - - # Create dict of keyword arguments for each dut - results = [[False], [False], [False]] - master_kwargs = {'dut': dut_master, 'result': results[0], 'expected': master_expect} - slave_kwargs = {'dut': dut_slave, 'result': results[1], 'expected': slave_expect} - listen_only_kwargs = { - 'dut': dut_listen_only, - 'result': results[2], - 'expected': listen_only_expect, - } - - # Create thread for each dut - dut_master_thread = Thread( - target=dut_thread_callback, name='Master Thread', kwargs=master_kwargs - ) - dut_slave_thread = Thread( - target=dut_thread_callback, name='Slave Thread', kwargs=slave_kwargs - ) - dut_listen_only_thread = Thread( - target=dut_thread_callback, name='Listen Only Thread', kwargs=listen_only_kwargs - ) - - # Start each thread - dut_listen_only_thread.start() - dut_master_thread.start() - dut_slave_thread.start() - - # Wait for threads to complete - dut_listen_only_thread.join() - dut_master_thread.join() - dut_slave_thread.join() - - # check each thread ran to completion - for result in results: - if result[0] is not True: - raise Exception('One or more threads did not run successfully') diff --git a/examples/peripherals/twai/twai_network/twai_network_slave/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_listen_only/CMakeLists.txt similarity index 92% rename from examples/peripherals/twai/twai_network/twai_network_slave/CMakeLists.txt rename to examples/peripherals/twai/twai_network/twai_listen_only/CMakeLists.txt index dd8dec0f26..5bb36d37d1 100644 --- a/examples/peripherals/twai/twai_network/twai_network_slave/CMakeLists.txt +++ b/examples/peripherals/twai/twai_network/twai_listen_only/CMakeLists.txt @@ -5,4 +5,4 @@ cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) # "Trim" the build. Include the minimal set of components, main, and anything it depends on. idf_build_set_property(MINIMAL_BUILD ON) -project(twai_network_slave) +project(twai_listen_only) diff --git a/examples/peripherals/twai/twai_network/twai_listen_only/main/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_listen_only/main/CMakeLists.txt new file mode 100644 index 0000000000..53533a4b72 --- /dev/null +++ b/examples/peripherals/twai/twai_network/twai_listen_only/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "twai_listen_only.c" + REQUIRES esp_driver_twai + INCLUDE_DIRS "." +) diff --git a/examples/peripherals/twai/twai_network/twai_listen_only/main/Kconfig.projbuild b/examples/peripherals/twai/twai_network/twai_listen_only/main/Kconfig.projbuild new file mode 100644 index 0000000000..395fdb7a34 --- /dev/null +++ b/examples/peripherals/twai/twai_network/twai_listen_only/main/Kconfig.projbuild @@ -0,0 +1,17 @@ +menu "Example Configuration" + + config EXAMPLE_TWAI_TX_GPIO + int "TWAI TX GPIO Num" + range 0 SOC_GPIO_OUT_RANGE_MAX + default 4 + help + GPIO number for TWAI TX to transceiver. + + config EXAMPLE_TWAI_RX_GPIO + int "TWAI RX GPIO Num" + range 0 SOC_GPIO_IN_RANGE_MAX + default 5 + help + GPIO number for TWAI RX from transceiver. + +endmenu diff --git a/examples/peripherals/twai/twai_network/twai_listen_only/main/twai_listen_only.c b/examples/peripherals/twai/twai_network/twai_listen_only/main/twai_listen_only.c new file mode 100644 index 0000000000..fa53395bbb --- /dev/null +++ b/examples/peripherals/twai/twai_network/twai_listen_only/main/twai_listen_only.c @@ -0,0 +1,146 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_twai.h" +#include "esp_twai_onchip.h" + +#define TWAI_LISTENER_TX_GPIO -1 // Listen only node doesn't need TX pin +#define TWAI_LISTENER_RX_GPIO CONFIG_EXAMPLE_TWAI_RX_GPIO +#define TWAI_BITRATE 1000000 + +// Message IDs (must match sender) +#define TWAI_DATA_ID 0x100 + +// Buffer for burst data handling +#define POLL_DEPTH 200 + +static const char *TAG = "twai_listen"; + +typedef struct { + twai_frame_t frame; + uint8_t data[TWAI_FRAME_MAX_LEN]; +} twai_listener_data_t; + +typedef struct { + twai_node_handle_t node_hdl; + twai_listener_data_t *rx_pool; + SemaphoreHandle_t free_pool_semaphore; + SemaphoreHandle_t rx_result_semaphore; + int write_idx; + int read_idx; +} twai_listener_ctx_t; + +// Error callback +static bool IRAM_ATTR twai_listener_on_error_callback(twai_node_handle_t handle, const twai_error_event_data_t *edata, void *user_ctx) +{ + ESP_EARLY_LOGW(TAG, "bus error: 0x%x", edata->err_flags.val); + return false; +} + +// Node state +static bool IRAM_ATTR twai_listener_on_state_change_callback(twai_node_handle_t handle, const twai_state_change_event_data_t *edata, void *user_ctx) +{ + const char *twai_state_name[] = {"error_active", "error_warning", "error_passive", "bus_off"}; + ESP_EARLY_LOGI(TAG, "state changed: %s -> %s", twai_state_name[edata->old_sta], twai_state_name[edata->new_sta]); + return false; +} + +// TWAI receive callback - store data and signal +static bool IRAM_ATTR twai_listener_rx_callback(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx) +{ + BaseType_t woken; + twai_listener_ctx_t *ctx = (twai_listener_ctx_t *)user_ctx; + + if (xSemaphoreTakeFromISR(ctx->free_pool_semaphore, &woken) != pdTRUE) { + ESP_EARLY_LOGI(TAG, "Pool full, dropping frame"); + return (woken == pdTRUE); + } + if (twai_node_receive_from_isr(handle, &ctx->rx_pool[ctx->write_idx].frame) == ESP_OK) { + ctx->write_idx = (ctx->write_idx + 1) % POLL_DEPTH; + xSemaphoreGiveFromISR(ctx->rx_result_semaphore, &woken); + } + return (woken == pdTRUE); +} + +void app_main(void) +{ + printf("===================TWAI Listen Only Example Starting...===================\n"); + + // Create semaphore for receive notification + twai_listener_ctx_t twai_listener_ctx = {0}; + twai_listener_ctx.free_pool_semaphore = xSemaphoreCreateCounting(POLL_DEPTH, POLL_DEPTH); + twai_listener_ctx.rx_result_semaphore = xSemaphoreCreateCounting(POLL_DEPTH, 0); + assert(twai_listener_ctx.free_pool_semaphore != NULL); + assert(twai_listener_ctx.rx_result_semaphore != NULL); + + twai_listener_ctx.rx_pool = calloc(POLL_DEPTH, sizeof(twai_listener_data_t)); + assert(twai_listener_ctx.rx_pool != NULL); + for (int i = 0; i < POLL_DEPTH; i++) { + twai_listener_ctx.rx_pool[i].frame.buffer = twai_listener_ctx.rx_pool[i].data; + twai_listener_ctx.rx_pool[i].frame.buffer_len = sizeof(twai_listener_ctx.rx_pool[i].data); + } + ESP_LOGI(TAG, "Buffer initialized: %d slots for burst data", POLL_DEPTH); + + // Configure TWAI node + twai_onchip_node_config_t node_config = { + .io_cfg = { + .tx = TWAI_LISTENER_TX_GPIO, + .rx = TWAI_LISTENER_RX_GPIO, + .quanta_clk_out = -1, + .bus_off_indicator = -1, + }, + .bit_timing.bitrate = TWAI_BITRATE, + .flags.enable_listen_only = true, + }; + + // Create TWAI node + ESP_ERROR_CHECK(twai_new_node_onchip(&node_config, &twai_listener_ctx.node_hdl)); + ESP_LOGI(TAG, "TWAI node created"); + + // Configure acceptance filter + twai_mask_filter_config_t data_filter = { + .id = TWAI_DATA_ID, + .mask = 0x7F0, // Match high 7 bits of the ID, ignore low 4 bits + .is_ext = false, // Receive only standard ID + }; + ESP_ERROR_CHECK(twai_node_config_mask_filter(twai_listener_ctx.node_hdl, 0, &data_filter)); + ESP_LOGI(TAG, "Filter enabled for ID: 0x%03X Mask: 0x%03X", data_filter.id, data_filter.mask); + + // Register callbacks + twai_event_callbacks_t callbacks = { + .on_rx_done = twai_listener_rx_callback, + .on_error = twai_listener_on_error_callback, + .on_state_change = twai_listener_on_state_change_callback, + }; + ESP_ERROR_CHECK(twai_node_register_event_callbacks(twai_listener_ctx.node_hdl, &callbacks, &twai_listener_ctx)); + + // Enable TWAI node + ESP_ERROR_CHECK(twai_node_enable(twai_listener_ctx.node_hdl)); + ESP_LOGI(TAG, "TWAI start listening..."); + + // Main loop - process all buffered data when signaled + while (1) { + if (xSemaphoreTake(twai_listener_ctx.rx_result_semaphore, portMAX_DELAY) == pdTRUE) { + twai_frame_t *frame = &twai_listener_ctx.rx_pool[twai_listener_ctx.read_idx].frame; + ESP_LOGI(TAG, "RX: %x [%d] %x %x %x %x %x %x %x %x", \ + frame->header.id, frame->header.dlc, frame->buffer[0], frame->buffer[1], frame->buffer[2], frame->buffer[3], frame->buffer[4], frame->buffer[5], frame->buffer[6], frame->buffer[7]); + twai_listener_ctx.read_idx = (twai_listener_ctx.read_idx + 1) % POLL_DEPTH; + xSemaphoreGive(twai_listener_ctx.free_pool_semaphore); + } + } + + // Cleanup + vSemaphoreDelete(twai_listener_ctx.rx_result_semaphore); + vSemaphoreDelete(twai_listener_ctx.free_pool_semaphore); + free(twai_listener_ctx.rx_pool); + ESP_ERROR_CHECK(twai_node_disable(twai_listener_ctx.node_hdl)); + ESP_ERROR_CHECK(twai_node_delete(twai_listener_ctx.node_hdl)); +} diff --git a/examples/peripherals/twai/twai_network/twai_network_listen_only/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_network_listen_only/CMakeLists.txt deleted file mode 100644 index f5b56fa7ff..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_listen_only/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# The following lines of boilerplate have to be in your project's CMakeLists -# in this exact order for cmake to work correctly -cmake_minimum_required(VERSION 3.16) - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -# "Trim" the build. Include the minimal set of components, main, and anything it depends on. -idf_build_set_property(MINIMAL_BUILD ON) -project(twai_network_listen_only) diff --git a/examples/peripherals/twai/twai_network/twai_network_listen_only/main/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_network_listen_only/main/CMakeLists.txt deleted file mode 100644 index 9e924d4b1a..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_listen_only/main/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "twai_network_example_listen_only_main.c" - REQUIRES driver - INCLUDE_DIRS ".") diff --git a/examples/peripherals/twai/twai_network/twai_network_listen_only/main/Kconfig.projbuild b/examples/peripherals/twai/twai_network/twai_network_listen_only/main/Kconfig.projbuild deleted file mode 100644 index 14a2c83965..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_listen_only/main/Kconfig.projbuild +++ /dev/null @@ -1,19 +0,0 @@ -menu "Example Configuration" - - config EXAMPLE_TX_GPIO_NUM - int "TX GPIO number" - default 21 if IDF_TARGET_ESP32 - default 0 - help - This option selects the GPIO pin used for the TX signal. Connect the - TX signal to your transceiver. - - config EXAMPLE_RX_GPIO_NUM - int "RX GPIO number" - default 22 if IDF_TARGET_ESP32 - default 2 - help - This option selects the GPIO pin used for the RX signal. Connect the - RX signal to your transceiver. - -endmenu diff --git a/examples/peripherals/twai/twai_network/twai_network_listen_only/main/twai_network_example_listen_only_main.c b/examples/peripherals/twai/twai_network/twai_network_listen_only/main/twai_network_example_listen_only_main.c deleted file mode 100644 index dcfd828740..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_listen_only/main/twai_network_example_listen_only_main.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2010-2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -/* - * The following example demonstrates a Listen Only node in a TWAI network. The - * Listen Only node will not take part in any TWAI bus activity (no acknowledgments - * and no error frames). This example will execute multiple iterations, with each - * iteration the Listen Only node will do the following: - * 1) Listen for ping and ping response - * 2) Listen for start command - * 3) Listen for data messages - * 4) Listen for stop and stop response - */ -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "freertos/semphr.h" -#include "esp_err.h" -#include "esp_log.h" -#include "driver/twai.h" - -/* --------------------- Definitions and static variables ------------------ */ -//Example Configuration -#define NO_OF_ITERS 3 -#define RX_TASK_PRIO 9 -#define TX_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM -#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM -#define EXAMPLE_TAG "TWAI Listen Only" - -#define ID_MASTER_STOP_CMD 0x0A0 -#define ID_MASTER_START_CMD 0x0A1 -#define ID_MASTER_PING 0x0A2 -#define ID_SLAVE_STOP_RESP 0x0B0 -#define ID_SLAVE_DATA 0x0B1 -#define ID_SLAVE_PING_RESP 0x0B2 - -static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); -static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS(); -//Set TX queue length to 0 due to listen only mode -static const twai_general_config_t g_config = {.mode = TWAI_MODE_LISTEN_ONLY, - .tx_io = TX_GPIO_NUM, .rx_io = RX_GPIO_NUM, - .clkout_io = TWAI_IO_UNUSED, .bus_off_io = TWAI_IO_UNUSED, - .tx_queue_len = 0, .rx_queue_len = 5, - .alerts_enabled = TWAI_ALERT_NONE, - .clkout_divider = 0 - }; - -static SemaphoreHandle_t rx_sem; - -/* --------------------------- Tasks and Functions -------------------------- */ - -static void twai_receive_task(void *arg) -{ - xSemaphoreTake(rx_sem, portMAX_DELAY); - bool start_cmd = false; - bool stop_resp = false; - uint32_t iterations = 0; - - while (iterations < NO_OF_ITERS) { - twai_message_t rx_msg; - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_MASTER_PING) { - ESP_LOGI(EXAMPLE_TAG, "Received master ping"); - } else if (rx_msg.identifier == ID_SLAVE_PING_RESP) { - ESP_LOGI(EXAMPLE_TAG, "Received slave ping response"); - } else if (rx_msg.identifier == ID_MASTER_START_CMD) { - ESP_LOGI(EXAMPLE_TAG, "Received master start command"); - start_cmd = true; - } else if (rx_msg.identifier == ID_SLAVE_DATA) { - uint32_t data = 0; - for (int i = 0; i < rx_msg.data_length_code; i++) { - data |= (rx_msg.data[i] << (i * 8)); - } - ESP_LOGI(EXAMPLE_TAG, "Received data value %"PRIu32, data); - } else if (rx_msg.identifier == ID_MASTER_STOP_CMD) { - ESP_LOGI(EXAMPLE_TAG, "Received master stop command"); - } else if (rx_msg.identifier == ID_SLAVE_STOP_RESP) { - ESP_LOGI(EXAMPLE_TAG, "Received slave stop response"); - stop_resp = true; - } - if (start_cmd && stop_resp) { - //Each iteration is complete after a start command and stop response is received - iterations++; - start_cmd = 0; - stop_resp = 0; - } - } - - xSemaphoreGive(rx_sem); - vTaskDelete(NULL); -} - -void app_main(void) -{ - rx_sem = xSemaphoreCreateBinary(); - xTaskCreatePinnedToCore(twai_receive_task, "TWAI_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY); - - //Install and start TWAI driver - ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config)); - ESP_LOGI(EXAMPLE_TAG, "Driver installed"); - ESP_ERROR_CHECK(twai_start()); - ESP_LOGI(EXAMPLE_TAG, "Driver started"); - - xSemaphoreGive(rx_sem); //Start RX task - vTaskDelay(pdMS_TO_TICKS(100)); - xSemaphoreTake(rx_sem, portMAX_DELAY); //Wait for RX task to complete - - //Stop and uninstall TWAI driver - ESP_ERROR_CHECK(twai_stop()); - ESP_LOGI(EXAMPLE_TAG, "Driver stopped"); - ESP_ERROR_CHECK(twai_driver_uninstall()); - ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled"); - - //Cleanup - vSemaphoreDelete(rx_sem); -} diff --git a/examples/peripherals/twai/twai_network/twai_network_master/main/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_network_master/main/CMakeLists.txt deleted file mode 100644 index 0fd5bdf859..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_master/main/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "twai_network_example_master_main.c" - REQUIRES driver - INCLUDE_DIRS ".") diff --git a/examples/peripherals/twai/twai_network/twai_network_master/main/Kconfig.projbuild b/examples/peripherals/twai/twai_network/twai_network_master/main/Kconfig.projbuild deleted file mode 100644 index 14a2c83965..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_master/main/Kconfig.projbuild +++ /dev/null @@ -1,19 +0,0 @@ -menu "Example Configuration" - - config EXAMPLE_TX_GPIO_NUM - int "TX GPIO number" - default 21 if IDF_TARGET_ESP32 - default 0 - help - This option selects the GPIO pin used for the TX signal. Connect the - TX signal to your transceiver. - - config EXAMPLE_RX_GPIO_NUM - int "RX GPIO number" - default 22 if IDF_TARGET_ESP32 - default 2 - help - This option selects the GPIO pin used for the RX signal. Connect the - RX signal to your transceiver. - -endmenu diff --git a/examples/peripherals/twai/twai_network/twai_network_master/main/twai_network_example_master_main.c b/examples/peripherals/twai/twai_network/twai_network_master/main/twai_network_example_master_main.c deleted file mode 100644 index cc56a0b2cd..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_master/main/twai_network_example_master_main.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2010-2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -/* - * The following example demonstrates a master node in a TWAI network. The master - * node is responsible for initiating and stopping the transfer of data messages. - * The example will execute multiple iterations, with each iteration the master - * node will do the following: - * 1) Start the TWAI driver - * 2) Repeatedly send ping messages until a ping response from slave is received - * 3) Send start command to slave and receive data messages from slave - * 4) Send stop command to slave and wait for stop response from slave - * 5) Stop the TWAI driver - */ -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "freertos/semphr.h" -#include "esp_err.h" -#include "esp_log.h" -#include "driver/twai.h" - -/* --------------------- Definitions and static variables ------------------ */ -//Example Configuration -#define PING_PERIOD_MS 250 -#define NO_OF_DATA_MSGS 50 -#define NO_OF_ITERS 3 -#define ITER_DELAY_MS 1000 -#define RX_TASK_PRIO 8 -#define TX_TASK_PRIO 9 -#define CTRL_TSK_PRIO 10 -#define TX_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM -#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM -#define EXAMPLE_TAG "TWAI Master" - -#define ID_MASTER_STOP_CMD 0x0A0 -#define ID_MASTER_START_CMD 0x0A1 -#define ID_MASTER_PING 0x0A2 -#define ID_SLAVE_STOP_RESP 0x0B0 -#define ID_SLAVE_DATA 0x0B1 -#define ID_SLAVE_PING_RESP 0x0B2 - -typedef enum { - TX_SEND_PINGS, - TX_SEND_START_CMD, - TX_SEND_STOP_CMD, - TX_TASK_EXIT, -} tx_task_action_t; - -typedef enum { - RX_RECEIVE_PING_RESP, - RX_RECEIVE_DATA, - RX_RECEIVE_STOP_RESP, - RX_TASK_EXIT, -} rx_task_action_t; - -static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS(); -static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); -static const twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, TWAI_MODE_NORMAL); - -static const twai_message_t ping_message = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 1, // Is single shot (won't retry on error or NACK) - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = ID_MASTER_PING, - .data_length_code = 0, - .data = {0}, -}; - -static const twai_message_t start_message = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 0, // Not single shot - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = ID_MASTER_START_CMD, - .data_length_code = 0, - .data = {0}, -}; - -static const twai_message_t stop_message = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 0, // Not single shot - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = ID_MASTER_STOP_CMD, - .data_length_code = 0, - .data = {0}, -}; - -static QueueHandle_t tx_task_queue; -static QueueHandle_t rx_task_queue; -static SemaphoreHandle_t stop_ping_sem; -static SemaphoreHandle_t ctrl_task_sem; -static SemaphoreHandle_t done_sem; - -/* --------------------------- Tasks and Functions -------------------------- */ - -static void twai_receive_task(void *arg) -{ - while (1) { - rx_task_action_t action; - xQueueReceive(rx_task_queue, &action, portMAX_DELAY); - - if (action == RX_RECEIVE_PING_RESP) { - //Listen for ping response from slave - while (1) { - twai_message_t rx_msg; - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_SLAVE_PING_RESP) { - xSemaphoreGive(stop_ping_sem); - xSemaphoreGive(ctrl_task_sem); - break; - } - } - } else if (action == RX_RECEIVE_DATA) { - //Receive data messages from slave - uint32_t data_msgs_rec = 0; - while (data_msgs_rec < NO_OF_DATA_MSGS) { - twai_message_t rx_msg; - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_SLAVE_DATA) { - uint32_t data = 0; - for (int i = 0; i < rx_msg.data_length_code; i++) { - data |= (rx_msg.data[i] << (i * 8)); - } - ESP_LOGI(EXAMPLE_TAG, "Received data value %"PRIu32, data); - data_msgs_rec ++; - } - } - xSemaphoreGive(ctrl_task_sem); - } else if (action == RX_RECEIVE_STOP_RESP) { - //Listen for stop response from slave - while (1) { - twai_message_t rx_msg; - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_SLAVE_STOP_RESP) { - xSemaphoreGive(ctrl_task_sem); - break; - } - } - } else if (action == RX_TASK_EXIT) { - break; - } - } - vTaskDelete(NULL); -} - -static void twai_transmit_task(void *arg) -{ - while (1) { - tx_task_action_t action; - xQueueReceive(tx_task_queue, &action, portMAX_DELAY); - - if (action == TX_SEND_PINGS) { - //Repeatedly transmit pings - ESP_LOGI(EXAMPLE_TAG, "Transmitting ping"); - while (xSemaphoreTake(stop_ping_sem, 0) != pdTRUE) { - twai_transmit(&ping_message, portMAX_DELAY); - vTaskDelay(pdMS_TO_TICKS(PING_PERIOD_MS)); - } - } else if (action == TX_SEND_START_CMD) { - //Transmit start command to slave - twai_transmit(&start_message, portMAX_DELAY); - ESP_LOGI(EXAMPLE_TAG, "Transmitted start command"); - } else if (action == TX_SEND_STOP_CMD) { - //Transmit stop command to slave - twai_transmit(&stop_message, portMAX_DELAY); - ESP_LOGI(EXAMPLE_TAG, "Transmitted stop command"); - } else if (action == TX_TASK_EXIT) { - break; - } - } - vTaskDelete(NULL); -} - -static void twai_control_task(void *arg) -{ - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - tx_task_action_t tx_action; - rx_task_action_t rx_action; - - for (int iter = 0; iter < NO_OF_ITERS; iter++) { - ESP_ERROR_CHECK(twai_start()); - ESP_LOGI(EXAMPLE_TAG, "Driver started"); - - //Start transmitting pings, and listen for ping response - tx_action = TX_SEND_PINGS; - rx_action = RX_RECEIVE_PING_RESP; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - - //Send Start command to slave, and receive data messages - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - tx_action = TX_SEND_START_CMD; - rx_action = RX_RECEIVE_DATA; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - - //Send Stop command to slave when enough data messages have been received. Wait for stop response - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - tx_action = TX_SEND_STOP_CMD; - rx_action = RX_RECEIVE_STOP_RESP; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - ESP_ERROR_CHECK(twai_stop()); - ESP_LOGI(EXAMPLE_TAG, "Driver stopped"); - vTaskDelay(pdMS_TO_TICKS(ITER_DELAY_MS)); - } - //Stop TX and RX tasks - tx_action = TX_TASK_EXIT; - rx_action = RX_TASK_EXIT; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - - //Delete Control task - xSemaphoreGive(done_sem); - vTaskDelete(NULL); -} - -void app_main(void) -{ - //Create tasks, queues, and semaphores - rx_task_queue = xQueueCreate(1, sizeof(rx_task_action_t)); - tx_task_queue = xQueueCreate(1, sizeof(tx_task_action_t)); - ctrl_task_sem = xSemaphoreCreateBinary(); - stop_ping_sem = xSemaphoreCreateBinary(); - done_sem = xSemaphoreCreateBinary(); - xTaskCreatePinnedToCore(twai_receive_task, "TWAI_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY); - xTaskCreatePinnedToCore(twai_transmit_task, "TWAI_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY); - xTaskCreatePinnedToCore(twai_control_task, "TWAI_ctrl", 4096, NULL, CTRL_TSK_PRIO, NULL, tskNO_AFFINITY); - - //Install TWAI driver - ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config)); - ESP_LOGI(EXAMPLE_TAG, "Driver installed"); - - xSemaphoreGive(ctrl_task_sem); //Start control task - xSemaphoreTake(done_sem, portMAX_DELAY); //Wait for completion - - //Uninstall TWAI driver - ESP_ERROR_CHECK(twai_driver_uninstall()); - ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled"); - - //Cleanup - vQueueDelete(rx_task_queue); - vQueueDelete(tx_task_queue); - vSemaphoreDelete(ctrl_task_sem); - vSemaphoreDelete(stop_ping_sem); - vSemaphoreDelete(done_sem); -} diff --git a/examples/peripherals/twai/twai_network/twai_network_slave/main/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_network_slave/main/CMakeLists.txt deleted file mode 100644 index 5220b9c8ff..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_slave/main/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "twai_network_example_slave_main.c" - REQUIRES driver - INCLUDE_DIRS ".") diff --git a/examples/peripherals/twai/twai_network/twai_network_slave/main/Kconfig.projbuild b/examples/peripherals/twai/twai_network/twai_network_slave/main/Kconfig.projbuild deleted file mode 100644 index 14a2c83965..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_slave/main/Kconfig.projbuild +++ /dev/null @@ -1,19 +0,0 @@ -menu "Example Configuration" - - config EXAMPLE_TX_GPIO_NUM - int "TX GPIO number" - default 21 if IDF_TARGET_ESP32 - default 0 - help - This option selects the GPIO pin used for the TX signal. Connect the - TX signal to your transceiver. - - config EXAMPLE_RX_GPIO_NUM - int "RX GPIO number" - default 22 if IDF_TARGET_ESP32 - default 2 - help - This option selects the GPIO pin used for the RX signal. Connect the - RX signal to your transceiver. - -endmenu diff --git a/examples/peripherals/twai/twai_network/twai_network_slave/main/twai_network_example_slave_main.c b/examples/peripherals/twai/twai_network/twai_network_slave/main/twai_network_example_slave_main.c deleted file mode 100644 index 63bcece707..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_slave/main/twai_network_example_slave_main.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2010-2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -/* - * The following example demonstrates a slave node in a TWAI network. The slave - * node is responsible for sending data messages to the master. The example will - * execute multiple iterations, with each iteration the slave node will do the - * following: - * 1) Start the TWAI driver - * 2) Listen for ping messages from master, and send ping response - * 3) Listen for start command from master - * 4) Send data messages to master and listen for stop command - * 5) Send stop response to master - * 6) Stop the TWAI driver - */ - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "freertos/semphr.h" -#include "esp_err.h" -#include "esp_log.h" -#include "driver/twai.h" - -/* --------------------- Definitions and static variables ------------------ */ -//Example Configuration -#define DATA_PERIOD_MS 50 -#define NO_OF_ITERS 3 -#define ITER_DELAY_MS 1000 -#define RX_TASK_PRIO 8 //Receiving task priority -#define TX_TASK_PRIO 9 //Sending task priority -#define CTRL_TSK_PRIO 10 //Control task priority -#define TX_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM -#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM -#define EXAMPLE_TAG "TWAI Slave" - -#define ID_MASTER_STOP_CMD 0x0A0 -#define ID_MASTER_START_CMD 0x0A1 -#define ID_MASTER_PING 0x0A2 -#define ID_SLAVE_STOP_RESP 0x0B0 -#define ID_SLAVE_DATA 0x0B1 -#define ID_SLAVE_PING_RESP 0x0B2 - -typedef enum { - TX_SEND_PING_RESP, - TX_SEND_DATA, - TX_SEND_STOP_RESP, - TX_TASK_EXIT, -} tx_task_action_t; - -typedef enum { - RX_RECEIVE_PING, - RX_RECEIVE_START_CMD, - RX_RECEIVE_STOP_CMD, - RX_TASK_EXIT, -} rx_task_action_t; - -static const twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, TWAI_MODE_NORMAL); -static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS(); -static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); - -static const twai_message_t ping_resp = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 0, // Not single shot - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = ID_SLAVE_PING_RESP, - .data_length_code = 0, - .data = {0}, -}; - -static const twai_message_t stop_resp = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 0, // Not single shot - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = ID_SLAVE_STOP_RESP, - .data_length_code = 0, - .data = {0}, -}; - -// Data bytes of data message will be initialized in the transmit task -static twai_message_t data_message = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 0, // Not single shot - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = ID_SLAVE_DATA, - .data_length_code = 4, - .data = {1, 2, 3, 4}, -}; - -static QueueHandle_t tx_task_queue; -static QueueHandle_t rx_task_queue; -static SemaphoreHandle_t ctrl_task_sem; -static SemaphoreHandle_t stop_data_sem; -static SemaphoreHandle_t done_sem; - -/* --------------------------- Tasks and Functions -------------------------- */ - -static void twai_receive_task(void *arg) -{ - while (1) { - rx_task_action_t action; - xQueueReceive(rx_task_queue, &action, portMAX_DELAY); - if (action == RX_RECEIVE_PING) { - //Listen for pings from master - twai_message_t rx_msg; - while (1) { - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_MASTER_PING) { - xSemaphoreGive(ctrl_task_sem); - break; - } - } - } else if (action == RX_RECEIVE_START_CMD) { - //Listen for start command from master - twai_message_t rx_msg; - while (1) { - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_MASTER_START_CMD) { - xSemaphoreGive(ctrl_task_sem); - break; - } - } - } else if (action == RX_RECEIVE_STOP_CMD) { - //Listen for stop command from master - twai_message_t rx_msg; - while (1) { - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_MASTER_STOP_CMD) { - xSemaphoreGive(stop_data_sem); - xSemaphoreGive(ctrl_task_sem); - break; - } - } - } else if (action == RX_TASK_EXIT) { - break; - } - } - vTaskDelete(NULL); -} - -static void twai_transmit_task(void *arg) -{ - while (1) { - tx_task_action_t action; - xQueueReceive(tx_task_queue, &action, portMAX_DELAY); - - if (action == TX_SEND_PING_RESP) { - //Transmit ping response to master - twai_transmit(&ping_resp, portMAX_DELAY); - ESP_LOGI(EXAMPLE_TAG, "Transmitted ping response"); - xSemaphoreGive(ctrl_task_sem); - } else if (action == TX_SEND_DATA) { - //Transmit data messages until stop command is received - ESP_LOGI(EXAMPLE_TAG, "Start transmitting data"); - while (1) { - //FreeRTOS tick count used to simulate sensor data - uint32_t sensor_data = xTaskGetTickCount(); - for (int i = 0; i < 4; i++) { - data_message.data[i] = (sensor_data >> (i * 8)) & 0xFF; - } - twai_transmit(&data_message, portMAX_DELAY); - ESP_LOGI(EXAMPLE_TAG, "Transmitted data value %"PRIu32, sensor_data); - vTaskDelay(pdMS_TO_TICKS(DATA_PERIOD_MS)); - if (xSemaphoreTake(stop_data_sem, 0) == pdTRUE) { - break; - } - } - } else if (action == TX_SEND_STOP_RESP) { - //Transmit stop response to master - twai_transmit(&stop_resp, portMAX_DELAY); - ESP_LOGI(EXAMPLE_TAG, "Transmitted stop response"); - xSemaphoreGive(ctrl_task_sem); - } else if (action == TX_TASK_EXIT) { - break; - } - } - vTaskDelete(NULL); -} - -static void twai_control_task(void *arg) -{ - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - tx_task_action_t tx_action; - rx_task_action_t rx_action; - - for (int iter = 0; iter < NO_OF_ITERS; iter++) { - ESP_ERROR_CHECK(twai_start()); - ESP_LOGI(EXAMPLE_TAG, "Driver started"); - - //Listen of pings from master - rx_action = RX_RECEIVE_PING; - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - - //Send ping response - tx_action = TX_SEND_PING_RESP; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - - //Listen for start command - rx_action = RX_RECEIVE_START_CMD; - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - - //Start sending data messages and listen for stop command - tx_action = TX_SEND_DATA; - rx_action = RX_RECEIVE_STOP_CMD; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - - //Send stop response - tx_action = TX_SEND_STOP_RESP; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - - //Wait for bus to become free - twai_status_info_t status_info; - twai_get_status_info(&status_info); - while (status_info.msgs_to_tx > 0) { - vTaskDelay(pdMS_TO_TICKS(100)); - twai_get_status_info(&status_info); - } - - ESP_ERROR_CHECK(twai_stop()); - ESP_LOGI(EXAMPLE_TAG, "Driver stopped"); - vTaskDelay(pdMS_TO_TICKS(ITER_DELAY_MS)); - } - - //Stop TX and RX tasks - tx_action = TX_TASK_EXIT; - rx_action = RX_TASK_EXIT; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - - //Delete Control task - xSemaphoreGive(done_sem); - vTaskDelete(NULL); -} - -void app_main(void) -{ - //Add short delay to allow master it to initialize first - for (int i = 3; i > 0; i--) { - printf("Slave starting in %d\n", i); - vTaskDelay(pdMS_TO_TICKS(1000)); - } - - //Create semaphores and tasks - tx_task_queue = xQueueCreate(1, sizeof(tx_task_action_t)); - rx_task_queue = xQueueCreate(1, sizeof(rx_task_action_t)); - ctrl_task_sem = xSemaphoreCreateBinary(); - stop_data_sem = xSemaphoreCreateBinary(); - done_sem = xSemaphoreCreateBinary(); - xTaskCreatePinnedToCore(twai_receive_task, "TWAI_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY); - xTaskCreatePinnedToCore(twai_transmit_task, "TWAI_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY); - xTaskCreatePinnedToCore(twai_control_task, "TWAI_ctrl", 4096, NULL, CTRL_TSK_PRIO, NULL, tskNO_AFFINITY); - - //Install TWAI driver, trigger tasks to start - ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config)); - ESP_LOGI(EXAMPLE_TAG, "Driver installed"); - - xSemaphoreGive(ctrl_task_sem); //Start Control task - xSemaphoreTake(done_sem, portMAX_DELAY); //Wait for tasks to complete - - //Uninstall TWAI driver - ESP_ERROR_CHECK(twai_driver_uninstall()); - ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled"); - - //Cleanup - vSemaphoreDelete(ctrl_task_sem); - vSemaphoreDelete(stop_data_sem); - vSemaphoreDelete(done_sem); - vQueueDelete(tx_task_queue); - vQueueDelete(rx_task_queue); -} diff --git a/examples/peripherals/twai/twai_network/twai_network_master/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_sender/CMakeLists.txt similarity index 92% rename from examples/peripherals/twai/twai_network/twai_network_master/CMakeLists.txt rename to examples/peripherals/twai/twai_network/twai_sender/CMakeLists.txt index 2cfde1c6f9..0b3f7b267f 100644 --- a/examples/peripherals/twai/twai_network/twai_network_master/CMakeLists.txt +++ b/examples/peripherals/twai/twai_network/twai_sender/CMakeLists.txt @@ -5,4 +5,4 @@ cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) # "Trim" the build. Include the minimal set of components, main, and anything it depends on. idf_build_set_property(MINIMAL_BUILD ON) -project(twai_network_master) +project(twai_sender) diff --git a/examples/peripherals/twai/twai_network/twai_sender/main/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_sender/main/CMakeLists.txt new file mode 100644 index 0000000000..d9ba487f69 --- /dev/null +++ b/examples/peripherals/twai/twai_network/twai_sender/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "twai_sender.c" + REQUIRES esp_driver_twai esp_timer + INCLUDE_DIRS "." +) diff --git a/examples/peripherals/twai/twai_network/twai_sender/main/Kconfig.projbuild b/examples/peripherals/twai/twai_network/twai_sender/main/Kconfig.projbuild new file mode 100644 index 0000000000..395fdb7a34 --- /dev/null +++ b/examples/peripherals/twai/twai_network/twai_sender/main/Kconfig.projbuild @@ -0,0 +1,17 @@ +menu "Example Configuration" + + config EXAMPLE_TWAI_TX_GPIO + int "TWAI TX GPIO Num" + range 0 SOC_GPIO_OUT_RANGE_MAX + default 4 + help + GPIO number for TWAI TX to transceiver. + + config EXAMPLE_TWAI_RX_GPIO + int "TWAI RX GPIO Num" + range 0 SOC_GPIO_IN_RANGE_MAX + default 5 + help + GPIO number for TWAI RX from transceiver. + +endmenu diff --git a/examples/peripherals/twai/twai_network/twai_sender/main/twai_sender.c b/examples/peripherals/twai/twai_network/twai_sender/main/twai_sender.c new file mode 100644 index 0000000000..794cd21ff1 --- /dev/null +++ b/examples/peripherals/twai/twai_network/twai_sender/main/twai_sender.c @@ -0,0 +1,139 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_twai.h" +#include "esp_twai_onchip.h" + +#define TWAI_SENDER_TX_GPIO CONFIG_EXAMPLE_TWAI_TX_GPIO +#define TWAI_SENDER_RX_GPIO CONFIG_EXAMPLE_TWAI_RX_GPIO +#define TWAI_QUEUE_DEPTH 10 +#define TWAI_BITRATE 1000000 + +// Message IDs +#define TWAI_DATA_ID 0x100 +#define TWAI_HEARTBEAT_ID 0x7FF +#define TWAI_DATA_LEN 1000 + +static const char *TAG = "twai_sender"; + +typedef struct { + twai_frame_t frame; + uint8_t data[TWAI_FRAME_MAX_LEN]; +} twai_sender_data_t; + +// Transmission completion callback +static IRAM_ATTR bool twai_sender_tx_done_callback(twai_node_handle_t handle, const twai_tx_done_event_data_t *edata, void *user_ctx) +{ + BaseType_t woken; + SemaphoreHandle_t *tx_semaphore = (SemaphoreHandle_t *)user_ctx; + if (!edata->is_tx_success) { + ESP_EARLY_LOGW(TAG, "Failed to transmit message, ID: 0x%X", edata->done_tx_frame->header.id); + } + xSemaphoreGiveFromISR(*tx_semaphore, &woken); + return (woken == pdTRUE); +} + +// Bus error callback +static IRAM_ATTR bool twai_sender_on_error_callback(twai_node_handle_t handle, const twai_error_event_data_t *edata, void *user_ctx) +{ + ESP_EARLY_LOGW(TAG, "TWAI node error: 0x%x", edata->err_flags.val); + return false; // No task wake required +} + +void app_main(void) +{ + twai_node_handle_t sender_node = NULL; + SemaphoreHandle_t tx_semaphore = NULL; + printf("===================TWAI Sender Example Starting...===================\n"); + + // Configure TWAI node + twai_onchip_node_config_t node_config = { + .io_cfg = { + .tx = TWAI_SENDER_TX_GPIO, + .rx = TWAI_SENDER_RX_GPIO, + .quanta_clk_out = -1, + .bus_off_indicator = -1, + }, + .bit_timing = { + .bitrate = TWAI_BITRATE, + }, + .fail_retry_cnt = 3, + .tx_queue_depth = TWAI_QUEUE_DEPTH, + }; + + // Create TWAI node + ESP_ERROR_CHECK(twai_new_node_onchip(&node_config, &sender_node)); + + // Register transmission completion callback + twai_event_callbacks_t callbacks = { + .on_tx_done = twai_sender_tx_done_callback, + .on_error = twai_sender_on_error_callback, + }; + ESP_ERROR_CHECK(twai_node_register_event_callbacks(sender_node, &callbacks, &tx_semaphore)); + + // Enable TWAI node + ESP_ERROR_CHECK(twai_node_enable(sender_node)); + + // Create semaphore for transmission completion + tx_semaphore = xSemaphoreCreateCounting(howmany(TWAI_DATA_LEN, TWAI_FRAME_MAX_LEN), 0); + assert(tx_semaphore != NULL); + + ESP_LOGI(TAG, "TWAI Sender started successfully"); + ESP_LOGI(TAG, "Sending messages with IDs: 0x%03X (data), 0x%03X (heartbeat)", TWAI_DATA_ID, TWAI_HEARTBEAT_ID); + + while (1) { + // Send heartbeat message + uint64_t timestamp = esp_timer_get_time(); + twai_frame_t tx_frame = { + .header.id = TWAI_HEARTBEAT_ID, + .buffer = (uint8_t *) ×tamp, + .buffer_len = sizeof(timestamp), + }; + ESP_ERROR_CHECK(twai_node_transmit(sender_node, &tx_frame, 500)); + ESP_LOGI(TAG, "Sending heartbeat message: %lld", timestamp); + xSemaphoreTake(tx_semaphore, portMAX_DELAY); + + // Send burst data messages every 10 seconds + if ((timestamp / 1000000) % 10 == 0) { + int num_frames = howmany(TWAI_DATA_LEN, TWAI_FRAME_MAX_LEN); + twai_sender_data_t *data = (twai_sender_data_t *)calloc(num_frames, sizeof(twai_sender_data_t)); + assert(data != NULL); + ESP_LOGI(TAG, "Sending packet of %d bytes in %d frames", TWAI_DATA_LEN, num_frames); + for (int i = 0; i < num_frames; i++) { + data[i].frame.header.id = TWAI_DATA_ID; + data[i].frame.buffer = data[i].data; + data[i].frame.buffer_len = TWAI_FRAME_MAX_LEN; + memset(data[i].data, i, TWAI_FRAME_MAX_LEN); + ESP_ERROR_CHECK(twai_node_transmit(sender_node, &data[i].frame, 500)); + } + + // Frames mounted, wait for all frames to be transmitted + for (int i = 0; i < num_frames; i++) { + xSemaphoreTake(tx_semaphore, portMAX_DELAY); + } + free(data); + } + + vTaskDelay(pdMS_TO_TICKS(1000)); + twai_node_status_t status; + twai_node_get_info(sender_node, &status, NULL); + if (status.state == TWAI_ERROR_BUS_OFF) { + ESP_LOGW(TAG, "Bus-off detected"); + return; + } + } + + vSemaphoreDelete(tx_semaphore); + ESP_ERROR_CHECK(twai_node_disable(sender_node)); + ESP_ERROR_CHECK(twai_node_delete(sender_node)); +} diff --git a/examples/peripherals/twai/twai_self_test/README.md b/examples/peripherals/twai/twai_self_test/README.md deleted file mode 100644 index f321f6aea1..0000000000 --- a/examples/peripherals/twai/twai_self_test/README.md +++ /dev/null @@ -1,86 +0,0 @@ -| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | - -# TWAI Self Test Example - -(See the README.md file in the upper level 'examples' directory for more information about examples.) - -The TWAI Self Test Example demonstrates how a node can transmit TWAI messages to itself using the TWAI driver's "No Acknowledgement" mode and Self Reception Requests. The Self Test Example can be run as a simple test to determine whether a target (ESP32, ESP32-S2, ESP32-S3 or ESP32-C3) is properly connected to a working external transceiver. - -## How to use example - -### Hardware Required - -This example requires only a single target (e.g., an ESP32 or ESP32-S2). The target must be connected to an external transceiver (e.g., a SN65HVD23X transceiver). This connection usually consists of a TX and an RX signal. - -Note: If you don't have an external transceiver, this example can still be run by simply connecting the TX GPIO and RX GPIO with a jumper. - -### Configure the project - -* Set the target of the build (where `{IDF_TARGET}` stands for the target chip such as `esp32` or `esp32s2`). -* Then run `menuconfig` to configure the example. - -```sh -idf.py set-target {IDF_TARGET} -idf.py menuconfig -``` - -* Under `Example Configuration`, configure the pin assignments using the options `TX GPIO Number` and `RX GPIO Number` according to how the target was connected to the transceiver. By default, `TX GPIO Number` and `RX GPIO Number` are set to the following values: - * On the ESP32, `TX GPIO Number` and `RX GPIO Number` default to `21` and `22` respectively - * On other chips, `TX GPIO Number` and `RX GPIO Number` default to `0` and `2` respectively - -### Build and Flash - -Build the project and flash it to the board, then run monitor tool to view serial output: - -```sh -idf.py -p PORT flash monitor -``` - -(Replace PORT with the name of the serial port to use.) - -(To exit the serial monitor, type ``Ctrl-]``.) - -See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. - -## Example Output - -```text -I (345) TWAI Self Test: Driver installed -I (345) TWAI Self Test: Driver started -I (355) TWAI Self Test: Msg received - Data = 0 -... -I (1335) TWAI Self Test: Msg received - Data = 99 -I (1335) TWAI Self Test: Driver stopped -I (1435) TWAI Self Test: Driver started -I (1435) TWAI Self Test: Msg received - Data = 0 -... -I (2425) TWAI Self Test: Msg received - Data = 99 -I (2425) TWAI Self Test: Driver stopped -I (2525) TWAI Self Test: Driver started -I (2525) TWAI Self Test: Msg received - Data = 0 -... -I (3515) TWAI Self Test: Msg received - Data = 99 -I (3515) TWAI Self Test: Driver stopped -I (3615) TWAI Self Test: Driver uninstalled -``` - -## Troubleshooting - -```text -I (345) TWAI Self Test: Driver installed -I (345) TWAI Self Test: Driver started -``` - -If the TWAI driver is installed and started but no messages are received, check that the target is correctly connected to the external transceiver, and that the external transceiver is operating properly (i.e., properly powered and not in sleep mode). - -## Example Breakdown - -The TWAI Self Test Example will do multiple iterations of the following steps: - -1. Install the TWAI driver -2. Start the TWAI driver -3. Simultaneously transmit and receive multiple messages using the self reception request. -4. Stop the TWAI driver -5. Repeat steps 2 to 4 for multiple iterations -6. Uninstall the TWAI driver diff --git a/examples/peripherals/twai/twai_self_test/main/CMakeLists.txt b/examples/peripherals/twai/twai_self_test/main/CMakeLists.txt deleted file mode 100644 index a1d798b3b4..0000000000 --- a/examples/peripherals/twai/twai_self_test/main/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "twai_self_test_example_main.c" - REQUIRES driver - INCLUDE_DIRS ".") diff --git a/examples/peripherals/twai/twai_self_test/main/Kconfig.projbuild b/examples/peripherals/twai/twai_self_test/main/Kconfig.projbuild deleted file mode 100644 index 1688672cec..0000000000 --- a/examples/peripherals/twai/twai_self_test/main/Kconfig.projbuild +++ /dev/null @@ -1,23 +0,0 @@ -menu "Example Configuration" - - orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps" - - config EXAMPLE_TX_GPIO_NUM - int "TX GPIO number" - range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX - default 21 if IDF_TARGET_ESP32 - default 0 - help - This option selects the GPIO pin used for the TX signal. Connect the - TX signal to your transceiver. - - config EXAMPLE_RX_GPIO_NUM - int "RX GPIO number" - range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX - default 22 if IDF_TARGET_ESP32 - default 2 - help - This option selects the GPIO pin used for the RX signal. Connect the - RX signal to your transceiver. - -endmenu diff --git a/examples/peripherals/twai/twai_self_test/main/twai_self_test_example_main.c b/examples/peripherals/twai/twai_self_test/main/twai_self_test_example_main.c deleted file mode 100644 index 7cf7c6975e..0000000000 --- a/examples/peripherals/twai/twai_self_test/main/twai_self_test_example_main.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2010-2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -/* - * The following example demonstrates the self testing capabilities of the TWAI - * peripheral by utilizing the No Acknowledgment Mode and Self Reception Request - * capabilities. This example can be used to verify that the TWAI peripheral and - * its connections to the external transceiver operates without issue. The example - * will execute multiple iterations, each iteration will do the following: - * 1) Start the TWAI driver - * 2) Transmit and receive 100 messages using self reception request - * 3) Stop the TWAI driver - */ - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "esp_err.h" -#include "esp_log.h" -#include "driver/twai.h" - -/* --------------------- Definitions and static variables ------------------ */ - -//Example Configurations -#define NO_OF_MSGS 100 -#define NO_OF_ITERS 3 -#define TX_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM -#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM -#define TX_TASK_PRIO 8 //Sending task priority -#define RX_TASK_PRIO 9 //Receiving task priority -#define CTRL_TSK_PRIO 10 //Control task priority -#define MSG_ID 0x555 //11 bit standard format ID -#define EXAMPLE_TAG "TWAI Self Test" - -static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS(); -//Filter all other IDs except MSG_ID -static const twai_filter_config_t f_config = {.acceptance_code = (MSG_ID << 21), - .acceptance_mask = ~(TWAI_STD_ID_MASK << 21), - .single_filter = true - }; -//Set to NO_ACK mode due to self testing with single module -static const twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, TWAI_MODE_NO_ACK); - -static SemaphoreHandle_t tx_sem; -static SemaphoreHandle_t rx_sem; -static SemaphoreHandle_t ctrl_sem; -static SemaphoreHandle_t done_sem; - -/* --------------------------- Tasks and Functions -------------------------- */ - -static void twai_transmit_task(void *arg) -{ - twai_message_t tx_msg = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 0, // Not single shot - .self = 1, // Message is a self reception request (loopback) - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = MSG_ID, - .data_length_code = 1, - .data = {0}, - }; - - for (int iter = 0; iter < NO_OF_ITERS; iter++) { - xSemaphoreTake(tx_sem, portMAX_DELAY); - for (int i = 0; i < NO_OF_MSGS; i++) { - //Transmit messages using self reception request - tx_msg.data[0] = i; - ESP_ERROR_CHECK(twai_transmit(&tx_msg, portMAX_DELAY)); - vTaskDelay(pdMS_TO_TICKS(10)); - } - } - vTaskDelete(NULL); -} - -static void twai_receive_task(void *arg) -{ - twai_message_t rx_message; - - for (int iter = 0; iter < NO_OF_ITERS; iter++) { - xSemaphoreTake(rx_sem, portMAX_DELAY); - for (int i = 0; i < NO_OF_MSGS; i++) { - //Receive message and print message data - ESP_ERROR_CHECK(twai_receive(&rx_message, portMAX_DELAY)); - ESP_LOGI(EXAMPLE_TAG, "Msg received\tID 0x%lx\tData = %d", rx_message.identifier, rx_message.data[0]); - } - //Indicate to control task all messages received for this iteration - xSemaphoreGive(ctrl_sem); - } - vTaskDelete(NULL); -} - -static void twai_control_task(void *arg) -{ - xSemaphoreTake(ctrl_sem, portMAX_DELAY); - for (int iter = 0; iter < NO_OF_ITERS; iter++) { - //Start TWAI Driver for this iteration - ESP_ERROR_CHECK(twai_start()); - ESP_LOGI(EXAMPLE_TAG, "Driver started"); - - //Trigger TX and RX tasks to start transmitting/receiving - xSemaphoreGive(rx_sem); - xSemaphoreGive(tx_sem); - xSemaphoreTake(ctrl_sem, portMAX_DELAY); //Wait for TX and RX tasks to finish iteration - - ESP_ERROR_CHECK(twai_stop()); //Stop the TWAI Driver - ESP_LOGI(EXAMPLE_TAG, "Driver stopped"); - vTaskDelay(pdMS_TO_TICKS(100)); //Delay then start next iteration - } - xSemaphoreGive(done_sem); - vTaskDelete(NULL); -} - -void app_main(void) -{ - //Create tasks and synchronization primitives - tx_sem = xSemaphoreCreateBinary(); - rx_sem = xSemaphoreCreateBinary(); - ctrl_sem = xSemaphoreCreateBinary(); - done_sem = xSemaphoreCreateBinary(); - - xTaskCreatePinnedToCore(twai_control_task, "TWAI_ctrl", 4096, NULL, CTRL_TSK_PRIO, NULL, tskNO_AFFINITY); - xTaskCreatePinnedToCore(twai_receive_task, "TWAI_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY); - xTaskCreatePinnedToCore(twai_transmit_task, "TWAI_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY); - - //Install TWAI driver - ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config)); - ESP_LOGI(EXAMPLE_TAG, "Driver installed"); - - //Start control task - xSemaphoreGive(ctrl_sem); - //Wait for all iterations and tasks to complete running - xSemaphoreTake(done_sem, portMAX_DELAY); - - //Uninstall TWAI driver - ESP_ERROR_CHECK(twai_driver_uninstall()); - ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled"); - - //Cleanup - vSemaphoreDelete(tx_sem); - vSemaphoreDelete(rx_sem); - vSemaphoreDelete(ctrl_sem); - vQueueDelete(done_sem); -} diff --git a/examples/peripherals/twai/twai_self_test/pytest_twai_self_test_example.py b/examples/peripherals/twai/twai_self_test/pytest_twai_self_test_example.py deleted file mode 100644 index 39a0243050..0000000000 --- a/examples/peripherals/twai/twai_self_test/pytest_twai_self_test_example.py +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD -# SPDX-License-Identifier: CC0-1.0 -import pytest -from pytest_embedded import Dut -from pytest_embedded_idf.utils import idf_parametrize - - -@pytest.mark.twai_transceiver -@idf_parametrize('target', ['esp32'], indirect=['target']) -def test_twai_self_test_example(dut: Dut) -> None: - dut.expect_exact('TWAI Self Test: Driver installed') - dut.expect_exact('TWAI Self Test: Driver uninstalled')