From 5026bc7a780919cb88e510b82acd5883417e730c Mon Sep 17 00:00:00 2001 From: Sympatron GmbH <35803463+Sympatron@users.noreply.github.com> Date: Tue, 11 Jan 2022 19:54:35 +0000 Subject: [PATCH] Native ESP32 CAN support (#1629) Co-authored-by: Guillermo Ruffino Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/esp32_can/__init__.py | 0 esphome/components/esp32_can/canbus.py | 39 +++++++ esphome/components/esp32_can/esp32_can.cpp | 123 +++++++++++++++++++++ esphome/components/esp32_can/esp32_can.h | 29 +++++ tests/test1.yaml | 25 +++++ 6 files changed, 217 insertions(+) create mode 100644 esphome/components/esp32_can/__init__.py create mode 100644 esphome/components/esp32_can/canbus.py create mode 100644 esphome/components/esp32_can/esp32_can.cpp create mode 100644 esphome/components/esp32_can/esp32_can.h diff --git a/CODEOWNERS b/CODEOWNERS index 18694b50ba..351d9f5fc9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -58,6 +58,7 @@ esphome/components/esp32/* @esphome/core esphome/components/esp32_ble/* @jesserockz esphome/components/esp32_ble_server/* @jesserockz esphome/components/esp32_camera_web_server/* @ayufan +esphome/components/esp32_can/* @Sympatron esphome/components/esp32_improv/* @jesserockz esphome/components/esp8266/* @esphome/core esphome/components/exposure_notifications/* @OttoWinter diff --git a/esphome/components/esp32_can/__init__.py b/esphome/components/esp32_can/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/esp32_can/canbus.py b/esphome/components/esp32_can/canbus.py new file mode 100644 index 0000000000..7761418c6a --- /dev/null +++ b/esphome/components/esp32_can/canbus.py @@ -0,0 +1,39 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import canbus +from esphome.const import CONF_ID, CONF_RX_PIN, CONF_TX_PIN +from esphome.components.canbus import CanbusComponent, CanSpeed, CONF_BIT_RATE + +CODEOWNERS = ["@Sympatron"] +DEPENDENCIES = ["esp32"] + +esp32_can_ns = cg.esphome_ns.namespace("esp32_can") +esp32_can = esp32_can_ns.class_("ESP32Can", CanbusComponent) + +# Currently the driver only supports a subset of the bit rates defined in canbus +CAN_SPEEDS = { + "50KBPS": CanSpeed.CAN_50KBPS, + "100KBPS": CanSpeed.CAN_100KBPS, + "125KBPS": CanSpeed.CAN_125KBPS, + "250KBPS": CanSpeed.CAN_250KBPS, + "500KBPS": CanSpeed.CAN_500KBPS, + "1000KBPS": CanSpeed.CAN_1000KBPS, +} + +CONFIG_SCHEMA = canbus.CANBUS_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(esp32_can), + cv.Optional(CONF_BIT_RATE, default="125KBPS"): cv.enum(CAN_SPEEDS, upper=True), + cv.Required(CONF_RX_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_number, + } +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await canbus.register_canbus(var, config) + + cg.add(var.set_rx(config[CONF_RX_PIN])) + cg.add(var.set_tx(config[CONF_TX_PIN])) diff --git a/esphome/components/esp32_can/esp32_can.cpp b/esphome/components/esp32_can/esp32_can.cpp new file mode 100644 index 0000000000..baae683988 --- /dev/null +++ b/esphome/components/esp32_can/esp32_can.cpp @@ -0,0 +1,123 @@ +#ifdef USE_ESP32 +#include "esp32_can.h" +#include "esphome/core/log.h" + +#include + +// WORKAROUND, because CAN_IO_UNUSED is just defined as (-1) in this version +// of the framework which does not work with -fpermissive +#undef CAN_IO_UNUSED +#define CAN_IO_UNUSED ((gpio_num_t) -1) + +namespace esphome { +namespace esp32_can { + +static const char *const TAG = "esp32_can"; + +static bool get_bitrate(canbus::CanSpeed bitrate, can_timing_config_t *t_config) { + switch (bitrate) { + case canbus::CAN_50KBPS: + *t_config = (can_timing_config_t) CAN_TIMING_CONFIG_50KBITS(); + return true; + case canbus::CAN_100KBPS: + *t_config = (can_timing_config_t) CAN_TIMING_CONFIG_100KBITS(); + return true; + case canbus::CAN_125KBPS: + *t_config = (can_timing_config_t) CAN_TIMING_CONFIG_125KBITS(); + return true; + case canbus::CAN_250KBPS: + *t_config = (can_timing_config_t) CAN_TIMING_CONFIG_250KBITS(); + return true; + case canbus::CAN_500KBPS: + *t_config = (can_timing_config_t) CAN_TIMING_CONFIG_500KBITS(); + return true; + case canbus::CAN_1000KBPS: + *t_config = (can_timing_config_t) CAN_TIMING_CONFIG_1MBITS(); + return true; + default: + return false; + } +} + +bool ESP32Can::setup_internal() { + can_general_config_t g_config = + CAN_GENERAL_CONFIG_DEFAULT((gpio_num_t) this->tx_, (gpio_num_t) this->rx_, CAN_MODE_NORMAL); + can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL(); + can_timing_config_t t_config; + + if (!get_bitrate(this->bit_rate_, &t_config)) { + // invalid bit rate + this->mark_failed(); + return false; + } + + // Install CAN driver + if (can_driver_install(&g_config, &t_config, &f_config) != ESP_OK) { + // Failed to install driver + this->mark_failed(); + return false; + } + + // Start CAN driver + if (can_start() != ESP_OK) { + // Failed to start driver + this->mark_failed(); + return false; + } + return true; +} + +canbus::Error ESP32Can::send_message(struct canbus::CanFrame *frame) { + if (frame->can_data_length_code > canbus::CAN_MAX_DATA_LENGTH) { + return canbus::ERROR_FAILTX; + } + + uint32_t flags = CAN_MSG_FLAG_NONE; + if (frame->use_extended_id) { + flags |= CAN_MSG_FLAG_EXTD; + } + if (frame->remote_transmission_request) { + flags |= CAN_MSG_FLAG_RTR; + } + + can_message_t message = { + .flags = flags, + .identifier = frame->can_id, + .data_length_code = frame->can_data_length_code, + }; + if (!frame->remote_transmission_request) { + memcpy(message.data, frame->data, frame->can_data_length_code); + } + + if (can_transmit(&message, pdMS_TO_TICKS(1000)) == ESP_OK) { + return canbus::ERROR_OK; + } else { + return canbus::ERROR_ALLTXBUSY; + } +} + +canbus::Error ESP32Can::read_message(struct canbus::CanFrame *frame) { + can_message_t message; + + if (can_receive(&message, 0) != ESP_OK) { + return canbus::ERROR_NOMSG; + } + + frame->can_id = message.identifier; + frame->use_extended_id = message.flags & CAN_MSG_FLAG_EXTD; + frame->remote_transmission_request = message.flags & CAN_MSG_FLAG_RTR; + frame->can_data_length_code = message.data_length_code; + + if (!frame->remote_transmission_request) { + size_t dlc = + message.data_length_code < canbus::CAN_MAX_DATA_LENGTH ? message.data_length_code : canbus::CAN_MAX_DATA_LENGTH; + memcpy(frame->data, message.data, dlc); + } + + return canbus::ERROR_OK; +} + +} // namespace esp32_can +} // namespace esphome + +#endif diff --git a/esphome/components/esp32_can/esp32_can.h b/esphome/components/esp32_can/esp32_can.h new file mode 100644 index 0000000000..a428834f65 --- /dev/null +++ b/esphome/components/esp32_can/esp32_can.h @@ -0,0 +1,29 @@ +#pragma once + +#ifdef USE_ESP32 + +#include "esphome/components/canbus/canbus.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace esp32_can { + +class ESP32Can : public canbus::Canbus { + public: + void set_rx(int rx) { rx_ = rx; } + void set_tx(int tx) { tx_ = tx; } + ESP32Can(){}; + + protected: + bool setup_internal() override; + canbus::Error send_message(struct canbus::CanFrame *frame) override; + canbus::Error read_message(struct canbus::CanFrame *frame) override; + + int rx_{-1}; + int tx_{-1}; +}; + +} // namespace esp32_can +} // namespace esphome + +#endif diff --git a/tests/test1.yaml b/tests/test1.yaml index 959ffb0d2d..d3351e3b12 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2472,6 +2472,11 @@ text_sensor: id: glob_int value: '0' - canbus.send: + canbus_id: mcp2515_can + can_id: 23 + data: [0x10, 0x20, 0x30] + - canbus.send: + canbus_id: esp32_internal_can can_id: 23 data: [0x10, 0x20, 0x30] - platform: template @@ -2509,6 +2514,7 @@ rtttl: canbus: - platform: mcp2515 + id: mcp2515_can cs_pin: GPIO17 can_id: 4 bit_rate: 50kbps @@ -2525,6 +2531,25 @@ canbus: lambda: 'return x[0] == 0x11;' then: light.toggle: ${roomname}_lights + - platform: esp32_can + id: esp32_internal_can + rx_pin: GPIO04 + tx_pin: GPIO05 + can_id: 4 + bit_rate: 50kbps + on_frame: + - can_id: 500 + then: + - lambda: |- + std::string b(x.begin(), x.end()); + ESP_LOGD("canid 500", "%s", &b[0] ); + - can_id: 23 + then: + - if: + condition: + lambda: 'return x[0] == 0x11;' + then: + light.toggle: ${roomname}_lights teleinfo: id: myteleinfo