ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
canbus.cpp
Go to the documentation of this file.
1#include "canbus.h"
2#include <algorithm>
4#include "esphome/core/log.h"
5
6namespace esphome::canbus {
7
8ESPHOME_LOG_TAG(TAG, "canbus");
9
11 if (!this->setup_internal()) {
12 ESP_LOGE(TAG, "setup error!");
13 this->mark_failed();
14 }
15}
16
18 if (this->use_extended_id_) {
19 ESP_LOGCONFIG(TAG, "config extended id=0x%08" PRIx32, this->can_id_);
20 } else {
21 ESP_LOGCONFIG(TAG, "config standard id=0x%03" PRIx32, this->can_id_);
22 }
23}
24
25canbus::Error Canbus::send_data(uint32_t can_id, bool use_extended_id, bool remote_transmission_request,
26 const uint8_t *data, size_t len) {
27 struct CanFrame can_message;
28
29 // Clamp before narrowing, so a lambda payload of 256 bytes or more cannot wrap to a short length
30 const uint8_t size = static_cast<uint8_t>(std::min<size_t>(len, CAN_MAX_DATA_LENGTH));
31 if (use_extended_id) {
32 ESP_LOGD(TAG, "send extended id=0x%08" PRIx32 " rtr=%s size=%d", can_id, TRUEFALSE(remote_transmission_request),
33 size);
34 } else {
35 ESP_LOGD(TAG, "send standard id=0x%03" PRIx32 " rtr=%s size=%d", can_id, TRUEFALSE(remote_transmission_request),
36 size);
37 }
38 can_message.can_data_length_code = size;
39 can_message.can_id = can_id;
40 can_message.use_extended_id = use_extended_id;
41 can_message.remote_transmission_request = remote_transmission_request;
42
43 for (int i = 0; i < size; i++) {
44 can_message.data[i] = data[i];
45 ESP_LOGVV(TAG, " data[%d]=%02x", i, can_message.data[i]);
46 }
47
48 canbus::Error error = this->send_message(&can_message);
49 if (error != canbus::ERROR_OK) {
50 if (use_extended_id) {
51 ESP_LOGW(TAG, "send to extended id=0x%08" PRIx32 " failed with error %d!", can_id, error);
52 } else {
53 ESP_LOGW(TAG, "send to standard id=0x%03" PRIx32 " failed with error %d!", can_id, error);
54 }
55 }
56 return error;
57}
58
60 if (trigger->use_extended_id_) {
61 ESP_LOGVV(TAG, "add trigger for extended canid=0x%08" PRIx32, trigger->can_id_);
62 } else {
63 ESP_LOGVV(TAG, "add trigger for std canid=0x%03" PRIx32, trigger->can_id_);
64 }
65 this->triggers_.push_back(trigger);
66};
67
71 this->log_events_(this->get_events(), now);
72 this->last_event_check_time_ = now;
73 }
74
75 struct CanFrame can_message;
76 uint32_t message_counter = 0;
77 for (; message_counter < this->max_frames_per_loop_; message_counter++) {
78 if (this->read_message(&can_message) != canbus::ERROR_OK) {
79 break;
80 }
81
82 if (can_message.use_extended_id) {
83 ESP_LOGD(TAG, "received can message (#%" PRIu32 ") extended can_id=0x%" PRIx32 " size=%d", message_counter + 1,
84 can_message.can_id, can_message.can_data_length_code);
85 } else {
86 ESP_LOGD(TAG, "received can message (#%" PRIu32 ") std can_id=0x%" PRIx32 " size=%d", message_counter + 1,
87 can_message.can_id, can_message.can_data_length_code);
88 }
89
90 std::vector<uint8_t> data;
91
92 // show data received
93 for (int i = 0; i < std::min(can_message.can_data_length_code, CAN_MAX_DATA_LENGTH); i++) {
94 ESP_LOGV(TAG, " can_message.data[%d]=%02x", i, can_message.data[i]);
95 data.push_back(can_message.data[i]);
96 }
97
98 this->callback_manager_(can_message.can_id, can_message.use_extended_id, can_message.remote_transmission_request,
99 data);
100
101 // fire all triggers
102 for (auto *trigger : this->triggers_) {
103 if ((trigger->can_id_ == (can_message.can_id & trigger->can_id_mask_)) &&
104 (trigger->use_extended_id_ == can_message.use_extended_id) &&
105 (!trigger->remote_transmission_request_.has_value() ||
106 trigger->remote_transmission_request_.value() == can_message.remote_transmission_request)) {
107 trigger->trigger(data, can_message.can_id, can_message.remote_transmission_request);
108 }
109 }
110 }
111
112 if (message_counter == this->max_frames_per_loop_) {
113 ESP_LOGW(TAG,
114 "Reached max_frames_per_loop=%" PRIu32 ", deferring remaining CAN frames. Some frames might be dropped",
115 this->max_frames_per_loop_);
116 }
117}
118
120 // Bus-off gets its own handling because automatic recovery can make it flap
122 this->last_bus_off_time_ = now;
123 if (!this->bus_off_) {
124 this->bus_off_ = true;
125 this->status_set_warning(LOG_STR("Bus off"));
126 }
127 } else if (this->bus_off_) {
129 // No bus-off event for the hold-off time, so ask whether it is really over
130 if (this->get_status().bus_off) {
131 this->last_bus_off_time_ = now;
132 } else {
133 this->bus_off_ = false;
134 this->status_clear_warning();
135 // Events gathered while bus-off say nothing about the recovered bus
136 this->events_to_log_ = 0;
137 }
138 }
139 } else {
140 this->events_to_log_ |= events;
141 if (this->events_to_log_ != 0 && (now - this->last_event_log_time_) >= EVENT_LOG_THROTTLE_MS) {
143 bool error_passive = this->events_to_log_ & CanEventFlags::CAN_EVENT_PASSIVE;
144 if (error_passive && (this->events_to_log_ & CanEventFlags::CAN_EVENT_ACTIVE)) {
145 // Both edges within one throttle window, so the counters decide the current state
146 auto status = this->get_status();
147 error_passive = status.rx_error_counter >= CAN_ERROR_PASSIVE_THRESHOLD ||
148 status.tx_error_counter >= CAN_ERROR_PASSIVE_THRESHOLD;
149 }
150 ESP_LOGW(TAG, "entered %s", error_passive ? LOG_STR_LITERAL("error-passive") : LOG_STR_LITERAL("error-active"));
151 }
153 ESP_LOGD(TAG, "receive buffer overrun");
154 }
155 this->events_to_log_ = 0;
156 this->last_event_log_time_ = now;
157 }
158 }
159
160#ifdef ESPHOME_LOG_HAS_VERBOSE
161 if ((now - this->last_state_log_time_) >= STATE_LOG_INTERVAL_MS) {
162 auto status = this->get_status();
163 ESP_LOGV(TAG, "Status: bus_off %d, tx_err %d, rx_err %d", status.bus_off, status.tx_error_counter,
164 status.rx_error_counter);
165 this->last_state_log_time_ = now;
166 }
167#endif
168}
169
170} // namespace esphome::canbus
uint8_t status
Definition bl0942.h:8
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void mark_failed()
Mark this component as failed.
void status_clear_warning()
Definition component.h:296
virtual CanStatus get_status()
Definition canbus.h:108
CallbackManager< void(uint32_t can_id, bool extended_id, bool rtr, const std::vector< uint8_t > &data)> callback_manager_
Definition canbus.h:133
void loop() override
Definition canbus.cpp:68
static constexpr uint32_t EVENT_CHECK_INTERVAL_MS
Definition canbus.h:136
void log_events_(CanEventFlags events, uint32_t now)
Definition canbus.cpp:119
static constexpr uint32_t STATE_LOG_INTERVAL_MS
Definition canbus.h:148
canbus::Error send_data(uint32_t can_id, bool use_extended_id, bool remote_transmission_request, const uint8_t *data, size_t len)
Definition canbus.cpp:25
void setup() override
Definition canbus.cpp:10
void add_trigger(CanbusTrigger *trigger)
Definition canbus.cpp:59
virtual CanEventFlags get_events()
Definition canbus.h:156
uint32_t last_state_log_time_
Definition canbus.h:149
uint32_t max_frames_per_loop_
Definition canbus.h:127
uint32_t last_event_log_time_
Definition canbus.h:141
static constexpr uint32_t EVENT_LOG_THROTTLE_MS
Definition canbus.h:140
void dump_config() override
Definition canbus.cpp:17
static constexpr uint32_t EVENT_LOG_BUS_OFF_HOLDOFF_MS
Definition canbus.h:144
virtual Error send_message(struct CanFrame *frame)=0
uint32_t last_bus_off_time_
Definition canbus.h:145
virtual bool setup_internal()=0
std::vector< CanbusTrigger * > triggers_
Definition canbus.h:125
uint32_t last_event_check_time_
Definition canbus.h:137
virtual Error read_message(struct CanFrame *frame)=0
ESPHOME_LOG_TAG(TAG, "canbus")
@ CAN_EVENT_PASSIVE
Definition canbus.h:67
@ CAN_EVENT_BUS_OFF
Definition canbus.h:69
@ CAN_EVENT_RX_QUEUE_FULL
Definition canbus.h:70
const void size_t len
Definition hal.h:68
uint16_t size
Definition helpers.cpp:34
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t
uint8_t can_data_length_code
Definition canbus.h:62