From 89d6df58c55925b722fbdf0294c6d3040ca1a788 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda Date: Mon, 23 Sep 2024 21:54:19 -0400 Subject: [PATCH] Add sx127x component --- esphome/components/sx127x/__init__.py | 125 ++++++++++++++++++++ esphome/components/sx127x/sx127x.cpp | 155 +++++++++++++++++++++++++ esphome/components/sx127x/sx127x.h | 158 ++++++++++++++++++++++++++ 3 files changed, 438 insertions(+) create mode 100644 esphome/components/sx127x/__init__.py create mode 100644 esphome/components/sx127x/sx127x.cpp create mode 100644 esphome/components/sx127x/sx127x.h diff --git a/esphome/components/sx127x/__init__.py b/esphome/components/sx127x/__init__.py new file mode 100644 index 0000000000..1568c310cf --- /dev/null +++ b/esphome/components/sx127x/__init__.py @@ -0,0 +1,125 @@ +from esphome import 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 + +CODEOWNERS = ["@swoboda1337"] +DEPENDENCIES = ["spi"] + +CONF_PA_POWER = "pa_power" +CONF_PA_PIN = "pa_pin" +CONF_NSS_PIN = "nss_pin" +CONF_RST_PIN = "rst_pin" +CONF_MODULATION = "modulation" +CONF_RX_FLOOR = "rx_floor" +CONF_RX_START = "rx_start" +CONF_RX_BANDWIDTH = "rx_bandwidth" +CONF_FSK_FDEV = "fsk_fdev" +CONF_FSK_RAMP = "fsk_ramp" +CONF_FSK_SHAPING = "fsk_shaping" + +sx127x_ns = cg.esphome_ns.namespace("sx127x") +SX127x = sx127x_ns.class_("SX127x", cg.Component, spi.SPIDevice) +SX127xOpMode = sx127x_ns.enum("SX127xOpMode") +SX127xRxBw = sx127x_ns.enum("SX127xRxBw") +SX127xPaConfig = sx127x_ns.enum("SX127xPaConfig") +SX127xPaRamp = sx127x_ns.enum("SX127xPaRamp") + +PA_PIN = { + "RFO": SX127xPaConfig.PA_PIN_RFO, + "BOOST": SX127xPaConfig.PA_PIN_BOOST, +} + +SHAPING = { + "BT_0_3": SX127xPaRamp.SHAPING_BT_0_3, + "BT_0_5": SX127xPaRamp.SHAPING_BT_0_5, + "BT_1_0": SX127xPaRamp.SHAPING_BT_1_0, + "NONE": SX127xPaRamp.SHAPING_NONE, +} + +RAMP = { + "10us": SX127xPaRamp.PA_RAMP_10, + "12us": SX127xPaRamp.PA_RAMP_12, + "15us": SX127xPaRamp.PA_RAMP_15, + "20us": SX127xPaRamp.PA_RAMP_20, + "25us": SX127xPaRamp.PA_RAMP_25, + "31us": SX127xPaRamp.PA_RAMP_31, + "40us": SX127xPaRamp.PA_RAMP_40, + "50us": SX127xPaRamp.PA_RAMP_50, + "62us": SX127xPaRamp.PA_RAMP_62, + "100us": SX127xPaRamp.PA_RAMP_100, + "125us": SX127xPaRamp.PA_RAMP_125, + "250us": SX127xPaRamp.PA_RAMP_250, + "500us": SX127xPaRamp.PA_RAMP_500, + "1000us": SX127xPaRamp.PA_RAMP_1000, + "2000us": SX127xPaRamp.PA_RAMP_2000, + "3400us": SX127xPaRamp.PA_RAMP_3400, +} + +MOD = { + "FSK": SX127xOpMode.MOD_FSK, + "OOK": SX127xOpMode.MOD_OOK, +} + +RX_BW = { + "2_6kHz": SX127xRxBw.RX_BW_2_6, + "3_1kHz": SX127xRxBw.RX_BW_3_1, + "3_9kHz": SX127xRxBw.RX_BW_3_9, + "5_2kHz": SX127xRxBw.RX_BW_5_2, + "6_3kHz": SX127xRxBw.RX_BW_6_3, + "7_8kHz": SX127xRxBw.RX_BW_7_8, + "10_4kHz": SX127xRxBw.RX_BW_10_4, + "12_5kHz": SX127xRxBw.RX_BW_12_5, + "15_6kHz": SX127xRxBw.RX_BW_15_6, + "20_8kHz": SX127xRxBw.RX_BW_20_8, + "25_0kHz": SX127xRxBw.RX_BW_25_0, + "31_3kHz": SX127xRxBw.RX_BW_31_3, + "41_7kHz": SX127xRxBw.RX_BW_41_7, + "50_0kHz": SX127xRxBw.RX_BW_50_0, + "62_5kHz": SX127xRxBw.RX_BW_62_5, + "83_3kHz": SX127xRxBw.RX_BW_83_3, + "100_0kHz": SX127xRxBw.RX_BW_100_0, + "125_0kHz": SX127xRxBw.RX_BW_125_0, + "166_7kHz": SX127xRxBw.RX_BW_166_7, + "200_0kHz": SX127xRxBw.RX_BW_200_0, + "250_0kHz": SX127xRxBw.RX_BW_250_0, +} + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(SX127x), + cv.Required(CONF_RST_PIN): pins.internal_gpio_output_pin_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_FSK_FDEV, default=5000): cv.int_range(min=0, max=100000), + cv.Optional(CONF_FSK_RAMP, default="40us"): cv.enum(RAMP), + cv.Optional(CONF_FSK_SHAPING, default="NONE"): cv.enum(SHAPING), + cv.Optional(CONF_RX_FLOOR, default=-94): cv.float_range(min=-128, max=-1), + cv.Optional(CONF_RX_START, default=True): cv.boolean, + cv.Optional(CONF_RX_BANDWIDTH, default="50_0kHz"): cv.enum(RX_BW), + cv.Optional(CONF_PA_PIN, default="BOOST"): cv.enum(PA_PIN), + cv.Optional(CONF_PA_POWER, default=17): cv.int_range(min=0, max=17), + } +).extend(spi.spi_device_schema(False, 8e6, "mode0")) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await spi.register_spi_device(var, config) + rst_pin = await cg.gpio_pin_expression(config[CONF_RST_PIN]) + cg.add(var.set_rst_pin(rst_pin)) + nss_pin = await cg.gpio_pin_expression(config[CONF_NSS_PIN]) + cg.add(var.set_nss_pin(nss_pin)) + cg.add(var.set_frequency(config[CONF_FREQUENCY])) + cg.add(var.set_modulation(config[CONF_MODULATION])) + cg.add(var.set_rx_floor(config[CONF_RX_FLOOR])) + cg.add(var.set_rx_start(config[CONF_RX_START])) + cg.add(var.set_rx_bandwidth(config[CONF_RX_BANDWIDTH])) + cg.add(var.set_pa_pin(config[CONF_PA_PIN])) + 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])) + cg.add(var.set_fsk_shaping(config[CONF_FSK_SHAPING])) diff --git a/esphome/components/sx127x/sx127x.cpp b/esphome/components/sx127x/sx127x.cpp new file mode 100644 index 0000000000..2fbc758559 --- /dev/null +++ b/esphome/components/sx127x/sx127x.cpp @@ -0,0 +1,155 @@ +#include "sx127x.h" + +namespace esphome { +namespace sx127x { + +static const char *const TAG = "sx127x"; + +uint8_t SX127x::read_register_(uint8_t reg) { return this->single_transfer_((uint8_t) reg & 0x7f, 0x00); } + +void SX127x::write_register_(uint8_t reg, uint8_t value) { this->single_transfer_((uint8_t) reg | 0x80, value); } + +uint8_t SX127x::single_transfer_(uint8_t reg, uint8_t value) { + uint8_t response; + this->delegate_->begin_transaction(); + this->nss_pin_->digital_write(false); + this->delegate_->transfer(reg); + response = this->delegate_->transfer(value); + this->nss_pin_->digital_write(true); + this->delegate_->end_transaction(); + return response; +} + +void SX127x::setup() { + ESP_LOGCONFIG(TAG, "Setting up SX127x..."); + + // setup nss and set high + this->nss_pin_->setup(); + this->nss_pin_->digital_write(true); + + // setup reset + this->rst_pin_->setup(); + + // start spi + this->spi_setup(); + + // configure rf + this->configure(); +} + +void SX127x::configure() { + // toggle chip reset + this->rst_pin_->digital_write(false); + delay(1); + this->rst_pin_->digital_write(true); + delay(10); + + // check silicon version to make sure hw is ok + if (this->read_register_(REG_VERSION) != 0x12) { + this->mark_failed(); + return; + } + + // set modulation and make sure transceiver is in sleep mode + this->write_register_(REG_OP_MODE, this->modulation_ | MODE_LF_ON | MODE_SLEEP); + delay(1); + + // set freq + uint64_t frf = ((uint64_t) this->frequency_ << 19) / 32000000; + this->write_register_(REG_FRF_MSB, (uint8_t) ((frf >> 16) & 0xFF)); + this->write_register_(REG_FRF_MID, (uint8_t) ((frf >> 8) & 0xFF)); + this->write_register_(REG_FRF_LSB, (uint8_t) ((frf >> 0) & 0xFF)); + + // set fdev + uint32_t fdev = std::min(this->fsk_fdev_ / 61, 0x3FFFu); + this->write_register_(REG_FDEV_MSB, (uint8_t) ((fdev >> 8) & 0xFF)); + this->write_register_(REG_FDEV_LSB, (uint8_t) ((fdev >> 0) & 0xFF)); + + // set the channel bw + this->write_register_(REG_RX_BW, this->rx_bandwidth_); + + // config pa + if (this->pa_pin_ == PA_PIN_BOOST) { + this->pa_power_ = std::max(this->pa_power_, 2u); + this->pa_power_ = std::min(this->pa_power_, 17u); + this->write_register_(REG_PA_CONFIG, (this->pa_power_ - 2) | this->pa_pin_ | PA_MAX_POWER); + } else { + this->pa_power_ = std::min(this->pa_power_, 14u); + this->write_register_(REG_PA_CONFIG, (this->pa_power_ - 0) | this->pa_pin_ | PA_MAX_POWER); + } + if (this->modulation_ == MOD_FSK) { + this->write_register_(REG_PA_RAMP, this->fsk_ramp_ | this->fsk_shaping_); + } else { + this->write_register_(REG_PA_RAMP, this->fsk_ramp_); + } + + // disable packet mode + this->write_register_(REG_PACKET_CONFIG_1, 0x00); + this->write_register_(REG_PACKET_CONFIG_2, 0x00); + + // disable bit synchronizer, disable sync generation and setup threshold + this->write_register_(REG_SYNC_CONFIG, 0x00); + this->write_register_(REG_OOK_PEAK, OOK_THRESH_STEP_0_5 | OOK_THRESH_PEAK); + this->write_register_(REG_OOK_AVG, OOK_THRESH_DEC_1_8); + + // set ook floor + this->write_register_(REG_OOK_FIX, 256 + int(this->rx_floor_ * 2.0)); + + // enable standby mode + this->set_mode_standby(); + delay(1); + + // enable rx mode + if (this->rx_start_) { + this->set_mode_rx(); + delay(1); + } +} + +void SX127x::set_mode_standby() { this->write_register_(REG_OP_MODE, this->modulation_ | MODE_LF_ON | MODE_STDBY); } + +void SX127x::set_mode_rx() { + this->write_register_(REG_OP_MODE, this->modulation_ | MODE_LF_ON | MODE_RX_FS); + delay(1); + this->write_register_(REG_OP_MODE, this->modulation_ | MODE_LF_ON | MODE_RX); +} + +void SX127x::set_mode_tx() { + this->write_register_(REG_OP_MODE, this->modulation_ | MODE_LF_ON | MODE_TX_FS); + delay(1); + this->write_register_(REG_OP_MODE, this->modulation_ | MODE_LF_ON | MODE_TX); +} + +void SX127x::dump_config() { + static const uint16_t RAMP_LUT[16] = {3400, 2000, 1000, 500, 250, 125, 100, 62, 50, 40, 31, 25, 20, 15, 12, 10}; + uint32_t rx_bw_mant = 16 + (this->rx_bandwidth_ >> 3) * 4; + uint32_t rx_bw_exp = this->rx_bandwidth_ & 0x7; + float rx_bw = (float) 32000000 / (rx_bw_mant * (1 << (rx_bw_exp + 2))); + ESP_LOGCONFIG(TAG, "SX127x:"); + LOG_PIN(" NSS Pin: ", this->nss_pin_); + LOG_PIN(" RST Pin: ", this->rst_pin_); + ESP_LOGCONFIG(TAG, " PA Pin: %s", this->pa_pin_ == PA_PIN_BOOST ? "BOOST" : "RFO"); + ESP_LOGCONFIG(TAG, " PA Power: %d dBm", this->pa_power_); + ESP_LOGCONFIG(TAG, " Frequency: %f MHz", (float) this->frequency_ / 1000000); + ESP_LOGCONFIG(TAG, " Modulation: %s", this->modulation_ == MOD_FSK ? "FSK" : "OOK"); + ESP_LOGCONFIG(TAG, " Rx Bandwidth: %.1f kHz", (float) rx_bw / 1000); + ESP_LOGCONFIG(TAG, " Rx Start: %s", this->rx_start_ ? "true" : "false"); + ESP_LOGCONFIG(TAG, " Rx Floor: %.1f dBm", this->rx_floor_); + ESP_LOGCONFIG(TAG, " FSK Fdev: %d Hz", this->fsk_fdev_); + ESP_LOGCONFIG(TAG, " FSK Ramp: %d us", RAMP_LUT[this->fsk_ramp_]); + if (this->fsk_shaping_ == SHAPING_BT_1_0) { + ESP_LOGCONFIG(TAG, " FSK Shaping: BT_1_0"); + } else if (this->fsk_shaping_ == SHAPING_BT_0_5) { + ESP_LOGCONFIG(TAG, " FSK Shaping: BT_0_5"); + } else if (this->fsk_shaping_ == SHAPING_BT_0_3) { + ESP_LOGCONFIG(TAG, " FSK Shaping: BT_0_3"); + } else { + ESP_LOGCONFIG(TAG, " FSK Shaping: NONE"); + } + if (this->is_failed()) { + ESP_LOGE(TAG, "Configuring SX127x failed"); + } +} + +} // namespace sx127x +} // namespace esphome diff --git a/esphome/components/sx127x/sx127x.h b/esphome/components/sx127x/sx127x.h new file mode 100644 index 0000000000..8140b842fa --- /dev/null +++ b/esphome/components/sx127x/sx127x.h @@ -0,0 +1,158 @@ +#pragma once + +#include "esphome/components/spi/spi.h" + +namespace esphome { +namespace sx127x { + +enum SX127xReg : uint8_t { + REG_OP_MODE = 0x01, + REG_FDEV_MSB = 0x04, + REG_FDEV_LSB = 0x05, + REG_FRF_MSB = 0x06, + REG_FRF_MID = 0x07, + REG_FRF_LSB = 0x08, + REG_PA_CONFIG = 0x09, + REG_PA_RAMP = 0x0A, + REG_RX_BW = 0x12, + REG_OOK_PEAK = 0x14, + REG_OOK_FIX = 0x15, + REG_OOK_AVG = 0x16, + REG_SYNC_CONFIG = 0x27, + REG_PACKET_CONFIG_1 = 0x30, + REG_PACKET_CONFIG_2 = 0x31, + REG_VERSION = 0x42 +}; + +enum SX127xOpMode : uint8_t { + MOD_FSK = 0x00, + MOD_OOK = 0x20, + MODE_LF_ON = 0x08, + MODE_RX = 0x05, + MODE_RX_FS = 0x04, + MODE_TX = 0x03, + MODE_TX_FS = 0x02, + MODE_STDBY = 0x01, + MODE_SLEEP = 0x00 +}; + +enum SX127xOokPeak : uint8_t { + BIT_SYNC_ON = 0x20, + BIT_SYNC_OFF = 0x00, + OOK_THRESH_AVG = 0x10, + OOK_THRESH_PEAK = 0x08, + OOK_THRESH_FIXED = 0x00, + OOK_THRESH_STEP_6_0 = 0x07, + OOK_THRESH_STEP_5_0 = 0x06, + OOK_THRESH_STEP_4_0 = 0x05, + OOK_THRESH_STEP_3_0 = 0x04, + OOK_THRESH_STEP_2_0 = 0x03, + OOK_THRESH_STEP_1_5 = 0x02, + OOK_THRESH_STEP_1_0 = 0x01, + OOK_THRESH_STEP_0_5 = 0x00 +}; + +enum SX127xOokAvg : uint8_t { + OOK_THRESH_DEC_16 = 0xE0, + OOK_THRESH_DEC_8 = 0xC0, + OOK_THRESH_DEC_4 = 0xA0, + OOK_THRESH_DEC_2 = 0x80, + OOK_THRESH_DEC_1_8 = 0x60, + OOK_THRESH_DEC_1_4 = 0x40, + OOK_THRESH_DEC_1_2 = 0x20, + OOK_THRESH_DEC_1 = 0x00 +}; + +enum SX127xRxBw : uint8_t { + RX_BW_2_6 = 0x17, + RX_BW_3_1 = 0x0F, + RX_BW_3_9 = 0x07, + RX_BW_5_2 = 0x16, + RX_BW_6_3 = 0x0E, + RX_BW_7_8 = 0x06, + RX_BW_10_4 = 0x15, + RX_BW_12_5 = 0x0D, + RX_BW_15_6 = 0x05, + RX_BW_20_8 = 0x14, + RX_BW_25_0 = 0x0C, + RX_BW_31_3 = 0x04, + RX_BW_41_7 = 0x13, + RX_BW_50_0 = 0x0B, + RX_BW_62_5 = 0x03, + RX_BW_83_3 = 0x12, + RX_BW_100_0 = 0x0A, + RX_BW_125_0 = 0x02, + RX_BW_166_7 = 0x11, + RX_BW_200_0 = 0x09, + RX_BW_250_0 = 0x01 +}; + +enum SX127xPaRamp : uint8_t { + SHAPING_BT_0_3 = 0x60, + SHAPING_BT_0_5 = 0x40, + SHAPING_BT_1_0 = 0x20, + SHAPING_NONE = 0x00, + PA_RAMP_10 = 0x0F, + PA_RAMP_12 = 0x0E, + PA_RAMP_15 = 0x0D, + PA_RAMP_20 = 0x0C, + PA_RAMP_25 = 0x0B, + PA_RAMP_31 = 0x0A, + PA_RAMP_40 = 0x09, + PA_RAMP_50 = 0x08, + PA_RAMP_62 = 0x07, + PA_RAMP_100 = 0x06, + PA_RAMP_125 = 0x05, + PA_RAMP_250 = 0x04, + PA_RAMP_500 = 0x03, + PA_RAMP_1000 = 0x02, + PA_RAMP_2000 = 0x01, + PA_RAMP_3400 = 0x00 +}; + +enum SX127xPaConfig : uint8_t { PA_PIN_RFO = 0x00, PA_PIN_BOOST = 0x80, PA_MAX_POWER = 0x70 }; + +class SX127x : public Component, + public spi::SPIDevice { + public: + float get_setup_priority() const override { return setup_priority::HARDWARE; } + void setup() override; + void dump_config() override; + void set_rst_pin(InternalGPIOPin *rst_pin) { this->rst_pin_ = rst_pin; } + void set_nss_pin(InternalGPIOPin *nss_pin) { this->nss_pin_ = nss_pin; } + void set_frequency(uint32_t frequency) { this->frequency_ = frequency; } + void set_modulation(SX127xOpMode modulation) { this->modulation_ = modulation; } + void set_fsk_shaping(SX127xPaRamp shaping) { this->fsk_shaping_ = shaping; } + void set_fsk_ramp(SX127xPaRamp ramp) { this->fsk_ramp_ = ramp; } + void set_fsk_fdev(uint32_t fdev) { this->fsk_fdev_ = fdev; } + void set_rx_start(bool start) { this->rx_start_ = start; } + void set_rx_floor(float floor) { this->rx_floor_ = floor; } + void set_rx_bandwidth(SX127xRxBw bandwidth) { this->rx_bandwidth_ = bandwidth; } + void set_pa_pin(SX127xPaConfig pin) { this->pa_pin_ = pin; } + void set_pa_power(uint32_t power) { this->pa_power_ = power; } + void set_mode_standby(); + void set_mode_tx(); + void set_mode_rx(); + void configure(); + + protected: + void write_register_(uint8_t reg, uint8_t value); + uint8_t single_transfer_(uint8_t reg, uint8_t value); + uint8_t read_register_(uint8_t reg); + InternalGPIOPin *rst_pin_{nullptr}; + InternalGPIOPin *nss_pin_{nullptr}; + SX127xPaConfig pa_pin_; + SX127xRxBw rx_bandwidth_; + SX127xOpMode modulation_; + SX127xPaRamp fsk_shaping_; + SX127xPaRamp fsk_ramp_; + uint32_t fsk_fdev_; + uint32_t frequency_; + uint32_t pa_power_; + float rx_floor_; + bool rx_start_; +}; + +} // namespace sx127x +} // namespace esphome \ No newline at end of file