diff --git a/esphome/components/canbus/__init__.py b/esphome/components/canbus/__init__.py index cb3634917e..6867177795 100644 --- a/esphome/components/canbus/__init__.py +++ b/esphome/components/canbus/__init__.py @@ -1,3 +1,4 @@ +import re from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv @@ -68,6 +69,16 @@ CAN_SPEEDS = { "800KBPS": CanSpeed.CAN_800KBPS, "1000KBPS": CanSpeed.CAN_1000KBPS, } + + +def get_rate(value): + match = re.match(r"(\d+)(?:K(\d+)?)?BPS", value, re.IGNORECASE) + if not match: + raise ValueError(f"Invalid rate format: {value}") + fraction = match.group(2) or "0" + return int((float(match.group(1)) + float(f"0.{fraction}")) * 1000) + + CANBUS_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(CanbusComponent), diff --git a/esphome/components/canbus/canbus.cpp b/esphome/components/canbus/canbus.cpp index 73b86cba87..696cfff2b7 100644 --- a/esphome/components/canbus/canbus.cpp +++ b/esphome/components/canbus/canbus.cpp @@ -46,7 +46,13 @@ void Canbus::send_data(uint32_t can_id, bool use_extended_id, bool remote_transm ESP_LOGVV(TAG, " data[%d]=%02x", i, can_message.data[i]); } - this->send_message(&can_message); + if (this->send_message(&can_message) != canbus::ERROR_OK) { + if (use_extended_id) { + ESP_LOGW(TAG, "send to extended id=0x%08" PRIx32 " failed!", can_id); + } else { + ESP_LOGW(TAG, "send to standard id=0x%03" PRIx32 " failed!", can_id); + } + } } void Canbus::add_trigger(CanbusTrigger *trigger) { diff --git a/esphome/components/esp32_can/canbus.py b/esphome/components/esp32_can/canbus.py index 37bdfa3962..dfa98b2eff 100644 --- a/esphome/components/esp32_can/canbus.py +++ b/esphome/components/esp32_can/canbus.py @@ -1,3 +1,5 @@ +import math + from esphome import pins import esphome.codegen as cg from esphome.components import canbus @@ -23,6 +25,8 @@ from esphome.const import ( CODEOWNERS = ["@Sympatron"] DEPENDENCIES = ["esp32"] +CONF_TX_ENQUEUE_TIMEOUT = "tx_enqueue_timeout" + esp32_can_ns = cg.esphome_ns.namespace("esp32_can") esp32_can = esp32_can_ns.class_("ESP32Can", CanbusComponent) @@ -84,10 +88,20 @@ CONFIG_SCHEMA = canbus.CANBUS_SCHEMA.extend( cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_RX_QUEUE_LEN): cv.uint32_t, cv.Optional(CONF_TX_QUEUE_LEN): cv.uint32_t, + cv.Optional(CONF_TX_ENQUEUE_TIMEOUT): cv.positive_time_period_milliseconds, } ) +def get_default_tx_enqueue_timeout(bit_rate): + bit_rate_numeric = canbus.get_rate(bit_rate) + bits_per_packet = 140 # ~max CAN message length + ms_per_packet = bits_per_packet / bit_rate_numeric * 1000 + return int( + max(min(math.ceil(10 * ms_per_packet), 1000), 1) + ) # ~10 packet lengths, min 1ms, max 1000ms + + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await canbus.register_canbus(var, config) @@ -98,3 +112,10 @@ async def to_code(config): cg.add(var.set_rx_queue_len(rx_queue_len)) if (tx_queue_len := config.get(CONF_TX_QUEUE_LEN)) is not None: cg.add(var.set_tx_queue_len(tx_queue_len)) + + if CONF_TX_ENQUEUE_TIMEOUT in config: + tx_enqueue_timeout_ms = config[CONF_TX_ENQUEUE_TIMEOUT].total_milliseconds + else: + tx_enqueue_timeout_ms = get_default_tx_enqueue_timeout(config[CONF_BIT_RATE]) + + cg.add(var.set_tx_enqueue_timeout_ms(tx_enqueue_timeout_ms)) diff --git a/esphome/components/esp32_can/esp32_can.cpp b/esphome/components/esp32_can/esp32_can.cpp index 5a45859b1f..a40f493075 100644 --- a/esphome/components/esp32_can/esp32_can.cpp +++ b/esphome/components/esp32_can/esp32_can.cpp @@ -124,7 +124,7 @@ canbus::Error ESP32Can::send_message(struct canbus::CanFrame *frame) { memcpy(message.data, frame->data, frame->can_data_length_code); } - if (twai_transmit(&message, pdMS_TO_TICKS(1000)) == ESP_OK) { + if (twai_transmit(&message, this->tx_enqueue_timeout_ticks_) == ESP_OK) { return canbus::ERROR_OK; } else { return canbus::ERROR_ALLTXBUSY; diff --git a/esphome/components/esp32_can/esp32_can.h b/esphome/components/esp32_can/esp32_can.h index b3086f9a48..416f037083 100644 --- a/esphome/components/esp32_can/esp32_can.h +++ b/esphome/components/esp32_can/esp32_can.h @@ -14,6 +14,9 @@ class ESP32Can : public canbus::Canbus { void set_tx(int tx) { tx_ = tx; } void set_tx_queue_len(uint32_t tx_queue_len) { this->tx_queue_len_ = tx_queue_len; } void set_rx_queue_len(uint32_t rx_queue_len) { this->rx_queue_len_ = rx_queue_len; } + void set_tx_enqueue_timeout_ms(uint32_t tx_enqueue_timeout_ms) { + this->tx_enqueue_timeout_ticks_ = pdMS_TO_TICKS(tx_enqueue_timeout_ms); + } ESP32Can(){}; protected: @@ -23,6 +26,7 @@ class ESP32Can : public canbus::Canbus { int rx_{-1}; int tx_{-1}; + TickType_t tx_enqueue_timeout_ticks_{}; optional<uint32_t> tx_queue_len_{}; optional<uint32_t> rx_queue_len_{}; };