ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
esp32_can.cpp
Go to the documentation of this file.
1#ifdef USE_ESP32
2#include "esp32_can.h"
3#include "esphome/core/log.h"
4
5#include <driver/twai.h>
6
7// WORKAROUND, because CAN_IO_UNUSED is just defined as (-1) in this version
8// of the framework which does not work with -fpermissive
9#undef CAN_IO_UNUSED
10#define CAN_IO_UNUSED (static_cast<gpio_num_t>(-1))
11
13
14ESPHOME_LOG_TAG(TAG, "esp32_can");
15
16static bool get_bitrate(canbus::CanSpeed bitrate, twai_timing_config_t *t_config) {
17 switch (bitrate) {
18#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32C61) || \
19 defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || \
20 defined(USE_ESP32_VARIANT_ESP32S3)
22 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_1KBITS();
23 return true;
25 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_5KBITS();
26 return true;
28 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_10KBITS();
29 return true;
31 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_12_5KBITS();
32 return true;
34 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_16KBITS();
35 return true;
36#endif
37#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32C61) || \
38 defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || \
39 defined(USE_ESP32_VARIANT_ESP32S3) || (defined(USE_ESP32_VARIANT_ESP32) && CONFIG_ESP32_REV_MIN_FULL >= 200)
41 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_20KBITS();
42 return true;
43#endif
45 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_25KBITS();
46 return true;
48 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_50KBITS();
49 return true;
51 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_100KBITS();
52 return true;
54 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_125KBITS();
55 return true;
57 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_250KBITS();
58 return true;
60 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_500KBITS();
61 return true;
63 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_800KBITS();
64 return true;
66 *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_1MBITS();
67 return true;
68 default:
69 return false;
70 }
71}
72
74 static int next_twai_ctrl_num = 0;
75 if (static_cast<unsigned>(next_twai_ctrl_num) >= SOC_TWAI_CONTROLLER_NUM) {
76 ESP_LOGW(TAG, "Maximum number of esp32_can components created already");
77 this->mark_failed();
78 return false;
79 }
80
81 // Select TWAI mode based on configuration
82 twai_mode_t twai_mode = TWAI_MODE_NORMAL;
83 if (this->mode_ == CAN_MODE_LISTEN_ONLY) {
84 twai_mode = TWAI_MODE_LISTEN_ONLY;
85 ESP_LOGI(TAG, "CAN bus configured in LISTEN_ONLY mode (passive, no ACKs)");
86 } else if (this->mode_ == CAN_MODE_NO_ACK) {
87 twai_mode = TWAI_MODE_NO_ACK;
88 ESP_LOGI(TAG, "CAN bus configured in NO_ACK mode (transmit without requiring ACK)");
89 }
90
91 twai_general_config_t g_config =
92 TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t) this->tx_, (gpio_num_t) this->rx_, twai_mode);
93 g_config.controller_id = next_twai_ctrl_num++;
94 g_config.alerts_enabled = TWAI_ALERT_ERR_PASS | TWAI_ALERT_ERR_ACTIVE | TWAI_ALERT_BUS_OFF |
95 TWAI_ALERT_BUS_RECOVERED | TWAI_ALERT_RX_QUEUE_FULL;
96 if (this->tx_queue_len_.has_value()) {
97 g_config.tx_queue_len = this->tx_queue_len_.value();
98 }
99 if (this->rx_queue_len_.has_value()) {
100 g_config.rx_queue_len = this->rx_queue_len_.value();
101 }
102
103 twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
104 twai_timing_config_t t_config;
105
106 if (!get_bitrate(this->bit_rate_, &t_config)) {
107 // invalid bit rate
108 this->mark_failed();
109 return false;
110 }
111
112 // Install TWAI driver
113 if (twai_driver_install_v2(&g_config, &t_config, &f_config, &(this->twai_handle_)) != ESP_OK) {
114 // Failed to install driver
115 this->mark_failed();
116 return false;
117 }
118
119 // Start TWAI driver
120 if (twai_start_v2(this->twai_handle_) != ESP_OK) {
121 // Failed to start driver
122 this->mark_failed();
123 return false;
124 }
125 return true;
126}
127
129 if (this->twai_handle_ == nullptr) {
130 return {}; // not set up yet, or setup failed
131 }
132
133 uint32_t alerts;
134 esp_err_t err = twai_read_alerts_v2(this->twai_handle_, &alerts, 0);
135 if (err != ESP_OK) {
136 if (err != ESP_ERR_TIMEOUT) {
137 ESP_LOGD(TAG, "failed to get CAN events");
138 }
139 alerts = 0;
140 }
141
142 uint8_t events = 0;
143 if (alerts & TWAI_ALERT_ERR_PASS) {
145 }
146 if (alerts & TWAI_ALERT_ERR_ACTIVE) {
147 events |= canbus::CAN_EVENT_ACTIVE;
148 }
149 if (alerts & TWAI_ALERT_BUS_OFF) {
151 // Start recovery at once, as the MCP2515 does in hardware
152 this->recovery_pending_ = true;
153 }
154 if (alerts & TWAI_ALERT_BUS_RECOVERED) {
155 // Recovery leaves the controller stopped
156 this->restart_pending_ = true;
157 }
158 if (this->recovery_pending_ || this->restart_pending_) {
160 }
161 if (alerts & TWAI_ALERT_RX_QUEUE_FULL) {
163 }
164 return static_cast<canbus::CanEventFlags>(events);
165}
166
168 // These only fail with an invalid handle or outside the expected state; retry on the next pass
169 if (this->recovery_pending_) {
170 if (twai_initiate_recovery_v2(this->twai_handle_) != ESP_OK) {
171 this->status_set_error(LOG_STR("Recovery after bus off failed"));
172 return;
173 }
174 this->recovery_pending_ = false;
175 }
176 if (this->restart_pending_) {
177 if (twai_start_v2(this->twai_handle_) != ESP_OK) {
178 this->status_set_error(LOG_STR("Restart after bus off failed"));
179 return;
180 }
181 this->restart_pending_ = false;
182 }
183 this->status_clear_error();
184}
185
188
189 twai_status_info_t twai_status;
190
191 if (this->twai_handle_ != nullptr && twai_get_status_info_v2(this->twai_handle_, &twai_status) == ESP_OK) {
192 status.bus_off = (twai_status.state == TWAI_STATE_BUS_OFF) || (twai_status.state == TWAI_STATE_RECOVERING);
193 status.rx_error_counter = twai_status.rx_error_counter;
194 status.tx_error_counter = twai_status.tx_error_counter;
195
196 status.tx_failed_count = twai_status.tx_failed_count;
197 status.rx_missed_count = twai_status.rx_missed_count;
198 status.rx_overrun_count = twai_status.rx_overrun_count;
199 status.arb_lost_count = twai_status.arb_lost_count;
200 status.bus_error_count = twai_status.bus_error_count;
201 } else {
202 ESP_LOGD(TAG, "failed to get CAN status");
203 }
204
205 return status;
206}
207
209 // In listen-only mode, we cannot transmit
210 if (this->mode_ == CAN_MODE_LISTEN_ONLY) {
211 ESP_LOGW(TAG, "Cannot send messages in LISTEN_ONLY mode");
212 return canbus::ERROR_FAIL;
213 }
214
215 if (this->twai_handle_ == nullptr) {
216 // not setup yet or setup failed
217 return canbus::ERROR_FAIL;
218 }
219
220 if (frame->can_data_length_code > canbus::CAN_MAX_DATA_LENGTH) {
222 }
223
224 uint32_t flags = TWAI_MSG_FLAG_NONE;
225 if (frame->use_extended_id) {
226 flags |= TWAI_MSG_FLAG_EXTD;
227 }
228 if (frame->remote_transmission_request) {
229 flags |= TWAI_MSG_FLAG_RTR;
230 }
231
232 twai_message_t message = {
233 .flags = flags,
234 .identifier = frame->can_id,
235 .data_length_code = frame->can_data_length_code,
236 .data = {}, // to suppress warning, data is initialized properly below
237 };
238 if (!frame->remote_transmission_request) {
239 memcpy(message.data, frame->data, frame->can_data_length_code);
240 }
241
242 if (twai_transmit_v2(this->twai_handle_, &message, this->tx_enqueue_timeout_ticks_) == ESP_OK) {
243 return canbus::ERROR_OK;
244 } else {
246 }
247}
248
250 if (this->twai_handle_ == nullptr) {
251 // not setup yet or setup failed
252 return canbus::ERROR_FAIL;
253 }
254
255 twai_message_t message;
256
257 if (twai_receive_v2(this->twai_handle_, &message, 0) != ESP_OK) {
258 return canbus::ERROR_NOMSG;
259 }
260
261 frame->can_id = message.identifier;
262 frame->use_extended_id = message.flags & TWAI_MSG_FLAG_EXTD;
263 frame->remote_transmission_request = message.flags & TWAI_MSG_FLAG_RTR;
264 frame->can_data_length_code = message.data_length_code;
265
266 if (!frame->remote_transmission_request) {
267 size_t dlc =
268 message.data_length_code < canbus::CAN_MAX_DATA_LENGTH ? message.data_length_code : canbus::CAN_MAX_DATA_LENGTH;
269 memcpy(frame->data, message.data, dlc);
270 }
271
272 return canbus::ERROR_OK;
273}
274
275} // namespace esphome::esp32_can
276
277#endif
uint8_t status
Definition bl0942.h:8
void mark_failed()
Mark this component as failed.
void status_clear_error()
Definition component.h:302
canbus::Error read_message(struct canbus::CanFrame *frame) override
canbus::CanEventFlags get_events() override
optional< uint32_t > rx_queue_len_
Definition esp32_can.h:45
TickType_t tx_enqueue_timeout_ticks_
Definition esp32_can.h:43
optional< uint32_t > tx_queue_len_
Definition esp32_can.h:44
canbus::Error send_message(struct canbus::CanFrame *frame) override
bool setup_internal() override
Definition esp32_can.cpp:73
canbus::CanStatus get_status() override
const LogString * message
Definition component.cpp:35
uint16_t flags
@ CAN_EVENT_PASSIVE
Definition canbus.h:67
@ CAN_EVENT_BUS_OFF
Definition canbus.h:69
@ CAN_EVENT_RX_QUEUE_FULL
Definition canbus.h:70
ESPHOME_LOG_TAG(TAG, "esp32_can")
static void uint32_t
uint8_t can_data_length_code
Definition canbus.h:62