From 40b340ba7de2e3af67ff47e0a971a4635256aacb Mon Sep 17 00:00:00 2001 From: MilladAnsari <162457552+MilladAnsari@users.noreply.github.com> Date: Sun, 9 Aug 2026 14:05:04 +0330 Subject: [PATCH 1/3] feat(esp32): add TWAI (CAN bus) User C Module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 📚 Overview This PR introduces a custom User C Module for Espressif's TWAI (Two-Wire Automotive Interface / CAN Bus) driver, specifically tailored for ESP32 target microcontrollers. Since the standard machine.CAN module is not natively implemented for ESP32 ports in MicroPython, this binding provides a direct interface to ESP-IDF's native TWAI driver. 📚 Changes Included ⦁ *usermod_twai/usermod_twai.c*: Implements C bindings interfacing #include "driver/twai.h" with MicroPython runtime (MP_REGISTER_MODULE), exposing essential TWAI functions (init for pin/baudrate setup and send for transmitting CAN frames). ⦁ *usermod_twai/micropython.cmake*: Adds CMake build target configuration for compiling the module within custom MicroPython firmware builds. --- ext_mod/twai_esp32/micropython.cmake | 11 +++++ ext_mod/twai_esp32/usermod_twai.c | 69 ++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 ext_mod/twai_esp32/micropython.cmake create mode 100644 ext_mod/twai_esp32/usermod_twai.c diff --git a/ext_mod/twai_esp32/micropython.cmake b/ext_mod/twai_esp32/micropython.cmake new file mode 100644 index 00000000..325cd947 --- /dev/null +++ b/ext_mod/twai_esp32/micropython.cmake @@ -0,0 +1,11 @@ +add_library(usermod_twai INTERFACE) + +target_sources(usermod_twai INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/usermod_twai.c +) + +target_include_directories(usermod_twai INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) + +target_link_libraries(usermod INTERFACE usermod_twai) \ No newline at end of file diff --git a/ext_mod/twai_esp32/usermod_twai.c b/ext_mod/twai_esp32/usermod_twai.c new file mode 100644 index 00000000..9c40c642 --- /dev/null +++ b/ext_mod/twai_esp32/usermod_twai.c @@ -0,0 +1,69 @@ +#include "py/runtime.h" +#include "py/obj.h" +#include "driver/twai.h" + +static mp_obj_t twai_init_module(size_t n_args, const mp_obj_t *args) { + int tx_pin = mp_obj_get_int(args[0]); + int rx_pin = mp_obj_get_int(args[1]); + int baudrate = mp_obj_get_int(args[2]); + + twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(tx_pin, rx_pin, TWAI_MODE_NORMAL); + twai_timing_config_t t_config; + + if (baudrate == 50000) { + twai_timing_config_t t_cfg = TWAI_TIMING_CONFIG_50KBITS(); + t_config = t_cfg; + } else if (baudrate == 250000) { + twai_timing_config_t t_cfg = TWAI_TIMING_CONFIG_250KBITS(); + t_config = t_cfg; + } else { + twai_timing_config_t t_cfg = TWAI_TIMING_CONFIG_500KBITS(); + t_config = t_cfg; + } + + twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); + + if (twai_driver_install(&g_config, &t_config, &f_config) != ESP_OK) { + mp_raise_ValueError(MP_ERROR_TEXT("Failed to install TWAI driver")); + } + if (twai_start() != ESP_OK) { + mp_raise_ValueError(MP_ERROR_TEXT("Failed to start TWAI driver")); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(twai_init_obj, 3, 3, twai_init_module); + +static mp_obj_t twai_send_msg(size_t n_args, const mp_obj_t *args) { + uint32_t id = mp_obj_get_int(args[0]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ); + + twai_message_t message = {0}; + message.identifier = id; + message.data_length_code = bufinfo.len > 8 ? 8 : bufinfo.len; + for (int i = 0; i < message.data_length_code; i++) { + message.data[i] = ((uint8_t*)bufinfo.buf)[i]; + } + + if (twai_transmit(&message, pdMS_TO_TICKS(100)) != ESP_OK) { + mp_raise_ValueError(MP_ERROR_TEXT("Failed to transmit CAN message")); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(twai_send_obj, 2, 2, twai_send_msg); + +static const mp_rom_map_elem_t twai_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_twai) }, + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&twai_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&twai_send_obj) }, +}; +static MP_DEFINE_CONST_DICT(twai_module_globals, twai_module_globals_table); + +const mp_obj_module_t twai_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&twai_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_twai, twai_user_cmodule); \ No newline at end of file From cfa3c43921c1a81814cd90d9eeb73466a1413397 Mon Sep 17 00:00:00 2001 From: MilladAnsari <162457552+MilladAnsari@users.noreply.github.com> Date: Sun, 9 Aug 2026 14:13:56 +0330 Subject: [PATCH 2/3] build(cmake): register usermod_twai in micropython.cmake Register the `usermod_twai` module within the main `micropython.cmake` build configuration. This includes the CMake configuration file from `usermod_twai/micropython.cmake` to ensure the TWAI (CAN bus) User C Module is automatically picked up and compiled into the firmware during the build process. --- ext_mod/micropython.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/ext_mod/micropython.cmake b/ext_mod/micropython.cmake index 50051f6f..03686fc5 100644 --- a/ext_mod/micropython.cmake +++ b/ext_mod/micropython.cmake @@ -22,3 +22,4 @@ include(${CMAKE_CURRENT_LIST_DIR}/lcd_utils/micropython.cmake) if(DEFINED ENV{FUSION}) include(${CMAKE_CURRENT_LIST_DIR}/imu_fusion/micropython.cmake) endif() +include(${CMAKE_CURRENT_LIST_DIR}/usermod_twai/micropython.cmake) From 850334e0ef5c5e05cb58dde57d14dd1398a6db50 Mon Sep 17 00:00:00 2001 From: MilladAnsari <162457552+MilladAnsari@users.noreply.github.com> Date: Sun, 9 Aug 2026 18:14:02 +0330 Subject: [PATCH 3/3] add recv and receive functions for listening --- ext_mod/twai_esp32/usermod_twai.c | 69 ---------- .../micropython.cmake | 20 +-- ext_mod/usermod_twai/usermod_twai.c | 126 ++++++++++++++++++ 3 files changed, 136 insertions(+), 79 deletions(-) delete mode 100644 ext_mod/twai_esp32/usermod_twai.c rename ext_mod/{twai_esp32 => usermod_twai}/micropython.cmake (96%) create mode 100644 ext_mod/usermod_twai/usermod_twai.c diff --git a/ext_mod/twai_esp32/usermod_twai.c b/ext_mod/twai_esp32/usermod_twai.c deleted file mode 100644 index 9c40c642..00000000 --- a/ext_mod/twai_esp32/usermod_twai.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "py/runtime.h" -#include "py/obj.h" -#include "driver/twai.h" - -static mp_obj_t twai_init_module(size_t n_args, const mp_obj_t *args) { - int tx_pin = mp_obj_get_int(args[0]); - int rx_pin = mp_obj_get_int(args[1]); - int baudrate = mp_obj_get_int(args[2]); - - twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(tx_pin, rx_pin, TWAI_MODE_NORMAL); - twai_timing_config_t t_config; - - if (baudrate == 50000) { - twai_timing_config_t t_cfg = TWAI_TIMING_CONFIG_50KBITS(); - t_config = t_cfg; - } else if (baudrate == 250000) { - twai_timing_config_t t_cfg = TWAI_TIMING_CONFIG_250KBITS(); - t_config = t_cfg; - } else { - twai_timing_config_t t_cfg = TWAI_TIMING_CONFIG_500KBITS(); - t_config = t_cfg; - } - - twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); - - if (twai_driver_install(&g_config, &t_config, &f_config) != ESP_OK) { - mp_raise_ValueError(MP_ERROR_TEXT("Failed to install TWAI driver")); - } - if (twai_start() != ESP_OK) { - mp_raise_ValueError(MP_ERROR_TEXT("Failed to start TWAI driver")); - } - - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(twai_init_obj, 3, 3, twai_init_module); - -static mp_obj_t twai_send_msg(size_t n_args, const mp_obj_t *args) { - uint32_t id = mp_obj_get_int(args[0]); - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ); - - twai_message_t message = {0}; - message.identifier = id; - message.data_length_code = bufinfo.len > 8 ? 8 : bufinfo.len; - for (int i = 0; i < message.data_length_code; i++) { - message.data[i] = ((uint8_t*)bufinfo.buf)[i]; - } - - if (twai_transmit(&message, pdMS_TO_TICKS(100)) != ESP_OK) { - mp_raise_ValueError(MP_ERROR_TEXT("Failed to transmit CAN message")); - } - - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(twai_send_obj, 2, 2, twai_send_msg); - -static const mp_rom_map_elem_t twai_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_twai) }, - { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&twai_init_obj) }, - { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&twai_send_obj) }, -}; -static MP_DEFINE_CONST_DICT(twai_module_globals, twai_module_globals_table); - -const mp_obj_module_t twai_user_cmodule = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&twai_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_twai, twai_user_cmodule); \ No newline at end of file diff --git a/ext_mod/twai_esp32/micropython.cmake b/ext_mod/usermod_twai/micropython.cmake similarity index 96% rename from ext_mod/twai_esp32/micropython.cmake rename to ext_mod/usermod_twai/micropython.cmake index 325cd947..4bf1c3a5 100644 --- a/ext_mod/twai_esp32/micropython.cmake +++ b/ext_mod/usermod_twai/micropython.cmake @@ -1,11 +1,11 @@ -add_library(usermod_twai INTERFACE) - -target_sources(usermod_twai INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/usermod_twai.c -) - -target_include_directories(usermod_twai INTERFACE - ${CMAKE_CURRENT_LIST_DIR} -) - +add_library(usermod_twai INTERFACE) + +target_sources(usermod_twai INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/usermod_twai.c +) + +target_include_directories(usermod_twai INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) + target_link_libraries(usermod INTERFACE usermod_twai) \ No newline at end of file diff --git a/ext_mod/usermod_twai/usermod_twai.c b/ext_mod/usermod_twai/usermod_twai.c new file mode 100644 index 00000000..9a5d7933 --- /dev/null +++ b/ext_mod/usermod_twai/usermod_twai.c @@ -0,0 +1,126 @@ +#include "py/runtime.h" +#include "py/obj.h" +#include "driver/twai.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +// ------------------------------------------------------------------- +// ۱. تابع init +// ------------------------------------------------------------------- +static mp_obj_t twai_init_func(size_t n_args, const mp_obj_t *args) { + int tx_pin = mp_obj_get_int(args[0]); + int rx_pin = mp_obj_get_int(args[1]); + int baudrate = mp_obj_get_int(args[2]); + + twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t)tx_pin, (gpio_num_t)rx_pin, TWAI_MODE_NORMAL); + twai_timing_config_t t_config; + + if (baudrate == 50000) { + twai_timing_config_t t_tmp = TWAI_TIMING_CONFIG_50KBITS(); + t_config = t_tmp; + } else if (baudrate == 100000) { + twai_timing_config_t t_tmp = TWAI_TIMING_CONFIG_100KBITS(); + t_config = t_tmp; + } else if (baudrate == 250000) { + twai_timing_config_t t_tmp = TWAI_TIMING_CONFIG_250KBITS(); + t_config = t_tmp; + } else if (baudrate == 500000) { + twai_timing_config_t t_tmp = TWAI_TIMING_CONFIG_500KBITS(); + t_config = t_tmp; + } else { + mp_raise_ValueError(MP_ERROR_TEXT("Unsupported baudrate")); + } + + twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); + + if (twai_driver_install(&g_config, &t_config, &f_config) != ESP_OK) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed to install TWAI driver")); + } + + if (twai_start() != ESP_OK) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed to start TWAI driver")); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(twai_init_obj, 3, 3, twai_init_func); + +// ------------------------------------------------------------------- +// ۲. تابع send +// ------------------------------------------------------------------- +static mp_obj_t twai_send_func(mp_obj_t id_obj, mp_obj_t data_obj) { + uint32_t id = mp_obj_get_int(id_obj); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_obj, &bufinfo, MP_BUFFER_READ); + + if (bufinfo.len > 8) { + mp_raise_ValueError(MP_ERROR_TEXT("CAN data frame cannot exceed 8 bytes")); + } + + twai_message_t tx_msg = {0}; + tx_msg.identifier = id; + tx_msg.data_length_code = bufinfo.len; + for (size_t i = 0; i < bufinfo.len; i++) { + tx_msg.data[i] = ((uint8_t *)bufinfo.buf)[i]; + } + + if (twai_transmit(&tx_msg, pdMS_TO_TICKS(1000)) != ESP_OK) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed to transmit CAN message")); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(twai_send_obj, twai_send_func); + +// ------------------------------------------------------------------- +// ۳. تابع receive (تغییر نام تابع به twai_receive_func جهت جلوگیری از تداخل) +// ------------------------------------------------------------------- +static mp_obj_t twai_receive_func(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_timeout, MP_ARG_INT, {.u_int = 10} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + uint32_t timeout_ms = args[ARG_timeout].u_int; + + twai_message_t rx_msg; + esp_err_t res = twai_receive(&rx_msg, pdMS_TO_TICKS(timeout_ms)); + + if (res == ESP_OK) { + mp_obj_t data_bytes = mp_obj_new_bytes(rx_msg.data, rx_msg.data_length_code); + + mp_obj_t tuple[4]; + tuple[0] = mp_obj_new_int(rx_msg.identifier); + tuple[1] = data_bytes; + tuple[2] = mp_obj_new_bool(rx_msg.extd); + tuple[3] = mp_obj_new_bool(rx_msg.rtr); + + return mp_obj_new_tuple(4, tuple); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(twai_receive_obj, 0, twai_receive_func); + +// ------------------------------------------------------------------- +// ۴. جدول ماژول و ثبت در MicroPython +// ------------------------------------------------------------------- +static const mp_rom_map_elem_t twai_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_twai) }, + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&twai_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&twai_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&twai_receive_obj) }, + { MP_ROM_QSTR(MP_QSTR_receive), MP_ROM_PTR(&twai_receive_obj) }, +}; +static MP_DEFINE_CONST_DICT(twai_module_globals, twai_module_globals_table); + +const mp_obj_module_t twai_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&twai_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_twai, twai_user_cmodule); \ No newline at end of file