diff --git a/CODEOWNERS b/CODEOWNERS index 09b10e84cb..99c8021d42 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -19,6 +19,7 @@ esphome/components/async_tcp/* @OttoWinter esphome/components/atc_mithermometer/* @ahpohl esphome/components/bang_bang/* @OttoWinter esphome/components/binary_sensor/* @esphome/core +esphome/components/canbus/* @danielschramm @mvturnho esphome/components/captive_portal/* @OttoWinter esphome/components/climate/* @esphome/core esphome/components/climate_ir/* @glmnet @@ -43,6 +44,7 @@ esphome/components/light/* @esphome/core esphome/components/logger/* @esphome/core esphome/components/mcp23s08/* @SenexCrenshaw esphome/components/mcp23s17/* @SenexCrenshaw +esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp9808/* @k7hpn esphome/components/network/* @esphome/core esphome/components/ota/* @esphome/core diff --git a/esphome/components/canbus/__init__.py b/esphome/components/canbus/__init__.py new file mode 100644 index 0000000000..24decc14b0 --- /dev/null +++ b/esphome/components/canbus/__init__.py @@ -0,0 +1,124 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.core import CORE, coroutine +from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_DATA + +CODEOWNERS = ['@mvturnho', '@danielschramm'] +IS_PLATFORM_COMPONENT = True + +CONF_CAN_ID = 'can_id' +CONF_USE_EXTENDED_ID = 'use_extended_id' +CONF_CANBUS_ID = 'canbus_id' +CONF_BIT_RATE = 'bit_rate' +CONF_ON_FRAME = 'on_frame' +CONF_CANBUS_SEND = 'canbus.send' + + +def validate_id(id_value, id_ext): + if not id_ext: + if id_value > 0x7ff: + raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)") + + +def validate_raw_data(value): + if isinstance(value, str): + return value.encode('utf-8') + if isinstance(value, list): + return cv.Schema([cv.hex_uint8_t])(value) + raise cv.Invalid("data must either be a string wrapped in quotes or a list of bytes") + + +canbus_ns = cg.esphome_ns.namespace('canbus') +CanbusComponent = canbus_ns.class_('CanbusComponent', cg.Component) +CanbusTrigger = canbus_ns.class_('CanbusTrigger', + automation.Trigger.template(cg.std_vector.template(cg.uint8)), + cg.Component) +CanSpeed = canbus_ns.enum('CAN_SPEED') + +CAN_SPEEDS = { + '5KBPS': CanSpeed.CAN_5KBPS, + '10KBPS': CanSpeed.CAN_10KBPS, + '20KBPS': CanSpeed.CAN_20KBPS, + '31K25BPS': CanSpeed.CAN_31K25BPS, + '33KBPS': CanSpeed.CAN_33KBPS, + '40KBPS': CanSpeed.CAN_40KBPS, + '50KBPS': CanSpeed.CAN_50KBPS, + '80KBPS': CanSpeed.CAN_80KBPS, + '83K3BPS': CanSpeed.CAN_83K3BPS, + '95KBPS': CanSpeed.CAN_95KBPS, + '100KBPS': CanSpeed.CAN_100KBPS, + '125KBPS': CanSpeed.CAN_125KBPS, + '200KBPS': CanSpeed.CAN_200KBPS, + '250KBPS': CanSpeed.CAN_250KBPS, + '500KBPS': CanSpeed.CAN_500KBPS, + '1000KBPS': CanSpeed.CAN_1000KBPS, +} + +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(CanbusComponent), + cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1fffffff), + cv.Optional(CONF_BIT_RATE, default='125KBPS'): cv.enum(CAN_SPEEDS, upper=True), + cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, + cv.Optional(CONF_ON_FRAME): automation.validate_automation({ + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger), + cv.GenerateID(CONF_CAN_ID): cv.int_range(min=0, max=0x1fffffff), + cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, + }), +}).extend(cv.COMPONENT_SCHEMA) + + +@coroutine +def setup_canbus_core_(var, config): + validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID]) + yield cg.register_component(var, config) + cg.add(var.set_can_id([config[CONF_CAN_ID]])) + cg.add(var.set_use_extended_id([config[CONF_USE_EXTENDED_ID]])) + cg.add(var.set_bitrate(CAN_SPEEDS[config[CONF_BIT_RATE]])) + + for conf in config.get(CONF_ON_FRAME, []): + can_id = conf[CONF_CAN_ID] + ext_id = conf[CONF_USE_EXTENDED_ID] + validate_id(can_id, ext_id) + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id) + yield cg.register_component(trigger, conf) + yield automation.build_automation(trigger, [(cg.std_vector.template(cg.uint8), 'x')], conf) + + +@coroutine +def register_canbus(var, config): + if not CORE.has_id(config[CONF_ID]): + var = cg.new_Pvariable(config[CONF_ID], var) + yield setup_canbus_core_(var, config) + + +# Actions +@automation.register_action(CONF_CANBUS_SEND, + canbus_ns.class_('CanbusSendAction', automation.Action), + cv.maybe_simple_value({ + cv.GenerateID(CONF_CANBUS_ID): cv.use_id(CanbusComponent), + cv.Optional(CONF_CAN_ID): cv.int_range(min=0, max=0x1fffffff), + cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, + cv.Required(CONF_DATA): cv.templatable(validate_raw_data), + }, key=CONF_DATA)) +def canbus_action_to_code(config, action_id, template_arg, args): + validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID]) + var = cg.new_Pvariable(action_id, template_arg) + yield cg.register_parented(var, config[CONF_CANBUS_ID]) + + if CONF_CAN_ID in config: + can_id = yield cg.templatable(config[CONF_CAN_ID], args, cg.uint32) + cg.add(var.set_can_id(can_id)) + + use_extended_id = yield cg.templatable(config[CONF_USE_EXTENDED_ID], args, cg.uint32) + cg.add(var.set_use_extended_id(use_extended_id)) + + data = config[CONF_DATA] + if isinstance(data, bytes): + data = [int(x) for x in data] + if cg.is_template(data): + templ = yield cg.templatable(data, args, cg.std_vector.template(cg.uint8)) + cg.add(var.set_data_template(templ)) + else: + cg.add(var.set_data_static(data)) + yield var diff --git a/esphome/components/canbus/canbus.cpp b/esphome/components/canbus/canbus.cpp new file mode 100644 index 0000000000..20afd4b296 --- /dev/null +++ b/esphome/components/canbus/canbus.cpp @@ -0,0 +1,87 @@ +#include "canbus.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace canbus { + +static const char *TAG = "canbus"; + +void Canbus::setup() { + ESP_LOGCONFIG(TAG, "Setting up Canbus..."); + if (!this->setup_internal()) { + ESP_LOGE(TAG, "setup error!"); + this->mark_failed(); + } +} + +void Canbus::dump_config() { + if (this->use_extended_id_) { + ESP_LOGCONFIG(TAG, "config extended id=0x%08x", this->can_id_); + } else { + ESP_LOGCONFIG(TAG, "config standard id=0x%03x", this->can_id_); + } +} + +void Canbus::send_data(uint32_t can_id, bool use_extended_id, const std::vector &data) { + struct CanFrame can_message; + + uint8_t size = static_cast(data.size()); + if (use_extended_id) { + ESP_LOGD(TAG, "send extended id=0x%08x size=%d", can_id, size); + } else { + ESP_LOGD(TAG, "send extended id=0x%03x size=%d", can_id, size); + } + if (size > CAN_MAX_DATA_LENGTH) + size = CAN_MAX_DATA_LENGTH; + can_message.can_data_length_code = size; + can_message.can_id = can_id; + can_message.use_extended_id = use_extended_id; + + for (int i = 0; i < size; i++) { + can_message.data[i] = data[i]; + ESP_LOGVV(TAG, " data[%d]=%02x", i, can_message.data[i]); + } + + this->send_message(&can_message); +} + +void Canbus::add_trigger(CanbusTrigger *trigger) { + if (trigger->use_extended_id_) { + ESP_LOGVV(TAG, "add trigger for extended canid=0x%08x", trigger->can_id_); + } else { + ESP_LOGVV(TAG, "add trigger for std canid=0x%03x", trigger->can_id_); + } + this->triggers_.push_back(trigger); +}; + +void Canbus::loop() { + struct CanFrame can_message; + // readmessage + if (this->read_message(&can_message) == canbus::ERROR_OK) { + if (can_message.use_extended_id) { + ESP_LOGD(TAG, "received can message extended can_id=0x%x size=%d", can_message.can_id, + can_message.can_data_length_code); + } else { + ESP_LOGD(TAG, "received can message std can_id=0x%x size=%d", can_message.can_id, + can_message.can_data_length_code); + } + + std::vector data; + + // show data received + for (int i = 0; i < can_message.can_data_length_code; i++) { + ESP_LOGV(TAG, " can_message.data[%d]=%02x", i, can_message.data[i]); + data.push_back(can_message.data[i]); + } + + // fire all triggers + for (auto trigger : this->triggers_) { + if ((trigger->can_id_ == can_message.can_id) && (trigger->use_extended_id_ == can_message.use_extended_id)) { + trigger->trigger(data); + } + } + } +} + +} // namespace canbus +} // namespace esphome diff --git a/esphome/components/canbus/canbus.h b/esphome/components/canbus/canbus.h new file mode 100644 index 0000000000..37adf0bc9c --- /dev/null +++ b/esphome/components/canbus/canbus.h @@ -0,0 +1,134 @@ +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/optional.h" + +namespace esphome { +namespace canbus { + +enum Error : uint8_t { + ERROR_OK = 0, + ERROR_FAIL = 1, + ERROR_ALLTXBUSY = 2, + ERROR_FAILINIT = 3, + ERROR_FAILTX = 4, + ERROR_NOMSG = 5 +}; + +enum CanSpeed : uint8_t { + CAN_5KBPS, + CAN_10KBPS, + CAN_20KBPS, + CAN_31K25BPS, + CAN_33KBPS, + CAN_40KBPS, + CAN_50KBPS, + CAN_80KBPS, + CAN_83K3BPS, + CAN_95KBPS, + CAN_100KBPS, + CAN_125KBPS, + CAN_200KBPS, + CAN_250KBPS, + CAN_500KBPS, + CAN_1000KBPS +}; + +class CanbusTrigger; +template class CanbusSendAction; + +/* CAN payload length definitions according to ISO 11898-1 */ +static const uint8_t CAN_MAX_DATA_LENGTH = 8; + +/* +Can Frame describes a normative CAN Frame +The RTR = Remote Transmission Request is implemented in every CAN controller but rarely used +So currently the flag is passed to and from the hardware but currently ignored to the user application. +*/ +struct CanFrame { + bool use_extended_id = false; + bool remote_transmission_request = false; + uint32_t can_id; /* 29 or 11 bit CAN_ID */ + uint8_t can_data_length_code; /* frame payload length in byte (0 .. CAN_MAX_DATA_LENGTH) */ + uint8_t data[CAN_MAX_DATA_LENGTH] __attribute__((aligned(8))); +}; + +class Canbus : public Component { + public: + Canbus(){}; + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::HARDWARE; } + void loop() override; + + void send_data(uint32_t can_id, bool use_extended_id, const std::vector &data); + void set_can_id(uint32_t can_id) { this->can_id_ = can_id; } + void set_use_extended_id(bool use_extended_id) { this->use_extended_id_ = use_extended_id; } + void set_bitrate(CanSpeed bit_rate) { this->bit_rate_ = bit_rate; } + + void add_trigger(CanbusTrigger *trigger); + + protected: + template friend class CanbusSendAction; + std::vector triggers_{}; + uint32_t can_id_; + bool use_extended_id_; + CanSpeed bit_rate_; + + virtual bool setup_internal(); + virtual Error send_message(struct CanFrame *frame); + virtual Error read_message(struct CanFrame *frame); +}; + +template class CanbusSendAction : public Action, public Parented { + public: + void set_data_template(const std::function(Ts...)> func) { + this->data_func_ = func; + this->static_ = false; + } + void set_data_static(const std::vector &data) { + this->data_static_ = data; + this->static_ = true; + } + + void set_can_id(uint32_t can_id) { this->can_id_ = can_id; } + + void set_use_extended_id(bool use_extended_id) { this->use_extended_id_ = use_extended_id; } + + void play(Ts... x) override { + auto can_id = this->can_id_.has_value() ? *this->can_id_ : this->parent_->can_id_; + auto use_extended_id = + this->use_extended_id_.has_value() ? *this->use_extended_id_ : this->parent_->use_extended_id_; + if (this->static_) { + this->parent_->send_data(can_id, use_extended_id, this->data_static_); + } else { + auto val = this->data_func_(x...); + this->parent_->send_data(can_id, use_extended_id, val); + } + } + + protected: + optional can_id_{}; + optional use_extended_id_{}; + bool static_{false}; + std::function(Ts...)> data_func_{}; + std::vector data_static_{}; +}; + +class CanbusTrigger : public Trigger>, public Component { + friend class Canbus; + + public: + explicit CanbusTrigger(Canbus *parent, const std::uint32_t can_id, const bool use_extended_id) + : parent_(parent), can_id_(can_id), use_extended_id_(use_extended_id){}; + void setup() override { this->parent_->add_trigger(this); } + + protected: + Canbus *parent_; + uint32_t can_id_; + bool use_extended_id_; +}; + +} // namespace canbus +} // namespace esphome diff --git a/esphome/components/mcp2515/__init__.py b/esphome/components/mcp2515/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/mcp2515/canbus.py b/esphome/components/mcp2515/canbus.py new file mode 100644 index 0000000000..a877507e36 --- /dev/null +++ b/esphome/components/mcp2515/canbus.py @@ -0,0 +1,47 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import spi, canbus +from esphome.const import CONF_ID, CONF_MODE +from esphome.components.canbus import CanbusComponent + +CODEOWNERS = ['@mvturnho', '@danielschramm'] +DEPENDENCIES = ['spi'] + +CONF_CLOCK = 'clock' + +mcp2515_ns = cg.esphome_ns.namespace('mcp2515') +mcp2515 = mcp2515_ns.class_('MCP2515', CanbusComponent, spi.SPIDevice) +CanClock = mcp2515_ns.enum('CAN_CLOCK') +McpMode = mcp2515_ns.enum('CANCTRL_REQOP_MODE') + +CAN_CLOCK = { + '8MHZ': CanClock.MCP_8MHZ, + '16MHZ': CanClock.MCP_16MHZ, + '20MHZ': CanClock.MCP_20MHZ, +} + +MCP_MODE = { + 'NORMAL': McpMode.CANCTRL_REQOP_NORMAL, + 'LOOPBACK': McpMode.CANCTRL_REQOP_LOOPBACK, + 'LISTENONLY': McpMode.CANCTRL_REQOP_LISTENONLY, +} + +CONFIG_SCHEMA = canbus.CONFIG_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(mcp2515), + cv.Optional(CONF_CLOCK, default='8MHZ'): cv.enum(CAN_CLOCK, upper=True), + cv.Optional(CONF_MODE, default='NORMAL'): cv.enum(MCP_MODE, upper=True), +}).extend(spi.spi_device_schema(True)) + + +def to_code(config): + rhs = mcp2515.new() + var = cg.Pvariable(config[CONF_ID], rhs) + yield canbus.register_canbus(var, config) + if CONF_CLOCK in config: + canclock = CAN_CLOCK[config[CONF_CLOCK]] + cg.add(var.set_mcp_clock(canclock)) + if CONF_MODE in config: + mode = MCP_MODE[config[CONF_MODE]] + cg.add(var.set_mcp_mode(mode)) + + yield spi.register_spi_device(var, config) diff --git a/esphome/components/mcp2515/mcp2515.cpp b/esphome/components/mcp2515/mcp2515.cpp new file mode 100644 index 0000000000..228a951702 --- /dev/null +++ b/esphome/components/mcp2515/mcp2515.cpp @@ -0,0 +1,612 @@ +#include "mcp2515.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mcp2515 { + +static const char *TAG = "mcp2515"; + +const struct MCP2515::TxBnRegs MCP2515::TXB[N_TXBUFFERS] = {{MCP_TXB0CTRL, MCP_TXB0SIDH, MCP_TXB0DATA}, + {MCP_TXB1CTRL, MCP_TXB1SIDH, MCP_TXB1DATA}, + {MCP_TXB2CTRL, MCP_TXB2SIDH, MCP_TXB2DATA}}; + +const struct MCP2515::RxBnRegs MCP2515::RXB[N_RXBUFFERS] = {{MCP_RXB0CTRL, MCP_RXB0SIDH, MCP_RXB0DATA, CANINTF_RX0IF}, + {MCP_RXB1CTRL, MCP_RXB1SIDH, MCP_RXB1DATA, CANINTF_RX1IF}}; + +bool MCP2515::setup_internal() { + this->spi_setup(); + + if (this->reset_() == canbus::ERROR_FAIL) + return false; + this->set_bitrate_(this->bit_rate_, this->mcp_clock_); + this->set_mode_(this->mcp_mode_); + ESP_LOGV(TAG, "setup done"); + return true; +} + +canbus::Error MCP2515::reset_() { + this->enable(); + this->transfer_byte(INSTRUCTION_RESET); + this->disable(); + ESP_LOGV(TAG, "reset_()"); + delay(10); + + ESP_LOGV(TAG, "reset() CLEAR ALL TXB registers"); + + uint8_t zeros[14]; + memset(zeros, 0, sizeof(zeros)); + set_registers_(MCP_TXB0CTRL, zeros, 14); + set_registers_(MCP_TXB1CTRL, zeros, 14); + set_registers_(MCP_TXB2CTRL, zeros, 14); + ESP_LOGD(TAG, "reset() CLEARED TXB registers"); + + set_register_(MCP_RXB0CTRL, 0); + set_register_(MCP_RXB1CTRL, 0); + + set_register_(MCP_CANINTE, CANINTF_RX0IF | CANINTF_RX1IF | CANINTF_ERRIF | CANINTF_MERRF); + + modify_register_(MCP_RXB0CTRL, RXB_CTRL_RXM_MASK | RXB_0_CTRL_BUKT, RXB_CTRL_RXM_STDEXT | RXB_0_CTRL_BUKT); + modify_register_(MCP_RXB1CTRL, RXB_CTRL_RXM_MASK, RXB_CTRL_RXM_STDEXT); + + return canbus::ERROR_OK; +} + +uint8_t MCP2515::read_register_(const REGISTER reg) { + this->enable(); + this->transfer_byte(INSTRUCTION_READ); + this->transfer_byte(reg); + uint8_t ret = this->transfer_byte(0x00); + this->disable(); + + return ret; +} + +void MCP2515::read_registers_(const REGISTER reg, uint8_t values[], const uint8_t n) { + this->enable(); + this->transfer_byte(INSTRUCTION_READ); + this->transfer_byte(reg); + // this->transfer_array(values, n); + // mcp2515 has auto - increment of address - pointer + for (uint8_t i = 0; i < n; i++) { + values[i] = this->transfer_byte(0x00); + } + this->disable(); +} + +void MCP2515::set_register_(const REGISTER reg, const uint8_t value) { + this->enable(); + this->transfer_byte(INSTRUCTION_WRITE); + this->transfer_byte(reg); + this->transfer_byte(value); + this->disable(); +} + +void MCP2515::set_registers_(const REGISTER reg, uint8_t values[], const uint8_t n) { + this->enable(); + this->transfer_byte(INSTRUCTION_WRITE); + this->transfer_byte(reg); + // this->transfer_array(values, n); + for (uint8_t i = 0; i < n; i++) { + this->transfer_byte(values[i]); + } + this->disable(); +} + +void MCP2515::modify_register_(const REGISTER reg, const uint8_t mask, const uint8_t data) { + this->enable(); + this->transfer_byte(INSTRUCTION_BITMOD); + this->transfer_byte(reg); + this->transfer_byte(mask); + this->transfer_byte(data); + this->disable(); +} + +uint8_t MCP2515::get_status_() { + this->enable(); + this->transfer_byte(INSTRUCTION_READ_STATUS); + uint8_t i = this->transfer_byte(0x00); + this->disable(); + + return i; +} + +canbus::Error MCP2515::set_mode_(const CanctrlReqopMode mode) { + modify_register_(MCP_CANCTRL, CANCTRL_REQOP, mode); + + unsigned long end_time = millis() + 10; + bool mode_match = false; + while (millis() < end_time) { + uint8_t new_mode = read_register_(MCP_CANSTAT); + new_mode &= CANSTAT_OPMOD; + mode_match = new_mode == mode; + if (mode_match) { + break; + } + } + return mode_match ? canbus::ERROR_OK : canbus::ERROR_FAIL; +} + +canbus::Error MCP2515::set_clk_out_(const CanClkOut divisor) { + canbus::Error res; + uint8_t cfg3; + + if (divisor == CLKOUT_DISABLE) { + /* Turn off CLKEN */ + modify_register_(MCP_CANCTRL, CANCTRL_CLKEN, 0x00); + + /* Turn on CLKOUT for SOF */ + modify_register_(MCP_CNF3, CNF3_SOF, CNF3_SOF); + return canbus::ERROR_OK; + } + + /* Set the prescaler (CLKPRE) */ + modify_register_(MCP_CANCTRL, CANCTRL_CLKPRE, divisor); + + /* Turn on CLKEN */ + modify_register_(MCP_CANCTRL, CANCTRL_CLKEN, CANCTRL_CLKEN); + + /* Turn off CLKOUT for SOF */ + modify_register_(MCP_CNF3, CNF3_SOF, 0x00); + return canbus::ERROR_OK; +} + +void MCP2515::prepare_id_(uint8_t *buffer, const bool extended, const uint32_t id) { + uint16_t canid = (uint16_t)(id & 0x0FFFF); + + if (extended) { + buffer[MCP_EID0] = (uint8_t)(canid & 0xFF); + buffer[MCP_EID8] = (uint8_t)(canid >> 8); + canid = (uint16_t)(id >> 16); + buffer[MCP_SIDL] = (uint8_t)(canid & 0x03); + buffer[MCP_SIDL] += (uint8_t)((canid & 0x1C) << 3); + buffer[MCP_SIDL] |= TXB_EXIDE_MASK; + buffer[MCP_SIDH] = (uint8_t)(canid >> 5); + } else { + buffer[MCP_SIDH] = (uint8_t)(canid >> 3); + buffer[MCP_SIDL] = (uint8_t)((canid & 0x07) << 5); + buffer[MCP_EID0] = 0; + buffer[MCP_EID8] = 0; + } +} + +canbus::Error MCP2515::set_filter_mask_(const MASK mask, const bool extended, const uint32_t ul_data) { + canbus::Error res = set_mode_(CANCTRL_REQOP_CONFIG); + if (res != canbus::ERROR_OK) { + return res; + } + + uint8_t tbufdata[4]; + prepare_id_(tbufdata, extended, ul_data); + + REGISTER reg; + switch (mask) { + case MASK0: + reg = MCP_RXM0SIDH; + break; + case MASK1: + reg = MCP_RXM1SIDH; + break; + default: + return canbus::ERROR_FAIL; + } + + set_registers_(reg, tbufdata, 4); + + return canbus::ERROR_OK; +} + +canbus::Error MCP2515::set_filter_(const RXF num, const bool extended, const uint32_t ul_data) { + canbus::Error res = set_mode_(CANCTRL_REQOP_CONFIG); + if (res != canbus::ERROR_OK) { + return res; + } + + REGISTER reg; + + switch (num) { + case RXF0: + reg = MCP_RXF0SIDH; + break; + case RXF1: + reg = MCP_RXF1SIDH; + break; + case RXF2: + reg = MCP_RXF2SIDH; + break; + case RXF3: + reg = MCP_RXF3SIDH; + break; + case RXF4: + reg = MCP_RXF4SIDH; + break; + case RXF5: + reg = MCP_RXF5SIDH; + break; + default: + return canbus::ERROR_FAIL; + } + + uint8_t tbufdata[4]; + prepare_id_(tbufdata, extended, ul_data); + set_registers_(reg, tbufdata, 4); + + return canbus::ERROR_OK; +} + +canbus::Error MCP2515::send_message_(TXBn txbn, struct canbus::CanFrame *frame) { + const struct TxBnRegs *txbuf = &TXB[txbn]; + + uint8_t data[13]; + + prepare_id_(data, frame->use_extended_id, frame->can_id); + data[MCP_DLC] = + frame->remote_transmission_request ? (frame->can_data_length_code | RTR_MASK) : frame->can_data_length_code; + memcpy(&data[MCP_DATA], frame->data, frame->can_data_length_code); + set_registers_(txbuf->SIDH, data, 5 + frame->can_data_length_code); + modify_register_(txbuf->CTRL, TXB_TXREQ, TXB_TXREQ); + + return canbus::ERROR_OK; +} + +canbus::Error MCP2515::send_message(struct canbus::CanFrame *frame) { + if (frame->can_data_length_code > canbus::CAN_MAX_DATA_LENGTH) { + return canbus::ERROR_FAILTX; + } + TXBn tx_buffers[N_TXBUFFERS] = {TXB0, TXB1, TXB2}; + + for (auto &tx_buffer : tx_buffers) { + const struct TxBnRegs *txbuf = &TXB[tx_buffer]; + uint8_t ctrlval = read_register_(txbuf->CTRL); + if ((ctrlval & TXB_TXREQ) == 0) { + return send_message_(tx_buffer, frame); + } + } + + return canbus::ERROR_FAILTX; +} + +canbus::Error MCP2515::read_message_(RXBn rxbn, struct canbus::CanFrame *frame) { + const struct RxBnRegs *rxb = &RXB[rxbn]; + + uint8_t tbufdata[5]; + + read_registers_(rxb->SIDH, tbufdata, 5); + + uint32_t id = (tbufdata[MCP_SIDH] << 3) + (tbufdata[MCP_SIDL] >> 5); + bool use_extended_id = false; + bool remote_transmission_request = false; + + if ((tbufdata[MCP_SIDL] & TXB_EXIDE_MASK) == TXB_EXIDE_MASK) { + id = (id << 2) + (tbufdata[MCP_SIDL] & 0x03); + id = (id << 8) + tbufdata[MCP_EID8]; + id = (id << 8) + tbufdata[MCP_EID0]; + // id |= canbus::CAN_EFF_FLAG; + use_extended_id = true; + } + + uint8_t dlc = (tbufdata[MCP_DLC] & DLC_MASK); + if (dlc > canbus::CAN_MAX_DATA_LENGTH) { + return canbus::ERROR_FAIL; + } + + uint8_t ctrl = read_register_(rxb->CTRL); + if (ctrl & RXB_CTRL_RTR) { + // id |= canbus::CAN_RTR_FLAG; + remote_transmission_request = true; + } + + frame->can_id = id; + frame->can_data_length_code = dlc; + frame->use_extended_id = use_extended_id; + frame->remote_transmission_request = remote_transmission_request; + + read_registers_(rxb->DATA, frame->data, dlc); + + modify_register_(MCP_CANINTF, rxb->CANINTF_RXnIF, 0); + + return canbus::ERROR_OK; +} + +canbus::Error MCP2515::read_message(struct canbus::CanFrame *frame) { + canbus::Error rc; + uint8_t stat = get_status_(); + + if (stat & STAT_RX0IF) { + rc = read_message_(RXB0, frame); + } else if (stat & STAT_RX1IF) { + rc = read_message_(RXB1, frame); + } else { + rc = canbus::ERROR_NOMSG; + } + + return rc; +} + +bool MCP2515::check_receive_() { + uint8_t res = get_status_(); + return (res & STAT_RXIF_MASK) != 0; +} + +bool MCP2515::check_error_() { + uint8_t eflg = get_error_flags_(); + return (eflg & EFLG_ERRORMASK) != 0; +} + +uint8_t MCP2515::get_error_flags_() { return read_register_(MCP_EFLG); } + +void MCP2515::clear_rx_n_ovr_flags_() { modify_register_(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0); } + +uint8_t MCP2515::get_int_() { return read_register_(MCP_CANINTF); } + +void MCP2515::clear_int_() { set_register_(MCP_CANINTF, 0); } + +uint8_t MCP2515::get_int_mask_() { return read_register_(MCP_CANINTE); } + +void MCP2515::clear_tx_int_() { modify_register_(MCP_CANINTF, (CANINTF_TX0IF | CANINTF_TX1IF | CANINTF_TX2IF), 0); } + +void MCP2515::clear_rx_n_ovr_() { + uint8_t eflg = get_error_flags_(); + if (eflg != 0) { + clear_rx_n_ovr_flags_(); + clear_int_(); + // modify_register_(MCP_CANINTF, CANINTF_ERRIF, 0); + } +} + +void MCP2515::clear_merr_() { + // modify_register_(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0); + // clear_int_(); + modify_register_(MCP_CANINTF, CANINTF_MERRF, 0); +} + +void MCP2515::clear_errif_() { + // modify_register_(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0); + // clear_int_(); + modify_register_(MCP_CANINTF, CANINTF_ERRIF, 0); +} + +canbus::Error MCP2515::set_bitrate_(canbus::CanSpeed can_speed) { return this->set_bitrate_(can_speed, MCP_16MHZ); } + +canbus::Error MCP2515::set_bitrate_(canbus::CanSpeed can_speed, CanClock can_clock) { + canbus::Error error = set_mode_(CANCTRL_REQOP_CONFIG); + if (error != canbus::ERROR_OK) { + return error; + } + + uint8_t set, cfg1, cfg2, cfg3; + set = 1; + switch (can_clock) { + case (MCP_8MHZ): + switch (can_speed) { + case (canbus::CAN_5KBPS): // 5KBPS + cfg1 = MCP_8MHZ_5KBPS_CFG1; + cfg2 = MCP_8MHZ_5KBPS_CFG2; + cfg3 = MCP_8MHZ_5KBPS_CFG3; + break; + case (canbus::CAN_10KBPS): // 10KBPS + cfg1 = MCP_8MHZ_10KBPS_CFG1; + cfg2 = MCP_8MHZ_10KBPS_CFG2; + cfg3 = MCP_8MHZ_10KBPS_CFG3; + break; + case (canbus::CAN_20KBPS): // 20KBPS + cfg1 = MCP_8MHZ_20KBPS_CFG1; + cfg2 = MCP_8MHZ_20KBPS_CFG2; + cfg3 = MCP_8MHZ_20KBPS_CFG3; + break; + case (canbus::CAN_31K25BPS): // 31.25KBPS + cfg1 = MCP_8MHZ_31K25BPS_CFG1; + cfg2 = MCP_8MHZ_31K25BPS_CFG2; + cfg3 = MCP_8MHZ_31K25BPS_CFG3; + break; + case (canbus::CAN_33KBPS): // 33.333KBPS + cfg1 = MCP_8MHZ_33K3BPS_CFG1; + cfg2 = MCP_8MHZ_33K3BPS_CFG2; + cfg3 = MCP_8MHZ_33K3BPS_CFG3; + break; + case (canbus::CAN_40KBPS): // 40Kbps + cfg1 = MCP_8MHZ_40KBPS_CFG1; + cfg2 = MCP_8MHZ_40KBPS_CFG2; + cfg3 = MCP_8MHZ_40KBPS_CFG3; + break; + case (canbus::CAN_50KBPS): // 50Kbps + cfg1 = MCP_8MHZ_50KBPS_CFG1; + cfg2 = MCP_8MHZ_50KBPS_CFG2; + cfg3 = MCP_8MHZ_50KBPS_CFG3; + break; + case (canbus::CAN_80KBPS): // 80Kbps + cfg1 = MCP_8MHZ_80KBPS_CFG1; + cfg2 = MCP_8MHZ_80KBPS_CFG2; + cfg3 = MCP_8MHZ_80KBPS_CFG3; + break; + case (canbus::CAN_100KBPS): // 100Kbps + cfg1 = MCP_8MHZ_100KBPS_CFG1; + cfg2 = MCP_8MHZ_100KBPS_CFG2; + cfg3 = MCP_8MHZ_100KBPS_CFG3; + break; + case (canbus::CAN_125KBPS): // 125Kbps + cfg1 = MCP_8MHZ_125KBPS_CFG1; + cfg2 = MCP_8MHZ_125KBPS_CFG2; + cfg3 = MCP_8MHZ_125KBPS_CFG3; + break; + case (canbus::CAN_200KBPS): // 200Kbps + cfg1 = MCP_8MHZ_200KBPS_CFG1; + cfg2 = MCP_8MHZ_200KBPS_CFG2; + cfg3 = MCP_8MHZ_200KBPS_CFG3; + break; + case (canbus::CAN_250KBPS): // 250Kbps + cfg1 = MCP_8MHZ_250KBPS_CFG1; + cfg2 = MCP_8MHZ_250KBPS_CFG2; + cfg3 = MCP_8MHZ_250KBPS_CFG3; + break; + case (canbus::CAN_500KBPS): // 500Kbps + cfg1 = MCP_8MHZ_500KBPS_CFG1; + cfg2 = MCP_8MHZ_500KBPS_CFG2; + cfg3 = MCP_8MHZ_500KBPS_CFG3; + break; + case (canbus::CAN_1000KBPS): // 1Mbps + cfg1 = MCP_8MHZ_1000KBPS_CFG1; + cfg2 = MCP_8MHZ_1000KBPS_CFG2; + cfg3 = MCP_8MHZ_1000KBPS_CFG3; + break; + default: + set = 0; + break; + } + break; + + case (MCP_16MHZ): + switch (can_speed) { + case (canbus::CAN_5KBPS): // 5Kbps + cfg1 = MCP_16MHZ_5KBPS_CFG1; + cfg2 = MCP_16MHZ_5KBPS_CFG2; + cfg3 = MCP_16MHZ_5KBPS_CFG3; + break; + case (canbus::CAN_10KBPS): // 10Kbps + cfg1 = MCP_16MHZ_10KBPS_CFG1; + cfg2 = MCP_16MHZ_10KBPS_CFG2; + cfg3 = MCP_16MHZ_10KBPS_CFG3; + break; + case (canbus::CAN_20KBPS): // 20Kbps + cfg1 = MCP_16MHZ_20KBPS_CFG1; + cfg2 = MCP_16MHZ_20KBPS_CFG2; + cfg3 = MCP_16MHZ_20KBPS_CFG3; + break; + case (canbus::CAN_33KBPS): // 33.333Kbps + cfg1 = MCP_16MHZ_33K3BPS_CFG1; + cfg2 = MCP_16MHZ_33K3BPS_CFG2; + cfg3 = MCP_16MHZ_33K3BPS_CFG3; + break; + case (canbus::CAN_40KBPS): // 40Kbps + cfg1 = MCP_16MHZ_40KBPS_CFG1; + cfg2 = MCP_16MHZ_40KBPS_CFG2; + cfg3 = MCP_16MHZ_40KBPS_CFG3; + break; + case (canbus::CAN_50KBPS): // 50Kbps + cfg2 = MCP_16MHZ_50KBPS_CFG2; + cfg3 = MCP_16MHZ_50KBPS_CFG3; + break; + case (canbus::CAN_80KBPS): // 80Kbps + cfg1 = MCP_16MHZ_80KBPS_CFG1; + cfg2 = MCP_16MHZ_80KBPS_CFG2; + cfg3 = MCP_16MHZ_80KBPS_CFG3; + break; + case (canbus::CAN_83K3BPS): // 83.333Kbps + cfg1 = MCP_16MHZ_83K3BPS_CFG1; + cfg2 = MCP_16MHZ_83K3BPS_CFG2; + cfg3 = MCP_16MHZ_83K3BPS_CFG3; + break; + case (canbus::CAN_100KBPS): // 100Kbps + cfg1 = MCP_16MHZ_100KBPS_CFG1; + cfg2 = MCP_16MHZ_100KBPS_CFG2; + cfg3 = MCP_16MHZ_100KBPS_CFG3; + break; + case (canbus::CAN_125KBPS): // 125Kbps + cfg1 = MCP_16MHZ_125KBPS_CFG1; + cfg2 = MCP_16MHZ_125KBPS_CFG2; + cfg3 = MCP_16MHZ_125KBPS_CFG3; + break; + case (canbus::CAN_200KBPS): // 200Kbps + cfg1 = MCP_16MHZ_200KBPS_CFG1; + cfg2 = MCP_16MHZ_200KBPS_CFG2; + cfg3 = MCP_16MHZ_200KBPS_CFG3; + break; + case (canbus::CAN_250KBPS): // 250Kbps + cfg1 = MCP_16MHZ_250KBPS_CFG1; + cfg2 = MCP_16MHZ_250KBPS_CFG2; + cfg3 = MCP_16MHZ_250KBPS_CFG3; + break; + case (canbus::CAN_500KBPS): // 500Kbps + cfg1 = MCP_16MHZ_500KBPS_CFG1; + cfg2 = MCP_16MHZ_500KBPS_CFG2; + cfg3 = MCP_16MHZ_500KBPS_CFG3; + break; + case (canbus::CAN_1000KBPS): // 1Mbps + cfg1 = MCP_16MHZ_1000KBPS_CFG1; + cfg2 = MCP_16MHZ_1000KBPS_CFG2; + cfg3 = MCP_16MHZ_1000KBPS_CFG3; + break; + default: + set = 0; + break; + } + break; + + case (MCP_20MHZ): + switch (can_speed) { + case (canbus::CAN_33KBPS): // 33.333Kbps + cfg1 = MCP_20MHZ_33K3BPS_CFG1; + cfg2 = MCP_20MHZ_33K3BPS_CFG2; + cfg3 = MCP_20MHZ_33K3BPS_CFG3; + break; + case (canbus::CAN_40KBPS): // 40Kbps + cfg1 = MCP_20MHZ_40KBPS_CFG1; + cfg2 = MCP_20MHZ_40KBPS_CFG2; + cfg3 = MCP_20MHZ_40KBPS_CFG3; + break; + case (canbus::CAN_50KBPS): // 50Kbps + cfg1 = MCP_20MHZ_50KBPS_CFG1; + cfg2 = MCP_20MHZ_50KBPS_CFG2; + cfg3 = MCP_20MHZ_50KBPS_CFG3; + break; + case (canbus::CAN_80KBPS): // 80Kbps + cfg1 = MCP_20MHZ_80KBPS_CFG1; + cfg2 = MCP_20MHZ_80KBPS_CFG2; + cfg3 = MCP_20MHZ_80KBPS_CFG3; + break; + case (canbus::CAN_83K3BPS): // 83.333Kbps + cfg1 = MCP_20MHZ_83K3BPS_CFG1; + cfg2 = MCP_20MHZ_83K3BPS_CFG2; + cfg3 = MCP_20MHZ_83K3BPS_CFG3; + break; + case (canbus::CAN_100KBPS): // 100Kbps + cfg1 = MCP_20MHZ_100KBPS_CFG1; + cfg2 = MCP_20MHZ_100KBPS_CFG2; + cfg3 = MCP_20MHZ_100KBPS_CFG3; + break; + case (canbus::CAN_125KBPS): // 125Kbps + cfg1 = MCP_20MHZ_125KBPS_CFG1; + cfg2 = MCP_20MHZ_125KBPS_CFG2; + cfg3 = MCP_20MHZ_125KBPS_CFG3; + break; + case (canbus::CAN_200KBPS): // 200Kbps + cfg1 = MCP_20MHZ_200KBPS_CFG1; + cfg2 = MCP_20MHZ_200KBPS_CFG2; + cfg3 = MCP_20MHZ_200KBPS_CFG3; + break; + case (canbus::CAN_250KBPS): // 250Kbps + cfg1 = MCP_20MHZ_250KBPS_CFG1; + cfg2 = MCP_20MHZ_250KBPS_CFG2; + cfg3 = MCP_20MHZ_250KBPS_CFG3; + break; + case (canbus::CAN_500KBPS): // 500Kbps + cfg1 = MCP_20MHZ_500KBPS_CFG1; + cfg2 = MCP_20MHZ_500KBPS_CFG2; + cfg3 = MCP_20MHZ_500KBPS_CFG3; + break; + case (canbus::CAN_1000KBPS): // 1Mbps + cfg1 = MCP_20MHZ_1000KBPS_CFG1; + cfg2 = MCP_20MHZ_1000KBPS_CFG2; + cfg3 = MCP_20MHZ_1000KBPS_CFG3; + break; + default: + set = 0; + break; + } + break; + + default: + set = 0; + break; + } + + if (set) { + set_register_(MCP_CNF1, cfg1); + set_register_(MCP_CNF2, cfg2); + set_register_(MCP_CNF3, cfg3); + return canbus::ERROR_OK; + } else { + return canbus::ERROR_FAIL; + } +} +} // namespace mcp2515 +} // namespace esphome diff --git a/esphome/components/mcp2515/mcp2515.h b/esphome/components/mcp2515/mcp2515.h new file mode 100644 index 0000000000..3b9797a78a --- /dev/null +++ b/esphome/components/mcp2515/mcp2515.h @@ -0,0 +1,112 @@ +#pragma once + +#include "esphome/components/canbus/canbus.h" +#include "esphome/components/spi/spi.h" +#include "esphome/core/component.h" +#include "mcp2515_defs.h" + +namespace esphome { +namespace mcp2515 { +static const uint32_t SPI_CLOCK = 10000000; // 10MHz + +static const int N_TXBUFFERS = 3; +static const int N_RXBUFFERS = 2; +enum CanClock { MCP_20MHZ, MCP_16MHZ, MCP_8MHZ }; +enum MASK { MASK0, MASK1 }; +enum RXF { RXF0 = 0, RXF1 = 1, RXF2 = 2, RXF3 = 3, RXF4 = 4, RXF5 = 5 }; +enum RXBn { RXB0 = 0, RXB1 = 1 }; +enum TXBn { TXB0 = 0, TXB1 = 1, TXB2 = 2 }; + +enum CanClkOut { + CLKOUT_DISABLE = -1, + CLKOUT_DIV1 = 0x0, + CLKOUT_DIV2 = 0x1, + CLKOUT_DIV4 = 0x2, + CLKOUT_DIV8 = 0x3, +}; + +enum CANINTF : uint8_t { + CANINTF_RX0IF = 0x01, + CANINTF_RX1IF = 0x02, + CANINTF_TX0IF = 0x04, + CANINTF_TX1IF = 0x08, + CANINTF_TX2IF = 0x10, + CANINTF_ERRIF = 0x20, + CANINTF_WAKIF = 0x40, + CANINTF_MERRF = 0x80 +}; + +enum EFLG : uint8_t { + EFLG_RX1OVR = (1 << 7), + EFLG_RX0OVR = (1 << 6), + EFLG_TXBO = (1 << 5), + EFLG_TXEP = (1 << 4), + EFLG_RXEP = (1 << 3), + EFLG_TXWAR = (1 << 2), + EFLG_RXWAR = (1 << 1), + EFLG_EWARN = (1 << 0) +}; + +enum STAT : uint8_t { STAT_RX0IF = (1 << 0), STAT_RX1IF = (1 << 1) }; + +static const uint8_t STAT_RXIF_MASK = STAT_RX0IF | STAT_RX1IF; +static const uint8_t EFLG_ERRORMASK = EFLG_RX1OVR | EFLG_RX0OVR | EFLG_TXBO | EFLG_TXEP | EFLG_RXEP; + +class MCP2515 : public canbus::Canbus, + public spi::SPIDevice { + public: + MCP2515(){}; + void set_mcp_clock(CanClock clock) { this->mcp_clock_ = clock; }; + void set_mcp_mode(const CanctrlReqopMode mode) { this->mcp_mode_ = mode; } + static const struct TxBnRegs { + REGISTER CTRL; + REGISTER SIDH; + REGISTER DATA; + } TXB[N_TXBUFFERS]; + + static const struct RxBnRegs { + REGISTER CTRL; + REGISTER SIDH; + REGISTER DATA; + CANINTF CANINTF_RXnIF; + } RXB[N_RXBUFFERS]; + + protected: + CanClock mcp_clock_{MCP_8MHZ}; + CanctrlReqopMode mcp_mode_ = CANCTRL_REQOP_NORMAL; + bool setup_internal() override; + canbus::Error set_mode_(CanctrlReqopMode mode); + + uint8_t read_register_(REGISTER reg); + void read_registers_(REGISTER reg, uint8_t values[], uint8_t n); + void set_register_(REGISTER reg, uint8_t value); + void set_registers_(REGISTER reg, uint8_t values[], uint8_t n); + void modify_register_(REGISTER reg, uint8_t mask, uint8_t data); + + void prepare_id_(uint8_t *buffer, bool extended, uint32_t id); + canbus::Error reset_(); + canbus::Error set_clk_out_(CanClkOut divisor); + canbus::Error set_bitrate_(canbus::CanSpeed can_speed); + canbus::Error set_bitrate_(canbus::CanSpeed can_speed, CanClock can_clock); + canbus::Error set_filter_mask_(MASK mask, bool extended, uint32_t ul_data); + canbus::Error set_filter_(RXF num, bool extended, uint32_t ul_data); + canbus::Error send_message_(TXBn txbn, struct canbus::CanFrame *frame); + canbus::Error send_message(struct canbus::CanFrame *frame) override; + canbus::Error read_message_(RXBn rxbn, struct canbus::CanFrame *frame); + canbus::Error read_message(struct canbus::CanFrame *frame) override; + bool check_receive_(); + bool check_error_(); + uint8_t get_error_flags_(); + void clear_rx_n_ovr_flags_(); + uint8_t get_int_(); + uint8_t get_int_mask_(); + void clear_int_(); + void clear_tx_int_(); + uint8_t get_status_(); + void clear_rx_n_ovr_(); + void clear_merr_(); + void clear_errif_(); +}; +} // namespace mcp2515 +} // namespace esphome diff --git a/esphome/components/mcp2515/mcp2515_defs.h b/esphome/components/mcp2515/mcp2515_defs.h new file mode 100644 index 0000000000..454c760c6d --- /dev/null +++ b/esphome/components/mcp2515/mcp2515_defs.h @@ -0,0 +1,317 @@ +#pragma once + +namespace esphome { +namespace mcp2515 { + +static const uint8_t CANCTRL_REQOP = 0xE0; +static const uint8_t CANCTRL_ABAT = 0x10; +static const uint8_t CANCTRL_OSM = 0x08; +static const uint8_t CANCTRL_CLKEN = 0x04; +static const uint8_t CANCTRL_CLKPRE = 0x03; + +enum CanctrlReqopMode : uint8_t { + CANCTRL_REQOP_NORMAL = 0x00, + CANCTRL_REQOP_SLEEP = 0x20, + CANCTRL_REQOP_LOOPBACK = 0x40, + CANCTRL_REQOP_LISTENONLY = 0x60, + CANCTRL_REQOP_CONFIG = 0x80, + CANCTRL_REQOP_POWERUP = 0xE0 +}; + +enum TxbNCtrl : uint8_t { + TXB_ABTF = 0x40, + TXB_MLOA = 0x20, + TXB_TXERR = 0x10, + TXB_TXREQ = 0x08, + TXB_TXIE = 0x04, + TXB_TXP = 0x03 +}; + +enum INSTRUCTION : uint8_t { + INSTRUCTION_WRITE = 0x02, + INSTRUCTION_READ = 0x03, + INSTRUCTION_BITMOD = 0x05, + INSTRUCTION_LOAD_TX0 = 0x40, + INSTRUCTION_LOAD_TX1 = 0x42, + INSTRUCTION_LOAD_TX2 = 0x44, + INSTRUCTION_RTS_TX0 = 0x81, + INSTRUCTION_RTS_TX1 = 0x82, + INSTRUCTION_RTS_TX2 = 0x84, + INSTRUCTION_RTS_ALL = 0x87, + INSTRUCTION_READ_RX0 = 0x90, + INSTRUCTION_READ_RX1 = 0x94, + INSTRUCTION_READ_STATUS = 0xA0, + INSTRUCTION_RX_STATUS = 0xB0, + INSTRUCTION_RESET = 0xC0 +}; + +enum REGISTER : uint8_t { + MCP_RXF0SIDH = 0x00, + MCP_RXF0SIDL = 0x01, + MCP_RXF0EID8 = 0x02, + MCP_RXF0EID0 = 0x03, + MCP_RXF1SIDH = 0x04, + MCP_RXF1SIDL = 0x05, + MCP_RXF1EID8 = 0x06, + MCP_RXF1EID0 = 0x07, + MCP_RXF2SIDH = 0x08, + MCP_RXF2SIDL = 0x09, + MCP_RXF2EID8 = 0x0A, + MCP_RXF2EID0 = 0x0B, + MCP_CANSTAT = 0x0E, + MCP_CANCTRL = 0x0F, + MCP_RXF3SIDH = 0x10, + MCP_RXF3SIDL = 0x11, + MCP_RXF3EID8 = 0x12, + MCP_RXF3EID0 = 0x13, + MCP_RXF4SIDH = 0x14, + MCP_RXF4SIDL = 0x15, + MCP_RXF4EID8 = 0x16, + MCP_RXF4EID0 = 0x17, + MCP_RXF5SIDH = 0x18, + MCP_RXF5SIDL = 0x19, + MCP_RXF5EID8 = 0x1A, + MCP_RXF5EID0 = 0x1B, + MCP_TEC = 0x1C, + MCP_REC = 0x1D, + MCP_RXM0SIDH = 0x20, + MCP_RXM0SIDL = 0x21, + MCP_RXM0EID8 = 0x22, + MCP_RXM0EID0 = 0x23, + MCP_RXM1SIDH = 0x24, + MCP_RXM1SIDL = 0x25, + MCP_RXM1EID8 = 0x26, + MCP_RXM1EID0 = 0x27, + MCP_CNF3 = 0x28, + MCP_CNF2 = 0x29, + MCP_CNF1 = 0x2A, + MCP_CANINTE = 0x2B, + MCP_CANINTF = 0x2C, + MCP_EFLG = 0x2D, + MCP_TXB0CTRL = 0x30, + MCP_TXB0SIDH = 0x31, + MCP_TXB0SIDL = 0x32, + MCP_TXB0EID8 = 0x33, + MCP_TXB0EID0 = 0x34, + MCP_TXB0DLC = 0x35, + MCP_TXB0DATA = 0x36, + MCP_TXB1CTRL = 0x40, + MCP_TXB1SIDH = 0x41, + MCP_TXB1SIDL = 0x42, + MCP_TXB1EID8 = 0x43, + MCP_TXB1EID0 = 0x44, + MCP_TXB1DLC = 0x45, + MCP_TXB1DATA = 0x46, + MCP_TXB2CTRL = 0x50, + MCP_TXB2SIDH = 0x51, + MCP_TXB2SIDL = 0x52, + MCP_TXB2EID8 = 0x53, + MCP_TXB2EID0 = 0x54, + MCP_TXB2DLC = 0x55, + MCP_TXB2DATA = 0x56, + MCP_RXB0CTRL = 0x60, + MCP_RXB0SIDH = 0x61, + MCP_RXB0SIDL = 0x62, + MCP_RXB0EID8 = 0x63, + MCP_RXB0EID0 = 0x64, + MCP_RXB0DLC = 0x65, + MCP_RXB0DATA = 0x66, + MCP_RXB1CTRL = 0x70, + MCP_RXB1SIDH = 0x71, + MCP_RXB1SIDL = 0x72, + MCP_RXB1EID8 = 0x73, + MCP_RXB1EID0 = 0x74, + MCP_RXB1DLC = 0x75, + MCP_RXB1DATA = 0x76 +}; + +static const uint8_t CANSTAT_OPMOD = 0xE0; +static const uint8_t CANSTAT_ICOD = 0x0E; + +static const uint8_t CNF3_SOF = 0x80; + +static const uint8_t TXB_EXIDE_MASK = 0x08; +static const uint8_t DLC_MASK = 0x0F; +static const uint8_t RTR_MASK = 0x40; + +static const uint8_t RXB_CTRL_RXM_STD = 0x20; +static const uint8_t RXB_CTRL_RXM_EXT = 0x40; +static const uint8_t RXB_CTRL_RXM_STDEXT = 0x00; +static const uint8_t RXB_CTRL_RXM_MASK = 0x60; +static const uint8_t RXB_CTRL_RTR = 0x08; +static const uint8_t RXB_0_CTRL_BUKT = 0x04; + +static const uint8_t MCP_SIDH = 0; +static const uint8_t MCP_SIDL = 1; +static const uint8_t MCP_EID8 = 2; +static const uint8_t MCP_EID0 = 3; +static const uint8_t MCP_DLC = 4; +static const uint8_t MCP_DATA = 5; + +/* + * Speed 8M + */ +static const uint8_t MCP_8MHZ_1000KBPS_CFG1 = 0x00; +static const uint8_t MCP_8MHZ_1000KBPS_CFG2 = 0x80; +static const uint8_t MCP_8MHZ_1000KBPS_CFG3 = 0x80; + +static const uint8_t MCP_8MHZ_500KBPS_CFG1 = 0x00; +static const uint8_t MCP_8MHZ_500KBPS_CFG2 = 0x90; +static const uint8_t MCP_8MHZ_500KBPS_CFG3 = 0x82; + +static const uint8_t MCP_8MHZ_250KBPS_CFG1 = 0x00; +static const uint8_t MCP_8MHZ_250KBPS_CFG2 = 0xB1; +static const uint8_t MCP_8MHZ_250KBPS_CFG3 = 0x85; + +static const uint8_t MCP_8MHZ_200KBPS_CFG1 = 0x00; +static const uint8_t MCP_8MHZ_200KBPS_CFG2 = 0xB4; +static const uint8_t MCP_8MHZ_200KBPS_CFG3 = 0x86; + +static const uint8_t MCP_8MHZ_125KBPS_CFG1 = 0x01; +static const uint8_t MCP_8MHZ_125KBPS_CFG2 = 0xB1; +static const uint8_t MCP_8MHZ_125KBPS_CFG3 = 0x85; + +static const uint8_t MCP_8MHZ_100KBPS_CFG1 = 0x01; +static const uint8_t MCP_8MHZ_100KBPS_CFG2 = 0xB4; +static const uint8_t MCP_8MHZ_100KBPS_CFG3 = 0x86; + +static const uint8_t MCP_8MHZ_80KBPS_CFG1 = 0x01; +static const uint8_t MCP_8MHZ_80KBPS_CFG2 = 0xBF; +static const uint8_t MCP_8MHZ_80KBPS_CFG3 = 0x87; + +static const uint8_t MCP_8MHZ_50KBPS_CFG1 = 0x03; +static const uint8_t MCP_8MHZ_50KBPS_CFG2 = 0xB4; +static const uint8_t MCP_8MHZ_50KBPS_CFG3 = 0x86; + +static const uint8_t MCP_8MHZ_40KBPS_CFG1 = 0x03; +static const uint8_t MCP_8MHZ_40KBPS_CFG2 = 0xBF; +static const uint8_t MCP_8MHZ_40KBPS_CFG3 = 0x87; + +static const uint8_t MCP_8MHZ_33K3BPS_CFG1 = 0x47; +static const uint8_t MCP_8MHZ_33K3BPS_CFG2 = 0xE2; +static const uint8_t MCP_8MHZ_33K3BPS_CFG3 = 0x85; + +static const uint8_t MCP_8MHZ_31K25BPS_CFG1 = 0x07; +static const uint8_t MCP_8MHZ_31K25BPS_CFG2 = 0xA4; +static const uint8_t MCP_8MHZ_31K25BPS_CFG3 = 0x84; + +static const uint8_t MCP_8MHZ_20KBPS_CFG1 = 0x07; +static const uint8_t MCP_8MHZ_20KBPS_CFG2 = 0xBF; +static const uint8_t MCP_8MHZ_20KBPS_CFG3 = 0x87; + +static const uint8_t MCP_8MHZ_10KBPS_CFG1 = 0x0F; +static const uint8_t MCP_8MHZ_10KBPS_CFG2 = 0xBF; +static const uint8_t MCP_8MHZ_10KBPS_CFG3 = 0x87; + +static const uint8_t MCP_8MHZ_5KBPS_CFG1 = 0x1F; +static const uint8_t MCP_8MHZ_5KBPS_CFG2 = 0xBF; +static const uint8_t MCP_8MHZ_5KBPS_CFG3 = 0x87; + +/* + * speed 16M + */ +static const uint8_t MCP_16MHZ_1000KBPS_CFG1 = 0x00; +static const uint8_t MCP_16MHZ_1000KBPS_CFG2 = 0xD0; +static const uint8_t MCP_16MHZ_1000KBPS_CFG3 = 0x82; + +static const uint8_t MCP_16MHZ_500KBPS_CFG1 = 0x00; +static const uint8_t MCP_16MHZ_500KBPS_CFG2 = 0xF0; +static const uint8_t MCP_16MHZ_500KBPS_CFG3 = 0x86; + +static const uint8_t MCP_16MHZ_250KBPS_CFG1 = 0x41; +static const uint8_t MCP_16MHZ_250KBPS_CFG2 = 0xF1; +static const uint8_t MCP_16MHZ_250KBPS_CFG3 = 0x85; + +static const uint8_t MCP_16MHZ_200KBPS_CFG1 = 0x01; +static const uint8_t MCP_16MHZ_200KBPS_CFG2 = 0xFA; +static const uint8_t MCP_16MHZ_200KBPS_CFG3 = 0x87; + +static const uint8_t MCP_16MHZ_125KBPS_CFG1 = 0x03; +static const uint8_t MCP_16MHZ_125KBPS_CFG2 = 0xF0; +static const uint8_t MCP_16MHZ_125KBPS_CFG3 = 0x86; + +static const uint8_t MCP_16MHZ_100KBPS_CFG1 = 0x03; +static const uint8_t MCP_16MHZ_100KBPS_CFG2 = 0xFA; +static const uint8_t MCP_16MHZ_100KBPS_CFG3 = 0x87; + +static const uint8_t MCP_16MHZ_80KBPS_CFG1 = 0x03; +static const uint8_t MCP_16MHZ_80KBPS_CFG2 = 0xFF; +static const uint8_t MCP_16MHZ_80KBPS_CFG3 = 0x87; + +static const uint8_t MCP_16MHZ_83K3BPS_CFG1 = 0x03; +static const uint8_t MCP_16MHZ_83K3BPS_CFG2 = 0xBE; +static const uint8_t MCP_16MHZ_83K3BPS_CFG3 = 0x07; + +static const uint8_t MCP_16MHZ_50KBPS_CFG1 = 0x07; +static const uint8_t MCP_16MHZ_50KBPS_CFG2 = 0xFA; +static const uint8_t MCP_16MHZ_50KBPS_CFG3 = 0x87; + +static const uint8_t MCP_16MHZ_40KBPS_CFG1 = 0x07; +static const uint8_t MCP_16MHZ_40KBPS_CFG2 = 0xFF; +static const uint8_t MCP_16MHZ_40KBPS_CFG3 = 0x87; + +static const uint8_t MCP_16MHZ_33K3BPS_CFG1 = 0x4E; +static const uint8_t MCP_16MHZ_33K3BPS_CFG2 = 0xF1; +static const uint8_t MCP_16MHZ_33K3BPS_CFG3 = 0x85; + +static const uint8_t MCP_16MHZ_20KBPS_CFG1 = 0x0F; +static const uint8_t MCP_16MHZ_20KBPS_CFG2 = 0xFF; +static const uint8_t MCP_16MHZ_20KBPS_CFG3 = 0x87; + +static const uint8_t MCP_16MHZ_10KBPS_CFG1 = 0x1F; +static const uint8_t MCP_16MHZ_10KBPS_CFG2 = 0xFF; +static const uint8_t MCP_16MHZ_10KBPS_CFG3 = 0x87; + +static const uint8_t MCP_16MHZ_5KBPS_CFG1 = 0x3F; +static const uint8_t MCP_16MHZ_5KBPS_CFG2 = 0xFF; +static const uint8_t MCP_16MHZ_5KBPS_CFG3 = 0x87; + +/* + * speed 20M + */ +static const uint8_t MCP_20MHZ_1000KBPS_CFG1 = 0x00; +static const uint8_t MCP_20MHZ_1000KBPS_CFG2 = 0xD9; +static const uint8_t MCP_20MHZ_1000KBPS_CFG3 = 0x82; + +static const uint8_t MCP_20MHZ_500KBPS_CFG1 = 0x00; +static const uint8_t MCP_20MHZ_500KBPS_CFG2 = 0xFA; +static const uint8_t MCP_20MHZ_500KBPS_CFG3 = 0x87; + +static const uint8_t MCP_20MHZ_250KBPS_CFG1 = 0x41; +static const uint8_t MCP_20MHZ_250KBPS_CFG2 = 0xFB; +static const uint8_t MCP_20MHZ_250KBPS_CFG3 = 0x86; + +static const uint8_t MCP_20MHZ_200KBPS_CFG1 = 0x01; +static const uint8_t MCP_20MHZ_200KBPS_CFG2 = 0xFF; +static const uint8_t MCP_20MHZ_200KBPS_CFG3 = 0x87; + +static const uint8_t MCP_20MHZ_125KBPS_CFG1 = 0x03; +static const uint8_t MCP_20MHZ_125KBPS_CFG2 = 0xFA; +static const uint8_t MCP_20MHZ_125KBPS_CFG3 = 0x87; + +static const uint8_t MCP_20MHZ_100KBPS_CFG1 = 0x04; +static const uint8_t MCP_20MHZ_100KBPS_CFG2 = 0xFA; +static const uint8_t MCP_20MHZ_100KBPS_CFG3 = 0x87; + +static const uint8_t MCP_20MHZ_83K3BPS_CFG1 = 0x04; +static const uint8_t MCP_20MHZ_83K3BPS_CFG2 = 0xFE; +static const uint8_t MCP_20MHZ_83K3BPS_CFG3 = 0x87; + +static const uint8_t MCP_20MHZ_80KBPS_CFG1 = 0x04; +static const uint8_t MCP_20MHZ_80KBPS_CFG2 = 0xFF; +static const uint8_t MCP_20MHZ_80KBPS_CFG3 = 0x87; + +static const uint8_t MCP_20MHZ_50KBPS_CFG1 = 0x09; +static const uint8_t MCP_20MHZ_50KBPS_CFG2 = 0xFA; +static const uint8_t MCP_20MHZ_50KBPS_CFG3 = 0x87; + +static const uint8_t MCP_20MHZ_40KBPS_CFG1 = 0x09; +static const uint8_t MCP_20MHZ_40KBPS_CFG2 = 0xFF; +static const uint8_t MCP_20MHZ_40KBPS_CFG3 = 0x87; + +static const uint8_t MCP_20MHZ_33K3BPS_CFG1 = 0x0B; +static const uint8_t MCP_20MHZ_33K3BPS_CFG2 = 0xFF; +static const uint8_t MCP_20MHZ_33K3BPS_CFG3 = 0x87; + +} // namespace mcp2515 +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index 3982f228e7..612701a8c3 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1890,6 +1890,9 @@ text_sensor: - globals.set: id: glob_int value: '0' + - canbus.send: + can_id: 23 + data: [ 0x10, 0x20, 0x30 ] - platform: template name: Template Text Sensor id: template_text @@ -1916,3 +1919,22 @@ sn74hc595: rtttl: output: gpio_19 + +canbus: + - platform: mcp2515 + cs_pin: GPIO17 + 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: living_room_lights