├── .gitignore ├── LICENSE ├── README.md ├── examples ├── esp32can_receive │ └── esp32can_receive.ino ├── esp32can_rtos │ └── esp32can_rtos.ino └── esp32can_send │ └── esp32can_send.ino ├── keywords.txt ├── library.properties └── src ├── ESP32CAN.cpp └── ESP32CAN.h /.gitignore: -------------------------------------------------------------------------------- 1 | .pioenvs 2 | .piolibdeps 3 | .vscode/**/* 4 | .travis.yml 5 | platformio.ini 6 | lib/readme.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Michael Wagner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino Library for the ESP32 CAN Bus (ESP32-Arduino-CAN) 2 | 3 | ## V2.0 4 | This is a rewrite of the original library created by [miwagner](https://github.com/miwagner/ESP32-Arduino-CAN). The old version of this library used registry commands to configure the TWAI[^1] peripheral and has not been updated in years. Newer versions of the Espressif IDF have built in functions to configure the TWAI peripheral that are more reliable. V2.0 of this library incorporates these new IDF functions in the original function calls of the ESP32-Arduino-CAN library. 5 | 6 | ## Features 7 | * Easily configure the ESP32 TWAI[^1] peripheral 8 | * Send and receive CAN Bus messages 9 | * Various bus speeds 10 | * Standard (11bit) and extended (29bit) frames 11 | * ~~CAN Message Filter~~ 12 | 13 | ## Usage 14 | See the examples in the [/examples](examples) folder. 15 | 16 | ## Espressif IDF Documentation 17 | The Espressif IDF documentation at the link below should be reference first if you encounter any errors. Make sure you are using the most recent IDF version, V5.0 when writing this. 18 | 19 | https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/twai.html#examples 20 | 21 | ## TODO 22 | - [x] Advanced example with RTOS tasks 23 | - [x] Release to main branch, no push to miwagner version 24 | - [ ] Implement CAN Message Filter 25 | 26 | [^1]: TWAI is the name that Esperssif uses for the CAN peripheral. It stands for *two wire automotive interface* -------------------------------------------------------------------------------- /examples/esp32can_receive/esp32can_receive.ino: -------------------------------------------------------------------------------- 1 | /* ESP32 Arduino CAN Receive Basic Demo 2 | * This example will receive messages on the CAN bus 3 | * 4 | * An external transceiver is required and should be connected 5 | * to the CAN_tx and CAN_rx gpio pins specified by CANInit. Be sure 6 | * to use a 3.3V compatable transceiver such as the SN65HVD23x 7 | * 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | void setup() { 14 | Serial.begin(115200); 15 | Serial.println("ESP32-Arduino-CAN Receive Basic Demo"); 16 | 17 | /* initialize and start, use pin 5 as CAN_tx and pin 4 as CAN_rx, CAN bus is set to 500kbps */ 18 | ESP32Can.CANInit(GPIO_NUM_5, GPIO_NUM_4, ESP32CAN_SPEED_500KBPS); 19 | } 20 | 21 | void loop() { 22 | twai_message_t rx_frame; 23 | 24 | if (ESP32CAN_OK == ESP32Can.CANReadFrame(&rx_frame)) { /* only print when CAN message is received*/ 25 | Serial.print(rx_frame.identifier, HEX); /* print the CAN ID*/ 26 | Serial.print(" "); 27 | Serial.print(rx_frame.data_length_code); /* print number of bytes in data frame*/ 28 | 29 | for (int i=0; i 14 | #include 15 | 16 | /* RTOS priorities, higher number is more important */ 17 | #define CAN_TX_PRIORITY 3 18 | #define CAN_RX_PRIORITY 1 19 | 20 | #define CAN_TX_RATE_ms 500 21 | #define CAN_RX_RATE_ms 1000 22 | 23 | /* can frame to send */ 24 | twai_message_t tx_frame; 25 | 26 | /* CAN RTOS callback functions */ 27 | void canSend(void *pvParameters); 28 | void canReceive(void *pvParameters); 29 | 30 | /* CAN RTOS task handles */ 31 | static TaskHandle_t canTxTask = NULL; 32 | static TaskHandle_t canRxTask = NULL; 33 | 34 | void setup() { 35 | Serial.begin(115200); 36 | Serial.println("ESP32-Arduino-CAN RTOS Example"); 37 | 38 | /* initialize and start, use pin 5 as CAN_tx and pin 4 as CAN_rx, CAN bus is set to 500kbps */ 39 | ESP32Can.CANInit(GPIO_NUM_5, GPIO_NUM_4, ESP32CAN_SPEED_500KBPS); 40 | 41 | /* setup can send RTOS task */ 42 | xTaskCreatePinnedToCore(canSend, /* callback function */ 43 | "CAN TX", /* name of task */ 44 | 1024, /* stack size (bytes in ESP32, words in FreeRTOS */ 45 | NULL, /* parameter to pass to function */ 46 | CAN_TX_PRIORITY, /* task priority (0 to configMAX_PRIORITES - 1 */ 47 | &canTxTask, /* task handle */ 48 | 1); /* CPU core, Arduino runs on 1 */ 49 | 50 | /* setup can receive RTOS task */ 51 | xTaskCreatePinnedToCore(canReceive, /* callback function */ 52 | "CAN RX", /* name of task */ 53 | 2048, /* stack size (bytes in ESP32, words in FreeRTOS */ 54 | NULL, /* parameter to pass to function */ 55 | CAN_RX_PRIORITY, /* task priority (0 to configMAX_PRIORITES - 1 */ 56 | &canRxTask, /* task handle */ 57 | 1); /* CPU core, Arduino runs on 1 */ 58 | 59 | 60 | /* setup can send frame */ 61 | tx_frame.extd = 0; /* standard 11bit ID */ 62 | tx_frame.identifier = 0x123; /* CAN ID */ 63 | tx_frame.data_length_code = 8; /* 8 bytes of data */ 64 | for (int8_t i= 0; i<8; i++) { 65 | tx_frame.data[i] = 0xFF; /* pad frame with 0xFF */ 66 | } 67 | } 68 | 69 | void loop() { 70 | /* place time in seconds into first two bytes of dataframe */ 71 | uint16_t time = millis()/1000; 72 | tx_frame.data[0] = highByte(time); 73 | tx_frame.data[1] = lowByte(time); 74 | 75 | delay(500); 76 | } 77 | 78 | /* CAN Send function called by RTOS can send task */ 79 | void canSend(void *pvParameters) { 80 | TickType_t xLastWakeTime; /* keep track of last time can message was sent */ 81 | TickType_t xFrequency = CAN_TX_RATE_ms / portTICK_PERIOD_MS; /* set the transmit frequency */ 82 | 83 | /* this task will run forever at frequency set above 84 | * to stop this task from running call vTaskSuspend(canTxTask) in the main loop */ 85 | for (;;) { 86 | ESP32Can.CANWriteFrame(&tx_frame); /* send dataframe */ 87 | 88 | vTaskDelayUntil(&xLastWakeTime, xFrequency); /* do something else until it is time to send again */ 89 | /* the above delay function was used since it specifies an absolute wake time. 90 | * Make sure the code in the forever for loop can run faster then desired send frequency or this task will take all of the CPU time available */ 91 | } 92 | } 93 | 94 | void canReceive(void *pvParameters) { 95 | const TickType_t xDelay = CAN_RX_RATE_ms / portTICK_PERIOD_MS; 96 | twai_message_t rx_frame; 97 | 98 | for (;;) { 99 | if (ESP32CAN_OK == ESP32Can.CANReadFrame(&rx_frame)) { /* only print when CAN message is received*/ 100 | Serial.print(rx_frame.identifier, HEX); /* print the CAN ID*/ 101 | Serial.print(" "); 102 | Serial.print(rx_frame.data_length_code); /* print number of bytes in data frame*/ 103 | 104 | for (int i=0; i 11 | #include 12 | 13 | void setup() { 14 | Serial.begin(115200); 15 | Serial.println("ESP32-Arduino-CAN Send Basic Demo"); 16 | 17 | /* initialize and start, use pin 5 as CAN_tx and pin 4 as CAN_rx, CAN bus is set to 500kbps */ 18 | ESP32Can.CANInit(GPIO_NUM_5, GPIO_NUM_4, ESP32CAN_SPEED_500KBPS ); 19 | } 20 | 21 | void loop() { 22 | twai_message_t tx_frame; 23 | 24 | tx_frame.extd = 0; /* CAN ID is standard 11bit, for 29bit set to 1*/ 25 | tx_frame.data_length_code = 8; /* send 8 bytes of data */ 26 | tx_frame.identifier = 0x123; /* CAN id is 0x123 */ 27 | 28 | /* assemble the 8 bytes of data */ 29 | tx_frame.data[0] = 0xDE; 30 | tx_frame.data[1] = 0xAD; 31 | tx_frame.data[2] = 0xBE; 32 | tx_frame.data[3] = 0xEF; 33 | tx_frame.data[4] = 0xBA; 34 | tx_frame.data[5] = 0x5E; 35 | tx_frame.data[6] = 0xBA; 36 | tx_frame.data[7] = 0x11; 37 | 38 | ESP32Can.CANWriteFrame(&tx_frame); /* send the CAN message */ 39 | 40 | Serial.println("CAN Frame Sent"); 41 | 42 | /* delay before sending another CAN message*/ 43 | delay(1000); 44 | } 45 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map ESP32CAN 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | ESP32CAN KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | CANInit KEYWORD2 15 | CANWriteFrame KEYWORD2 16 | CANStop KEYWORD2 17 | CANConfigFilter KEYWORD2 18 | ####################################### 19 | # Constants (LITERAL1) 20 | ####################################### 21 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP32CAN 2 | version=0.0.1 3 | author=Michael Wagner 4 | maintainer=https://github.com/miwagner 5 | sentence=ESP32-Arduino-CAN 6 | paragraph=ESP32-Arduino-CAN 7 | category=Device Control 8 | url=https://github.com/miwagner/ESP32-Arduino-CAN 9 | architectures=esp32 10 | -------------------------------------------------------------------------------- /src/ESP32CAN.cpp: -------------------------------------------------------------------------------- 1 | #include "ESP32CAN.h" 2 | 3 | ESP32CAN_status_t ESP32CAN::CANInit(gpio_num_t tx_pin, gpio_num_t rx_pin, ESP32CAN_timing_t baud) { 4 | /* initialize configuration structures */ 5 | twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(tx_pin, rx_pin, TWAI_MODE_NORMAL); 6 | twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); 7 | 8 | twai_timing_config_t t_config; 9 | 10 | switch (baud) { 11 | case ESP32CAN_SPEED_100KBPS: 12 | t_config = TWAI_TIMING_CONFIG_100KBITS(); 13 | break; 14 | case ESP32CAN_SPEED_125KBPS: 15 | t_config = TWAI_TIMING_CONFIG_125KBITS(); 16 | break; 17 | case ESP32CAN_SPEED_250KBPS: 18 | t_config = TWAI_TIMING_CONFIG_250KBITS(); 19 | break; 20 | case ESP32CAN_SPEED_500KBPS: 21 | t_config = TWAI_TIMING_CONFIG_500KBITS(); 22 | break; 23 | case ESP32CAN_SPEED_800KBPS: 24 | t_config = TWAI_TIMING_CONFIG_800KBITS(); 25 | break; 26 | case ESP32CAN_SPEED_1MBPS: 27 | t_config = TWAI_TIMING_CONFIG_1MBITS(); 28 | break; 29 | default: 30 | debugPrintln("TWAI: undefined buad rate"); 31 | return ESP32CAN_NOK; 32 | break; 33 | } 34 | 35 | /* install TWAI driver */ 36 | switch (twai_driver_install(&g_config, &t_config, &f_config)) { 37 | case ESP_OK: 38 | debugPrintln("TWAI INSTALL: ok"); 39 | break; 40 | case ESP_ERR_INVALID_ARG: 41 | debugPrintln("TWAI INSTALL: ESP_ERR_INVALID_ARG"); 42 | return ESP32CAN_NOK; 43 | break; 44 | case ESP_ERR_NO_MEM: 45 | debugPrintln("TWAI INSTALL: ESP_ERR_NO_MEM"); 46 | return ESP32CAN_NOK; 47 | break; 48 | case ESP_ERR_INVALID_STATE: 49 | debugPrintln("TWAI INSTALL: ESP_ERR_INVALID_STATE"); 50 | return ESP32CAN_NOK; 51 | break; 52 | default: 53 | debugPrintln("TWAI INSTALL: uknown error"); 54 | return ESP32CAN_NOK; 55 | break; 56 | } 57 | 58 | /* start TWAI driver */ 59 | switch (twai_start()) { 60 | case ESP_OK: 61 | debugPrintln("TWAI START: ok"); 62 | break; 63 | case ESP_ERR_INVALID_STATE: 64 | debugPrintln("TWAI START: ESP_ERR_INVALID_STATE"); 65 | return ESP32CAN_NOK; 66 | break; 67 | default: 68 | debugPrintln("TWAI START: uknown error"); 69 | return ESP32CAN_NOK; 70 | break; 71 | } 72 | 73 | return ESP32CAN_OK; 74 | } 75 | 76 | ESP32CAN_status_t ESP32CAN::CANStop() { 77 | /* stop the TWAI driver */ 78 | switch (twai_stop()) { 79 | case ESP_OK: 80 | debugPrintln("TWAI STOP: ok"); 81 | break; 82 | case ESP_ERR_INVALID_STATE: 83 | debugPrintln("TWAI STOP: ESP_ERR_INVALID_STATE"); 84 | return ESP32CAN_NOK; 85 | break; 86 | default: 87 | debugPrintln("TWAI STOP: unknow error"); 88 | return ESP32CAN_NOK; 89 | break; 90 | } 91 | 92 | /* uninstall TWAI driver */ 93 | switch (twai_driver_uninstall()) { 94 | case ESP_OK: 95 | debugPrintln("TWAI UNINSTALL: ok"); 96 | break; 97 | case ESP_ERR_INVALID_STATE: 98 | debugPrintln("TWAI UNINSTALL: ESP_ERR_INVALID_STATE"); 99 | return ESP32CAN_NOK; 100 | break; 101 | default: 102 | break; 103 | } 104 | 105 | return ESP32CAN_OK; 106 | } 107 | 108 | ESP32CAN_status_t ESP32CAN::CANWriteFrame(const twai_message_t* p_frame) { 109 | /* queue message for transmission */ 110 | switch (twai_transmit(p_frame, pdMS_TO_TICKS(10))) { 111 | case ESP_OK: 112 | break; 113 | case ESP_ERR_INVALID_ARG: 114 | debugPrintln("TWAI TX: ESP_ERR_INVALID_ARG"); 115 | return ESP32CAN_NOK; 116 | break; 117 | case ESP_ERR_TIMEOUT: 118 | debugPrintln("TWAI TX: ESP_ERR_TIMEOUT"); 119 | return ESP32CAN_NOK; 120 | break; 121 | case ESP_FAIL: 122 | debugPrintln("TWAI TX: ESP_FAIL"); 123 | return ESP32CAN_NOK; 124 | break; 125 | case ESP_ERR_INVALID_STATE: 126 | debugPrintln("TWAI TX: ESP_ERR_INVALID_STATE"); 127 | return ESP32CAN_NOK; 128 | break; 129 | case ESP_ERR_NOT_SUPPORTED: 130 | debugPrintln("TWAI TX: ESP_ERR_NOT_SUPPORTED"); 131 | return ESP32CAN_NOK; 132 | break; 133 | default: 134 | debugPrintln("TWAI TX: unknow error"); 135 | return ESP32CAN_NOK; 136 | break; 137 | } 138 | 139 | return ESP32CAN_OK; 140 | } 141 | 142 | ESP32CAN_status_t ESP32CAN::CANReadFrame(twai_message_t* p_frame) { 143 | switch (twai_receive(p_frame, pdMS_TO_TICKS(10))) { 144 | case ESP_OK: 145 | break; 146 | case ESP_ERR_TIMEOUT: 147 | debugPrintln("TWAI RX: ESP_ERR_TIMEOUT"); 148 | return ESP32CAN_NOK; 149 | break; 150 | case ESP_ERR_INVALID_ARG: 151 | debugPrintln("TWAI RX: ESP_ERR_INVALID_ARG"); 152 | return ESP32CAN_NOK; 153 | break; 154 | case ESP_ERR_INVALID_STATE: 155 | debugPrintln("TWAI RX: ESP_ERR_INVALID_STATE"); 156 | return ESP32CAN_NOK; 157 | break; 158 | default: 159 | debugPrintln("TWAI RX: unknow error"); 160 | return ESP32CAN_NOK; 161 | break; 162 | } 163 | 164 | return ESP32CAN_OK; 165 | } 166 | 167 | // int ESP32CAN::CANConfigFilter(const CAN_filter_t* p_filter) 168 | // { 169 | // return CAN_config_filter(p_filter); 170 | // } 171 | 172 | ESP32CAN ESP32Can; 173 | -------------------------------------------------------------------------------- /src/ESP32CAN.h: -------------------------------------------------------------------------------- 1 | #ifndef INC_ESP32CAN_H 2 | #define INC_ESP32CAN_H 3 | 4 | #include 5 | #include "driver/gpio.h" 6 | #include "driver/twai.h" 7 | 8 | /* Defines ------------------------------------------------------------------- */ 9 | #define ESP32CAN_DEBUG /* serial print debug info */ 10 | 11 | /* Macros -------------------------------------------------------------------- */ 12 | #ifdef ESP32CAN_DEBUG 13 | #define debugPrint(x) Serial.print(x) 14 | #define debugPrintln(x) Serial.println(x) 15 | #else 16 | #define debugPrint(x) 17 | #define debugPrintln(x) 18 | #endif 19 | 20 | /* typedef ------------------------------------------------------------------- */ 21 | typedef enum { 22 | ESP32CAN_NOK = 0, /* not ok, something is wrong */ 23 | ESP32CAN_OK = 1 /* ok, all seems well */ 24 | } ESP32CAN_status_t; 25 | 26 | typedef enum { 27 | ESP32CAN_SPEED_100KBPS = 100, 28 | ESP32CAN_SPEED_125KBPS = 125, 29 | ESP32CAN_SPEED_250KBPS = 250, 30 | ESP32CAN_SPEED_500KBPS = 500, 31 | ESP32CAN_SPEED_800KBPS = 800, 32 | ESP32CAN_SPEED_1MBPS = 1000, 33 | } ESP32CAN_timing_t; 34 | 35 | /* Globals ------------------------------------------------------------------- */ 36 | 37 | /* Function Prototypes ------------------------------------------------------- */ 38 | 39 | /* Class --------------------------------------------------------------------- */ 40 | class ESP32CAN { 41 | public: 42 | ESP32CAN_status_t CANInit(gpio_num_t tx_pin, gpio_num_t rx_pin, ESP32CAN_timing_t baud); 43 | ESP32CAN_status_t CANStop(); 44 | ESP32CAN_status_t CANWriteFrame(const twai_message_t* p_frame); 45 | ESP32CAN_status_t CANReadFrame(twai_message_t* p_frame); 46 | 47 | // int CANConfigFilter(const CAN_filter_t* p_filter); 48 | 49 | private: 50 | 51 | }; 52 | 53 | /* External Globals ---------------------------------------------------------- */ 54 | extern ESP32CAN ESP32Can; 55 | 56 | #endif 57 | --------------------------------------------------------------------------------