esphome/esphome/components/canbus/canbus.h

135 lines
3.8 KiB
C++

#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<typename... Ts> 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<uint8_t> &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<typename... Ts> friend class CanbusSendAction;
std::vector<CanbusTrigger *> 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<typename... Ts> class CanbusSendAction : public Action<Ts...>, public Parented<Canbus> {
public:
void set_data_template(const std::function<std::vector<uint8_t>(Ts...)> func) {
this->data_func_ = func;
this->static_ = false;
}
void set_data_static(const std::vector<uint8_t> &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<uint32_t> can_id_{};
optional<bool> use_extended_id_{};
bool static_{false};
std::function<std::vector<uint8_t>(Ts...)> data_func_{};
std::vector<uint8_t> data_static_{};
};
class CanbusTrigger : public Trigger<std::vector<uint8_t>>, 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