Add automations

This commit is contained in:
Jonathan Swoboda 2024-10-21 09:45:35 -04:00
parent 19b02227a2
commit 483510e018
4 changed files with 152 additions and 11 deletions

View file

@ -2,7 +2,7 @@ from esphome import automation, pins
import esphome.codegen as cg
from esphome.components import spi
import esphome.config_validation as cv
from esphome.const import CONF_FREQUENCY, CONF_ID
from esphome.const import CONF_DATA, CONF_FREQUENCY, CONF_ID
from esphome.core import TimePeriod
CODEOWNERS = ["@swoboda1337"]
@ -48,7 +48,7 @@ SHAPING = {
"GAUSSIAN_BT_0_3": SX127xPaRamp.GAUSSIAN_BT_0_3,
"GAUSSIAN_BT_0_5": SX127xPaRamp.GAUSSIAN_BT_0_5,
"GAUSSIAN_BT_1_0": SX127xPaRamp.GAUSSIAN_BT_1_0,
"NO_SHAPING": SX127xPaRamp.NO_SHAPING,
"NONE": SX127xPaRamp.SHAPING_NONE,
}
RAMP = {
@ -99,6 +99,26 @@ RX_BW = {
"250_0kHz": SX127xRxBw.RX_BW_250_0,
}
SendPacketAction = sx127x_ns.class_(
"SendPacketAction", automation.Action, cg.Parented.template(SX127x)
)
SetModeTxAction = sx127x_ns.class_("SetModeTxAction", automation.Action)
SetModeRxAction = sx127x_ns.class_("SetModeRxAction", automation.Action)
SetModeStandbyAction = sx127x_ns.class_("SetModeStandbyAction", automation.Action)
def validate_raw_data(value):
if isinstance(value, str):
return value.encode("utf-8")
if isinstance(value, str):
return value
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"
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(SX127x),
@ -108,7 +128,7 @@ CONFIG_SCHEMA = cv.Schema(
cv.Required(CONF_NSS_PIN): pins.internal_gpio_output_pin_schema,
cv.Required(CONF_FREQUENCY): cv.int_range(min=137000000, max=1020000000),
cv.Required(CONF_MODULATION): cv.enum(MOD),
cv.Optional(CONF_SHAPING, default="NO_SHAPING"): cv.enum(SHAPING),
cv.Optional(CONF_SHAPING, default="NONE"): cv.enum(SHAPING),
cv.Optional(CONF_BITRATE, default=0): cv.int_range(min=0, max=300000),
cv.Optional(CONF_FSK_FDEV, default=5000): cv.int_range(min=0, max=100000),
cv.Optional(CONF_FSK_RAMP, default="40us"): cv.enum(RAMP),
@ -170,3 +190,51 @@ async def to_code(config):
cg.add(var.set_pa_power(config[CONF_PA_POWER]))
cg.add(var.set_fsk_fdev(config[CONF_FSK_FDEV]))
cg.add(var.set_fsk_ramp(config[CONF_FSK_RAMP]))
SET_MODE_ACTION_SCHEMA = automation.maybe_simple_id(
{
cv.GenerateID(): cv.use_id(SX127x),
}
)
@automation.register_action(
"sx127x.set_mode_tx", SetModeTxAction, SET_MODE_ACTION_SCHEMA
)
@automation.register_action(
"sx127x.set_mode_rx", SetModeRxAction, SET_MODE_ACTION_SCHEMA
)
@automation.register_action(
"sx127x.set_mode_standby", SetModeStandbyAction, SET_MODE_ACTION_SCHEMA
)
async def set_mode_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
return var
SEND_PACKET_ACTION_SCHEMA = cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(SX127x),
cv.Required(CONF_DATA): cv.templatable(validate_raw_data),
},
key=CONF_DATA,
)
@automation.register_action(
"sx127x.send_packet", SendPacketAction, SEND_PACKET_ACTION_SCHEMA
)
async def send_packet_action_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
data = config[CONF_DATA]
if isinstance(data, bytes):
data = list(data)
if cg.is_template(data):
templ = await 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))
return var

View file

@ -0,0 +1,66 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/sx127x/sx127x.h"
namespace esphome {
namespace sx127x {
template<typename... Ts> class SendPacketAction : public Action<Ts...>, public Parented<SX127x> {
public:
void set_data_template(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 play(Ts... x) override {
if (this->static_) {
this->parent_->transmit_packet(this->data_static_);
} else {
this->parent_->transmit_packet(this->data_func_(x...));
}
}
protected:
bool static_{false};
std::function<std::vector<uint8_t>(Ts...)> data_func_{};
std::vector<uint8_t> data_static_{};
};
template<typename... Ts> class SetModeTxAction : public Action<Ts...> {
public:
SetModeTxAction(SX127x *sx127x) : sx127x_(sx127x) {}
void play(Ts... x) override { this->sx127x_->set_mode_tx(); }
protected:
SX127x *sx127x_;
};
template<typename... Ts> class SetModeRxAction : public Action<Ts...> {
public:
SetModeRxAction(SX127x *sx127x) : sx127x_(sx127x) {}
void play(Ts... x) override { this->sx127x_->set_mode_rx(); }
protected:
SX127x *sx127x_;
};
template<typename... Ts> class SetModeStandbyAction : public Action<Ts...> {
public:
SetModeStandbyAction(SX127x *sx127x) : sx127x_(sx127x) {}
void play(Ts... x) override { this->sx127x_->set_mode_standby(); }
protected:
SX127x *sx127x_;
};
} // namespace sx127x
} // namespace esphome

View file

@ -8,7 +8,7 @@ namespace sx127x {
static const char *const TAG = "sx127x";
void IRAM_ATTR HOT SX127xStore::gpio_intr(SX127xStore *arg) {
if (arg->dio2_toggle) {
if (arg->dio2_set && arg->dio2_toggle) {
arg->dio2_pin.pin_mode(gpio::FLAG_INPUT);
}
arg->dio0_micros = micros();
@ -73,7 +73,7 @@ void SX127x::setup() {
this->dio2_pin_->setup();
this->dio2_pin_->pin_mode(gpio::FLAG_OPEN_DRAIN);
this->store_.dio2_pin = this->dio2_pin_->to_isr();
this->store_.dio2_toggle = true;
this->store_.dio2_set = true;
}
// start spi
@ -202,6 +202,7 @@ void SX127x::configure() {
// clear irq flag
this->store_.dio0_irq = false;
this->store_.dio2_toggle = (this->rx_duration_ > 0 && this->payload_length_ == 0);
// enable standby mode
this->set_mode_standby();
@ -260,7 +261,11 @@ void SX127x::set_mode_rx() {
if (this->dio2_pin_) {
this->write_register_(REG_OP_MODE, this->modulation_ | MODE_STDBY);
delay(1);
this->dio2_pin_->pin_mode(this->modulation_ == MOD_OOK ? gpio::FLAG_INPUT : gpio::FLAG_OPEN_DRAIN);
if (this->rx_duration_ > 0 && this->payload_length_ == 0) {
this->dio2_pin_->pin_mode(gpio::FLAG_OPEN_DRAIN);
} else {
this->dio2_pin_->pin_mode(gpio::FLAG_INPUT);
}
}
this->write_register_(REG_OP_MODE, this->modulation_ | MODE_RX_FS);
delay(1);
@ -303,11 +308,11 @@ void SX127x::dump_config() {
ESP_LOGCONFIG(TAG, " Sync Value: 0x%s", format_hex(this->sync_value_).c_str());
}
if (this->modulation_ == MOD_FSK) {
static const char *shaping_lut[4] = {"NO_SHAPING", "GAUSSIAN_BT_1_0", "GAUSSIAN_BT_0_5", "GAUSSIAN_BT_0_3"};
ESP_LOGCONFIG(TAG, " Shaping: %s", shaping_lut[this->shaping_ >> 5]);
static const char *shaping_lut[4] = {"NONE", "GAUSSIAN_BT_1_0", "GAUSSIAN_BT_0_5", "GAUSSIAN_BT_0_3"};
ESP_LOGCONFIG(TAG, " Shaping: %s", shaping_lut[this->shaping_ >> SHAPING_SHIFT]);
} else {
static const char *shaping_lut[4] = {"NO_SHAPING", "CUTOFF_BR_X_1", "CUTOFF_BR_X_2", "ERROR"};
ESP_LOGCONFIG(TAG, " Shaping: %s", shaping_lut[this->shaping_ >> 5]);
static const char *shaping_lut[4] = {"NONE", "CUTOFF_BR_X_1", "CUTOFF_BR_X_2", "ERROR"};
ESP_LOGCONFIG(TAG, " Shaping: %s", shaping_lut[this->shaping_ >> SHAPING_SHIFT]);
}
ESP_LOGCONFIG(TAG, " PA Pin: %s", this->pa_pin_ == PA_PIN_BOOST ? "BOOST" : "RFO");
ESP_LOGCONFIG(TAG, " PA Power: %" PRIu32 " dBm", this->pa_power_);

View file

@ -183,7 +183,8 @@ enum SX127xPaRamp : uint8_t {
GAUSSIAN_BT_0_3 = 0x60,
GAUSSIAN_BT_0_5 = 0x40,
GAUSSIAN_BT_1_0 = 0x20,
NO_SHAPING = 0x00,
SHAPING_NONE = 0x00,
SHAPING_SHIFT = 0x05,
PA_RAMP_10 = 0x0F,
PA_RAMP_12 = 0x0E,
PA_RAMP_15 = 0x0D,
@ -206,6 +207,7 @@ struct SX127xStore {
static void gpio_intr(SX127xStore *arg);
volatile uint32_t dio0_micros{0};
volatile bool dio0_irq{false};
volatile bool dio2_set{false};
volatile bool dio2_toggle{false};
ISRInternalGPIOPin dio2_pin;
};