mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[sx126x] Add sx126x component (#8516)
Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick+github@koston.org>
This commit is contained in:
		| @@ -442,6 +442,7 @@ esphome/components/sun/* @OttoWinter | ||||
| esphome/components/sun_gtil2/* @Mat931 | ||||
| esphome/components/switch/* @esphome/core | ||||
| esphome/components/switch/binary_sensor/* @ssieb | ||||
| esphome/components/sx126x/* @swoboda1337 | ||||
| esphome/components/sx127x/* @swoboda1337 | ||||
| esphome/components/syslog/* @clydebarrow | ||||
| esphome/components/t6615/* @tylermenezes | ||||
|   | ||||
							
								
								
									
										317
									
								
								esphome/components/sx126x/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								esphome/components/sx126x/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,317 @@ | ||||
| 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_BUSY_PIN, CONF_DATA, CONF_FREQUENCY, CONF_ID | ||||
| from esphome.core import TimePeriod | ||||
|  | ||||
| MULTI_CONF = True | ||||
| CODEOWNERS = ["@swoboda1337"] | ||||
| DEPENDENCIES = ["spi"] | ||||
|  | ||||
| CONF_SX126X_ID = "sx126x_id" | ||||
|  | ||||
| CONF_BANDWIDTH = "bandwidth" | ||||
| CONF_BITRATE = "bitrate" | ||||
| CONF_CODING_RATE = "coding_rate" | ||||
| CONF_CRC_ENABLE = "crc_enable" | ||||
| CONF_DEVIATION = "deviation" | ||||
| CONF_DIO1_PIN = "dio1_pin" | ||||
| CONF_HW_VERSION = "hw_version" | ||||
| CONF_MODULATION = "modulation" | ||||
| CONF_ON_PACKET = "on_packet" | ||||
| CONF_PA_POWER = "pa_power" | ||||
| CONF_PA_RAMP = "pa_ramp" | ||||
| CONF_PAYLOAD_LENGTH = "payload_length" | ||||
| CONF_PREAMBLE_DETECT = "preamble_detect" | ||||
| CONF_PREAMBLE_SIZE = "preamble_size" | ||||
| CONF_RST_PIN = "rst_pin" | ||||
| CONF_RX_START = "rx_start" | ||||
| CONF_RF_SWITCH = "rf_switch" | ||||
| CONF_SHAPING = "shaping" | ||||
| CONF_SPREADING_FACTOR = "spreading_factor" | ||||
| CONF_SYNC_VALUE = "sync_value" | ||||
| CONF_TCXO_VOLTAGE = "tcxo_voltage" | ||||
| CONF_TCXO_DELAY = "tcxo_delay" | ||||
|  | ||||
| sx126x_ns = cg.esphome_ns.namespace("sx126x") | ||||
| SX126x = sx126x_ns.class_("SX126x", cg.Component, spi.SPIDevice) | ||||
| SX126xListener = sx126x_ns.class_("SX126xListener") | ||||
| SX126xBw = sx126x_ns.enum("SX126xBw") | ||||
| SX126xPacketType = sx126x_ns.enum("SX126xPacketType") | ||||
| SX126xTcxoCtrl = sx126x_ns.enum("SX126xTcxoCtrl") | ||||
| SX126xRampTime = sx126x_ns.enum("SX126xRampTime") | ||||
| SX126xPulseShape = sx126x_ns.enum("SX126xPulseShape") | ||||
| SX126xLoraCr = sx126x_ns.enum("SX126xLoraCr") | ||||
|  | ||||
| BW = { | ||||
|     "4_8kHz": SX126xBw.SX126X_BW_4800, | ||||
|     "5_8kHz": SX126xBw.SX126X_BW_5800, | ||||
|     "7_3kHz": SX126xBw.SX126X_BW_7300, | ||||
|     "9_7kHz": SX126xBw.SX126X_BW_9700, | ||||
|     "11_7kHz": SX126xBw.SX126X_BW_11700, | ||||
|     "14_6kHz": SX126xBw.SX126X_BW_14600, | ||||
|     "19_5kHz": SX126xBw.SX126X_BW_19500, | ||||
|     "23_4kHz": SX126xBw.SX126X_BW_23400, | ||||
|     "29_3kHz": SX126xBw.SX126X_BW_29300, | ||||
|     "39_0kHz": SX126xBw.SX126X_BW_39000, | ||||
|     "46_9kHz": SX126xBw.SX126X_BW_46900, | ||||
|     "58_6kHz": SX126xBw.SX126X_BW_58600, | ||||
|     "78_2kHz": SX126xBw.SX126X_BW_78200, | ||||
|     "93_8kHz": SX126xBw.SX126X_BW_93800, | ||||
|     "117_3kHz": SX126xBw.SX126X_BW_117300, | ||||
|     "156_2kHz": SX126xBw.SX126X_BW_156200, | ||||
|     "187_2kHz": SX126xBw.SX126X_BW_187200, | ||||
|     "234_3kHz": SX126xBw.SX126X_BW_234300, | ||||
|     "312_0kHz": SX126xBw.SX126X_BW_312000, | ||||
|     "373_6kHz": SX126xBw.SX126X_BW_373600, | ||||
|     "467_0kHz": SX126xBw.SX126X_BW_467000, | ||||
|     "7_8kHz": SX126xBw.SX126X_BW_7810, | ||||
|     "10_4kHz": SX126xBw.SX126X_BW_10420, | ||||
|     "15_6kHz": SX126xBw.SX126X_BW_15630, | ||||
|     "20_8kHz": SX126xBw.SX126X_BW_20830, | ||||
|     "31_3kHz": SX126xBw.SX126X_BW_31250, | ||||
|     "41_7kHz": SX126xBw.SX126X_BW_41670, | ||||
|     "62_5kHz": SX126xBw.SX126X_BW_62500, | ||||
|     "125_0kHz": SX126xBw.SX126X_BW_125000, | ||||
|     "250_0kHz": SX126xBw.SX126X_BW_250000, | ||||
|     "500_0kHz": SX126xBw.SX126X_BW_500000, | ||||
| } | ||||
|  | ||||
| CODING_RATE = { | ||||
|     "CR_4_5": SX126xLoraCr.LORA_CR_4_5, | ||||
|     "CR_4_6": SX126xLoraCr.LORA_CR_4_6, | ||||
|     "CR_4_7": SX126xLoraCr.LORA_CR_4_7, | ||||
|     "CR_4_8": SX126xLoraCr.LORA_CR_4_8, | ||||
| } | ||||
|  | ||||
| MOD = { | ||||
|     "LORA": SX126xPacketType.PACKET_TYPE_LORA, | ||||
|     "FSK": SX126xPacketType.PACKET_TYPE_GFSK, | ||||
| } | ||||
|  | ||||
| TCXO_VOLTAGE = { | ||||
|     "1_6V": SX126xTcxoCtrl.TCXO_CTRL_1_6V, | ||||
|     "1_7V": SX126xTcxoCtrl.TCXO_CTRL_1_7V, | ||||
|     "1_8V": SX126xTcxoCtrl.TCXO_CTRL_1_8V, | ||||
|     "2_2V": SX126xTcxoCtrl.TCXO_CTRL_2_2V, | ||||
|     "2_4V": SX126xTcxoCtrl.TCXO_CTRL_2_4V, | ||||
|     "2_7V": SX126xTcxoCtrl.TCXO_CTRL_2_7V, | ||||
|     "3_0V": SX126xTcxoCtrl.TCXO_CTRL_3_0V, | ||||
|     "3_3V": SX126xTcxoCtrl.TCXO_CTRL_3_3V, | ||||
|     "NONE": SX126xTcxoCtrl.TCXO_CTRL_NONE, | ||||
| } | ||||
|  | ||||
| RAMP = { | ||||
|     "10us": SX126xRampTime.PA_RAMP_10, | ||||
|     "20us": SX126xRampTime.PA_RAMP_20, | ||||
|     "40us": SX126xRampTime.PA_RAMP_40, | ||||
|     "80us": SX126xRampTime.PA_RAMP_80, | ||||
|     "200us": SX126xRampTime.PA_RAMP_200, | ||||
|     "800us": SX126xRampTime.PA_RAMP_800, | ||||
|     "1700us": SX126xRampTime.PA_RAMP_1700, | ||||
|     "3400us": SX126xRampTime.PA_RAMP_3400, | ||||
| } | ||||
|  | ||||
| SHAPING = { | ||||
|     "GAUSSIAN_BT_0_3": SX126xPulseShape.GAUSSIAN_BT_0_3, | ||||
|     "GAUSSIAN_BT_0_5": SX126xPulseShape.GAUSSIAN_BT_0_5, | ||||
|     "GAUSSIAN_BT_0_7": SX126xPulseShape.GAUSSIAN_BT_0_7, | ||||
|     "GAUSSIAN_BT_1_0": SX126xPulseShape.GAUSSIAN_BT_1_0, | ||||
|     "NONE": SX126xPulseShape.NO_FILTER, | ||||
| } | ||||
|  | ||||
| RunImageCalAction = sx126x_ns.class_( | ||||
|     "RunImageCalAction", automation.Action, cg.Parented.template(SX126x) | ||||
| ) | ||||
| SendPacketAction = sx126x_ns.class_( | ||||
|     "SendPacketAction", automation.Action, cg.Parented.template(SX126x) | ||||
| ) | ||||
| SetModeTxAction = sx126x_ns.class_( | ||||
|     "SetModeTxAction", automation.Action, cg.Parented.template(SX126x) | ||||
| ) | ||||
| SetModeRxAction = sx126x_ns.class_( | ||||
|     "SetModeRxAction", automation.Action, cg.Parented.template(SX126x) | ||||
| ) | ||||
| SetModeSleepAction = sx126x_ns.class_( | ||||
|     "SetModeSleepAction", automation.Action, cg.Parented.template(SX126x) | ||||
| ) | ||||
| SetModeStandbyAction = sx126x_ns.class_( | ||||
|     "SetModeStandbyAction", automation.Action, cg.Parented.template(SX126x) | ||||
| ) | ||||
|  | ||||
|  | ||||
| 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" | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def validate_config(config): | ||||
|     lora_bws = [ | ||||
|         "7_8kHz", | ||||
|         "10_4kHz", | ||||
|         "15_6kHz", | ||||
|         "20_8kHz", | ||||
|         "31_3kHz", | ||||
|         "41_7kHz", | ||||
|         "62_5kHz", | ||||
|         "125_0kHz", | ||||
|         "250_0kHz", | ||||
|         "500_0kHz", | ||||
|     ] | ||||
|     if config[CONF_MODULATION] == "LORA": | ||||
|         if config[CONF_BANDWIDTH] not in lora_bws: | ||||
|             raise cv.Invalid(f"{config[CONF_BANDWIDTH]} is not available with LORA") | ||||
|         if config[CONF_PREAMBLE_SIZE] > 0 and config[CONF_PREAMBLE_SIZE] < 6: | ||||
|             raise cv.Invalid("Minimum preamble size is 6 with LORA") | ||||
|         if config[CONF_SPREADING_FACTOR] == 6 and config[CONF_PAYLOAD_LENGTH] == 0: | ||||
|             raise cv.Invalid("Payload length must be set when spreading factor is 6") | ||||
|     else: | ||||
|         if config[CONF_BANDWIDTH] in lora_bws: | ||||
|             raise cv.Invalid(f"{config[CONF_BANDWIDTH]} is not available with FSK") | ||||
|         if config[CONF_PREAMBLE_DETECT] > len(config[CONF_SYNC_VALUE]): | ||||
|             raise cv.Invalid("Preamble detection length must be <= sync value length") | ||||
|     return config | ||||
|  | ||||
|  | ||||
| CONFIG_SCHEMA = ( | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(SX126x), | ||||
|             cv.Optional(CONF_BANDWIDTH, default="125_0kHz"): cv.enum(BW), | ||||
|             cv.Optional(CONF_BITRATE, default=4800): cv.int_range(min=600, max=300000), | ||||
|             cv.Required(CONF_BUSY_PIN): pins.internal_gpio_input_pin_schema, | ||||
|             cv.Optional(CONF_CODING_RATE, default="CR_4_5"): cv.enum(CODING_RATE), | ||||
|             cv.Optional(CONF_CRC_ENABLE, default=False): cv.boolean, | ||||
|             cv.Optional(CONF_DEVIATION, default=5000): cv.int_range(min=0, max=100000), | ||||
|             cv.Required(CONF_DIO1_PIN): pins.internal_gpio_input_pin_schema, | ||||
|             cv.Required(CONF_FREQUENCY): cv.int_range(min=137000000, max=1020000000), | ||||
|             cv.Required(CONF_HW_VERSION): cv.one_of( | ||||
|                 "sx1261", "sx1262", "sx1268", "llcc68", lower=True | ||||
|             ), | ||||
|             cv.Required(CONF_MODULATION): cv.enum(MOD), | ||||
|             cv.Optional(CONF_ON_PACKET): automation.validate_automation(single=True), | ||||
|             cv.Optional(CONF_PA_POWER, default=17): cv.int_range(min=-3, max=22), | ||||
|             cv.Optional(CONF_PA_RAMP, default="40us"): cv.enum(RAMP), | ||||
|             cv.Optional(CONF_PAYLOAD_LENGTH, default=0): cv.int_range(min=0, max=256), | ||||
|             cv.Optional(CONF_PREAMBLE_DETECT, default=2): cv.int_range(min=0, max=4), | ||||
|             cv.Required(CONF_PREAMBLE_SIZE): cv.int_range(min=1, max=65535), | ||||
|             cv.Required(CONF_RST_PIN): pins.internal_gpio_output_pin_schema, | ||||
|             cv.Optional(CONF_RX_START, default=True): cv.boolean, | ||||
|             cv.Required(CONF_RF_SWITCH): cv.boolean, | ||||
|             cv.Optional(CONF_SHAPING, default="NONE"): cv.enum(SHAPING), | ||||
|             cv.Optional(CONF_SPREADING_FACTOR, default=7): cv.int_range(min=6, max=12), | ||||
|             cv.Optional(CONF_SYNC_VALUE, default=[]): cv.ensure_list(cv.hex_uint8_t), | ||||
|             cv.Optional(CONF_TCXO_VOLTAGE, default="NONE"): cv.enum(TCXO_VOLTAGE), | ||||
|             cv.Optional(CONF_TCXO_DELAY, default="5ms"): cv.All( | ||||
|                 cv.positive_time_period_microseconds, | ||||
|                 cv.Range(max=TimePeriod(microseconds=262144000)), | ||||
|             ), | ||||
|         }, | ||||
|     ) | ||||
|     .extend(cv.COMPONENT_SCHEMA) | ||||
|     .extend(spi.spi_device_schema(True, 8e6, "mode0")) | ||||
|     .add_extra(validate_config) | ||||
| ) | ||||
|  | ||||
|  | ||||
| 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) | ||||
|     if CONF_ON_PACKET in config: | ||||
|         await automation.build_automation( | ||||
|             var.get_packet_trigger(), | ||||
|             [ | ||||
|                 (cg.std_vector.template(cg.uint8), "x"), | ||||
|                 (cg.float_, "rssi"), | ||||
|                 (cg.float_, "snr"), | ||||
|             ], | ||||
|             config[CONF_ON_PACKET], | ||||
|         ) | ||||
|     if CONF_DIO1_PIN in config: | ||||
|         dio1_pin = await cg.gpio_pin_expression(config[CONF_DIO1_PIN]) | ||||
|         cg.add(var.set_dio1_pin(dio1_pin)) | ||||
|     rst_pin = await cg.gpio_pin_expression(config[CONF_RST_PIN]) | ||||
|     cg.add(var.set_rst_pin(rst_pin)) | ||||
|     busy_pin = await cg.gpio_pin_expression(config[CONF_BUSY_PIN]) | ||||
|     cg.add(var.set_busy_pin(busy_pin)) | ||||
|     cg.add(var.set_bandwidth(config[CONF_BANDWIDTH])) | ||||
|     cg.add(var.set_frequency(config[CONF_FREQUENCY])) | ||||
|     cg.add(var.set_hw_version(config[CONF_HW_VERSION])) | ||||
|     cg.add(var.set_deviation(config[CONF_DEVIATION])) | ||||
|     cg.add(var.set_modulation(config[CONF_MODULATION])) | ||||
|     cg.add(var.set_pa_ramp(config[CONF_PA_RAMP])) | ||||
|     cg.add(var.set_pa_power(config[CONF_PA_POWER])) | ||||
|     cg.add(var.set_shaping(config[CONF_SHAPING])) | ||||
|     cg.add(var.set_bitrate(config[CONF_BITRATE])) | ||||
|     cg.add(var.set_crc_enable(config[CONF_CRC_ENABLE])) | ||||
|     cg.add(var.set_payload_length(config[CONF_PAYLOAD_LENGTH])) | ||||
|     cg.add(var.set_preamble_size(config[CONF_PREAMBLE_SIZE])) | ||||
|     cg.add(var.set_preamble_detect(config[CONF_PREAMBLE_DETECT])) | ||||
|     cg.add(var.set_coding_rate(config[CONF_CODING_RATE])) | ||||
|     cg.add(var.set_spreading_factor(config[CONF_SPREADING_FACTOR])) | ||||
|     cg.add(var.set_sync_value(config[CONF_SYNC_VALUE])) | ||||
|     cg.add(var.set_rx_start(config[CONF_RX_START])) | ||||
|     cg.add(var.set_rf_switch(config[CONF_RF_SWITCH])) | ||||
|     cg.add(var.set_tcxo_voltage(config[CONF_TCXO_VOLTAGE])) | ||||
|     cg.add(var.set_tcxo_delay(config[CONF_TCXO_DELAY])) | ||||
|  | ||||
|  | ||||
| NO_ARGS_ACTION_SCHEMA = automation.maybe_simple_id( | ||||
|     { | ||||
|         cv.GenerateID(): cv.use_id(SX126x), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| @automation.register_action( | ||||
|     "sx126x.run_image_cal", RunImageCalAction, NO_ARGS_ACTION_SCHEMA | ||||
| ) | ||||
| @automation.register_action( | ||||
|     "sx126x.set_mode_tx", SetModeTxAction, NO_ARGS_ACTION_SCHEMA | ||||
| ) | ||||
| @automation.register_action( | ||||
|     "sx126x.set_mode_rx", SetModeRxAction, NO_ARGS_ACTION_SCHEMA | ||||
| ) | ||||
| @automation.register_action( | ||||
|     "sx126x.set_mode_sleep", SetModeSleepAction, NO_ARGS_ACTION_SCHEMA | ||||
| ) | ||||
| @automation.register_action( | ||||
|     "sx126x.set_mode_standby", SetModeStandbyAction, NO_ARGS_ACTION_SCHEMA | ||||
| ) | ||||
| async def no_args_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]) | ||||
|     return var | ||||
|  | ||||
|  | ||||
| SEND_PACKET_ACTION_SCHEMA = cv.maybe_simple_value( | ||||
|     { | ||||
|         cv.GenerateID(): cv.use_id(SX126x), | ||||
|         cv.Required(CONF_DATA): cv.templatable(validate_raw_data), | ||||
|     }, | ||||
|     key=CONF_DATA, | ||||
| ) | ||||
|  | ||||
|  | ||||
| @automation.register_action( | ||||
|     "sx126x.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 | ||||
							
								
								
									
										62
									
								
								esphome/components/sx126x/automation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								esphome/components/sx126x/automation.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/components/sx126x/sx126x.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace sx126x { | ||||
|  | ||||
| template<typename... Ts> class RunImageCalAction : public Action<Ts...>, public Parented<SX126x> { | ||||
|  public: | ||||
|   void play(Ts... x) override { this->parent_->run_image_cal(); } | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class SendPacketAction : public Action<Ts...>, public Parented<SX126x> { | ||||
|  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 Parented<SX126x> { | ||||
|  public: | ||||
|   void play(Ts... x) override { this->parent_->set_mode_tx(); } | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class SetModeRxAction : public Action<Ts...>, public Parented<SX126x> { | ||||
|  public: | ||||
|   void play(Ts... x) override { this->parent_->set_mode_rx(); } | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class SetModeSleepAction : public Action<Ts...>, public Parented<SX126x> { | ||||
|  public: | ||||
|   void play(Ts... x) override { this->parent_->set_mode_sleep(); } | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class SetModeStandbyAction : public Action<Ts...>, public Parented<SX126x> { | ||||
|  public: | ||||
|   void play(Ts... x) override { this->parent_->set_mode_standby(STDBY_XOSC); } | ||||
| }; | ||||
|  | ||||
| }  // namespace sx126x | ||||
| }  // namespace esphome | ||||
							
								
								
									
										26
									
								
								esphome/components/sx126x/packet_transport/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								esphome/components/sx126x/packet_transport/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components.packet_transport import ( | ||||
|     PacketTransport, | ||||
|     new_packet_transport, | ||||
|     transport_schema, | ||||
| ) | ||||
| import esphome.config_validation as cv | ||||
| from esphome.cpp_types import PollingComponent | ||||
|  | ||||
| from .. import CONF_SX126X_ID, SX126x, SX126xListener, sx126x_ns | ||||
|  | ||||
| SX126xTransport = sx126x_ns.class_( | ||||
|     "SX126xTransport", PacketTransport, PollingComponent, SX126xListener | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = transport_schema(SX126xTransport).extend( | ||||
|     { | ||||
|         cv.GenerateID(CONF_SX126X_ID): cv.use_id(SX126x), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var, _ = await new_packet_transport(config) | ||||
|     sx126x = await cg.get_variable(config[CONF_SX126X_ID]) | ||||
|     cg.add(var.set_parent(sx126x)) | ||||
| @@ -0,0 +1,26 @@ | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/application.h" | ||||
| #include "sx126x_transport.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace sx126x { | ||||
|  | ||||
| static const char *const TAG = "sx126x_transport"; | ||||
|  | ||||
| void SX126xTransport::setup() { | ||||
|   PacketTransport::setup(); | ||||
|   this->parent_->register_listener(this); | ||||
| } | ||||
|  | ||||
| void SX126xTransport::update() { | ||||
|   PacketTransport::update(); | ||||
|   this->updated_ = true; | ||||
|   this->resend_data_ = true; | ||||
| } | ||||
|  | ||||
| void SX126xTransport::send_packet(const std::vector<uint8_t> &buf) const { this->parent_->transmit_packet(buf); } | ||||
|  | ||||
| void SX126xTransport::on_packet(const std::vector<uint8_t> &packet, float rssi, float snr) { this->process_(packet); } | ||||
|  | ||||
| }  // namespace sx126x | ||||
| }  // namespace esphome | ||||
| @@ -0,0 +1,25 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/sx126x/sx126x.h" | ||||
| #include "esphome/components/packet_transport/packet_transport.h" | ||||
| #include <vector> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace sx126x { | ||||
|  | ||||
| class SX126xTransport : public packet_transport::PacketTransport, public Parented<SX126x>, public SX126xListener { | ||||
|  public: | ||||
|   void setup() override; | ||||
|   void update() override; | ||||
|   void on_packet(const std::vector<uint8_t> &packet, float rssi, float snr) override; | ||||
|   float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } | ||||
|  | ||||
|  protected: | ||||
|   void send_packet(const std::vector<uint8_t> &buf) const override; | ||||
|   bool should_send() override { return true; } | ||||
|   size_t get_max_packet_size() override { return this->parent_->get_max_packet_size(); } | ||||
| }; | ||||
|  | ||||
| }  // namespace sx126x | ||||
| }  // namespace esphome | ||||
							
								
								
									
										523
									
								
								esphome/components/sx126x/sx126x.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										523
									
								
								esphome/components/sx126x/sx126x.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,523 @@ | ||||
| #include "sx126x.h" | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace sx126x { | ||||
|  | ||||
| static const char *const TAG = "sx126x"; | ||||
| static const uint16_t RAMP[8] = {10, 20, 40, 80, 200, 800, 1700, 3400}; | ||||
| static const uint32_t BW_HZ[31] = {4800,  5800,  7300,  9700,   11700,  14600,  19500,  23400,  29300,  39000,  46900, | ||||
|                                    58600, 78200, 93800, 117300, 156200, 187200, 234300, 312000, 373600, 467000, 7810, | ||||
|                                    10420, 15630, 20830, 31250,  41670,  62500,  125000, 250000, 500000}; | ||||
| static const uint8_t BW_LORA[10] = {LORA_BW_7810,  LORA_BW_10420, LORA_BW_15630,  LORA_BW_20830,  LORA_BW_31250, | ||||
|                                     LORA_BW_41670, LORA_BW_62500, LORA_BW_125000, LORA_BW_250000, LORA_BW_500000}; | ||||
| static const uint8_t BW_FSK[21] = { | ||||
|     FSK_BW_4800,   FSK_BW_5800,   FSK_BW_7300,   FSK_BW_9700,   FSK_BW_11700,  FSK_BW_14600,  FSK_BW_19500, | ||||
|     FSK_BW_23400,  FSK_BW_29300,  FSK_BW_39000,  FSK_BW_46900,  FSK_BW_58600,  FSK_BW_78200,  FSK_BW_93800, | ||||
|     FSK_BW_117300, FSK_BW_156200, FSK_BW_187200, FSK_BW_234300, FSK_BW_312000, FSK_BW_373600, FSK_BW_467000}; | ||||
|  | ||||
| static constexpr uint32_t RESET_DELAY_HIGH_US = 5000; | ||||
| static constexpr uint32_t RESET_DELAY_LOW_US = 2000; | ||||
| static constexpr uint32_t SWITCHING_DELAY_US = 1; | ||||
| static constexpr uint32_t TRANSMIT_TIMEOUT_MS = 4000; | ||||
| static constexpr uint32_t BUSY_TIMEOUT_MS = 20; | ||||
|  | ||||
| // OCP (Over Current Protection) values | ||||
| static constexpr uint8_t OCP_80MA = 0x18;   // 80 mA max current | ||||
| static constexpr uint8_t OCP_140MA = 0x38;  // 140 mA max current | ||||
|  | ||||
| // LoRa low data rate optimization threshold | ||||
| static constexpr float LOW_DATA_RATE_OPTIMIZE_THRESHOLD = 16.38f;  // 16.38 ms | ||||
|  | ||||
| uint8_t SX126x::read_fifo_(uint8_t offset, std::vector<uint8_t> &packet) { | ||||
|   this->wait_busy_(); | ||||
|   this->enable(); | ||||
|   this->transfer_byte(RADIO_READ_BUFFER); | ||||
|   this->transfer_byte(offset); | ||||
|   uint8_t status = this->transfer_byte(0x00); | ||||
|   for (uint8_t &byte : packet) { | ||||
|     byte = this->transfer_byte(0x00); | ||||
|   } | ||||
|   this->disable(); | ||||
|   return status; | ||||
| } | ||||
|  | ||||
| void SX126x::write_fifo_(uint8_t offset, const std::vector<uint8_t> &packet) { | ||||
|   this->wait_busy_(); | ||||
|   this->enable(); | ||||
|   this->transfer_byte(RADIO_WRITE_BUFFER); | ||||
|   this->transfer_byte(offset); | ||||
|   for (const uint8_t &byte : packet) { | ||||
|     this->transfer_byte(byte); | ||||
|   } | ||||
|   this->disable(); | ||||
|   delayMicroseconds(SWITCHING_DELAY_US); | ||||
| } | ||||
|  | ||||
| uint8_t SX126x::read_opcode_(uint8_t opcode, uint8_t *data, uint8_t size) { | ||||
|   this->wait_busy_(); | ||||
|   this->enable(); | ||||
|   this->transfer_byte(opcode); | ||||
|   uint8_t status = this->transfer_byte(0x00); | ||||
|   for (int32_t i = 0; i < size; i++) { | ||||
|     data[i] = this->transfer_byte(0x00); | ||||
|   } | ||||
|   this->disable(); | ||||
|   return status; | ||||
| } | ||||
|  | ||||
| void SX126x::write_opcode_(uint8_t opcode, uint8_t *data, uint8_t size) { | ||||
|   this->wait_busy_(); | ||||
|   this->enable(); | ||||
|   this->transfer_byte(opcode); | ||||
|   for (int32_t i = 0; i < size; i++) { | ||||
|     this->transfer_byte(data[i]); | ||||
|   } | ||||
|   this->disable(); | ||||
|   delayMicroseconds(SWITCHING_DELAY_US); | ||||
| } | ||||
|  | ||||
| void SX126x::read_register_(uint16_t reg, uint8_t *data, uint8_t size) { | ||||
|   this->wait_busy_(); | ||||
|   this->enable(); | ||||
|   this->write_byte(RADIO_READ_REGISTER); | ||||
|   this->write_byte((reg >> 8) & 0xFF); | ||||
|   this->write_byte((reg >> 0) & 0xFF); | ||||
|   this->write_byte(0x00); | ||||
|   for (int32_t i = 0; i < size; i++) { | ||||
|     data[i] = this->transfer_byte(0x00); | ||||
|   } | ||||
|   this->disable(); | ||||
| } | ||||
|  | ||||
| void SX126x::write_register_(uint16_t reg, uint8_t *data, uint8_t size) { | ||||
|   this->wait_busy_(); | ||||
|   this->enable(); | ||||
|   this->write_byte(RADIO_WRITE_REGISTER); | ||||
|   this->write_byte((reg >> 8) & 0xFF); | ||||
|   this->write_byte((reg >> 0) & 0xFF); | ||||
|   for (int32_t i = 0; i < size; i++) { | ||||
|     this->transfer_byte(data[i]); | ||||
|   } | ||||
|   this->disable(); | ||||
|   delayMicroseconds(SWITCHING_DELAY_US); | ||||
| } | ||||
|  | ||||
| void SX126x::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Running setup"); | ||||
|  | ||||
|   // setup pins | ||||
|   this->busy_pin_->setup(); | ||||
|   this->rst_pin_->setup(); | ||||
|   this->dio1_pin_->setup(); | ||||
|  | ||||
|   // start spi | ||||
|   this->spi_setup(); | ||||
|  | ||||
|   // configure rf | ||||
|   this->configure(); | ||||
| } | ||||
|  | ||||
| void SX126x::configure() { | ||||
|   uint8_t buf[8]; | ||||
|  | ||||
|   // toggle chip reset | ||||
|   this->rst_pin_->digital_write(true); | ||||
|   delayMicroseconds(RESET_DELAY_HIGH_US); | ||||
|   this->rst_pin_->digital_write(false); | ||||
|   delayMicroseconds(RESET_DELAY_LOW_US); | ||||
|   this->rst_pin_->digital_write(true); | ||||
|   delayMicroseconds(RESET_DELAY_HIGH_US); | ||||
|  | ||||
|   // wakeup | ||||
|   this->read_opcode_(RADIO_GET_STATUS, nullptr, 0); | ||||
|  | ||||
|   // config tcxo | ||||
|   if (this->tcxo_voltage_ != TCXO_CTRL_NONE) { | ||||
|     uint32_t delay = this->tcxo_delay_ >> 6; | ||||
|     buf[0] = this->tcxo_voltage_; | ||||
|     buf[1] = (delay >> 16) & 0xFF; | ||||
|     buf[2] = (delay >> 8) & 0xFF; | ||||
|     buf[3] = (delay >> 0) & 0xFF; | ||||
|     this->write_opcode_(RADIO_SET_TCXOMODE, buf, 4); | ||||
|     buf[0] = 0x7F; | ||||
|     this->write_opcode_(RADIO_CALIBRATE, buf, 1); | ||||
|   } | ||||
|  | ||||
|   // clear errors | ||||
|   buf[0] = 0x00; | ||||
|   buf[1] = 0x00; | ||||
|   this->write_opcode_(RADIO_CLR_ERROR, buf, 2); | ||||
|  | ||||
|   // rf switch | ||||
|   if (this->rf_switch_) { | ||||
|     buf[0] = 0x01; | ||||
|     this->write_opcode_(RADIO_SET_RFSWITCHMODE, buf, 1); | ||||
|   } | ||||
|  | ||||
|   // check silicon version to make sure hw is ok | ||||
|   this->read_register_(REG_VERSION_STRING, (uint8_t *) this->version_, 16); | ||||
|   if (strncmp(this->version_, "SX126", 5) != 0 && strncmp(this->version_, "LLCC68", 6) != 0) { | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   // setup packet type | ||||
|   buf[0] = this->modulation_; | ||||
|   this->write_opcode_(RADIO_SET_PACKETTYPE, buf, 1); | ||||
|  | ||||
|   // calibrate image | ||||
|   this->run_image_cal(); | ||||
|  | ||||
|   // set frequency | ||||
|   uint64_t freq = ((uint64_t) this->frequency_ << 25) / XTAL_FREQ; | ||||
|   buf[0] = (uint8_t) ((freq >> 24) & 0xFF); | ||||
|   buf[1] = (uint8_t) ((freq >> 16) & 0xFF); | ||||
|   buf[2] = (uint8_t) ((freq >> 8) & 0xFF); | ||||
|   buf[3] = (uint8_t) (freq & 0xFF); | ||||
|   this->write_opcode_(RADIO_SET_RFFREQUENCY, buf, 4); | ||||
|  | ||||
|   // configure pa | ||||
|   int8_t pa_power = this->pa_power_; | ||||
|   if (this->hw_version_ == "sx1261") { | ||||
|     // the following values were taken from section 13.1.14.1 table 13-21 | ||||
|     // in rev 2.1 of the datasheet | ||||
|     if (pa_power == 15) { | ||||
|       uint8_t cfg[4] = {0x06, 0x00, 0x01, 0x01}; | ||||
|       this->write_opcode_(RADIO_SET_PACONFIG, cfg, 4); | ||||
|     } else { | ||||
|       uint8_t cfg[4] = {0x04, 0x00, 0x01, 0x01}; | ||||
|       this->write_opcode_(RADIO_SET_PACONFIG, cfg, 4); | ||||
|     } | ||||
|     pa_power = std::max(pa_power, (int8_t) -3); | ||||
|     pa_power = std::min(pa_power, (int8_t) 14); | ||||
|     buf[0] = OCP_80MA; | ||||
|     this->write_register_(REG_OCP, buf, 1); | ||||
|   } else { | ||||
|     // the following values were taken from section 13.1.14.1 table 13-21 | ||||
|     // in rev 2.1 of the datasheet | ||||
|     uint8_t cfg[4] = {0x04, 0x07, 0x00, 0x01}; | ||||
|     this->write_opcode_(RADIO_SET_PACONFIG, cfg, 4); | ||||
|     pa_power = std::max(pa_power, (int8_t) -3); | ||||
|     pa_power = std::min(pa_power, (int8_t) 22); | ||||
|     buf[0] = OCP_140MA; | ||||
|     this->write_register_(REG_OCP, buf, 1); | ||||
|   } | ||||
|   buf[0] = pa_power; | ||||
|   buf[1] = this->pa_ramp_; | ||||
|   this->write_opcode_(RADIO_SET_TXPARAMS, buf, 2); | ||||
|  | ||||
|   // configure modem | ||||
|   if (this->modulation_ == PACKET_TYPE_LORA) { | ||||
|     // set modulation params | ||||
|     float duration = 1000.0f * std::pow(2, this->spreading_factor_) / BW_HZ[this->bandwidth_]; | ||||
|     buf[0] = this->spreading_factor_; | ||||
|     buf[1] = BW_LORA[this->bandwidth_ - SX126X_BW_7810]; | ||||
|     buf[2] = this->coding_rate_; | ||||
|     buf[3] = (duration > LOW_DATA_RATE_OPTIMIZE_THRESHOLD) ? 0x01 : 0x00; | ||||
|     this->write_opcode_(RADIO_SET_MODULATIONPARAMS, buf, 4); | ||||
|  | ||||
|     // set packet params and sync word | ||||
|     this->set_packet_params_(this->payload_length_); | ||||
|     if (this->sync_value_.size() == 2) { | ||||
|       this->write_register_(REG_LORA_SYNCWORD, this->sync_value_.data(), this->sync_value_.size()); | ||||
|     } | ||||
|   } else { | ||||
|     // set modulation params | ||||
|     uint32_t bitrate = ((uint64_t) XTAL_FREQ * 32) / this->bitrate_; | ||||
|     uint32_t fdev = ((uint64_t) this->deviation_ << 25) / XTAL_FREQ; | ||||
|     buf[0] = (bitrate >> 16) & 0xFF; | ||||
|     buf[1] = (bitrate >> 8) & 0xFF; | ||||
|     buf[2] = (bitrate >> 0) & 0xFF; | ||||
|     buf[3] = this->shaping_; | ||||
|     buf[4] = BW_FSK[this->bandwidth_ - SX126X_BW_4800]; | ||||
|     buf[5] = (fdev >> 16) & 0xFF; | ||||
|     buf[6] = (fdev >> 8) & 0xFF; | ||||
|     buf[7] = (fdev >> 0) & 0xFF; | ||||
|     this->write_opcode_(RADIO_SET_MODULATIONPARAMS, buf, 8); | ||||
|  | ||||
|     // set packet params and sync word | ||||
|     this->set_packet_params_(this->payload_length_); | ||||
|     if (!this->sync_value_.empty()) { | ||||
|       this->write_register_(REG_GFSK_SYNCWORD, this->sync_value_.data(), this->sync_value_.size()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // switch to rx or sleep | ||||
|   if (this->rx_start_) { | ||||
|     this->set_mode_rx(); | ||||
|   } else { | ||||
|     this->set_mode_sleep(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| size_t SX126x::get_max_packet_size() { | ||||
|   if (this->payload_length_ > 0) { | ||||
|     return this->payload_length_; | ||||
|   } | ||||
|   return 255; | ||||
| } | ||||
|  | ||||
| void SX126x::set_packet_params_(uint8_t payload_length) { | ||||
|   uint8_t buf[9]; | ||||
|   if (this->modulation_ == PACKET_TYPE_LORA) { | ||||
|     buf[0] = (this->preamble_size_ >> 8) & 0xFF; | ||||
|     buf[1] = (this->preamble_size_ >> 0) & 0xFF; | ||||
|     buf[2] = (this->payload_length_ > 0) ? 0x01 : 0x00; | ||||
|     buf[3] = payload_length; | ||||
|     buf[4] = (this->crc_enable_) ? 0x01 : 0x00; | ||||
|     buf[5] = 0x00; | ||||
|     this->write_opcode_(RADIO_SET_PACKETPARAMS, buf, 6); | ||||
|   } else { | ||||
|     uint16_t preamble_size = this->preamble_size_ * 8; | ||||
|     buf[0] = (preamble_size >> 8) & 0xFF; | ||||
|     buf[1] = (preamble_size >> 0) & 0xFF; | ||||
|     buf[2] = (this->preamble_detect_ > 0) ? ((this->preamble_detect_ - 1) | 0x04) : 0x00; | ||||
|     buf[3] = this->sync_value_.size() * 8; | ||||
|     buf[4] = 0x00; | ||||
|     buf[5] = 0x00; | ||||
|     buf[6] = payload_length; | ||||
|     buf[7] = this->crc_enable_ ? 0x06 : 0x01; | ||||
|     buf[8] = 0x00; | ||||
|     this->write_opcode_(RADIO_SET_PACKETPARAMS, buf, 9); | ||||
|   } | ||||
| } | ||||
|  | ||||
| SX126xError SX126x::transmit_packet(const std::vector<uint8_t> &packet) { | ||||
|   if (this->payload_length_ > 0 && this->payload_length_ != packet.size()) { | ||||
|     ESP_LOGE(TAG, "Packet size does not match config"); | ||||
|     return SX126xError::INVALID_PARAMS; | ||||
|   } | ||||
|   if (packet.empty() || packet.size() > this->get_max_packet_size()) { | ||||
|     ESP_LOGE(TAG, "Packet size out of range"); | ||||
|     return SX126xError::INVALID_PARAMS; | ||||
|   } | ||||
|  | ||||
|   SX126xError ret = SX126xError::NONE; | ||||
|   this->set_mode_standby(STDBY_XOSC); | ||||
|   if (this->payload_length_ == 0) { | ||||
|     this->set_packet_params_(packet.size()); | ||||
|   } | ||||
|   this->write_fifo_(0x00, packet); | ||||
|   this->set_mode_tx(); | ||||
|  | ||||
|   // wait until transmit completes, typically the delay will be less than 100 ms | ||||
|   uint32_t start = millis(); | ||||
|   while (!this->dio1_pin_->digital_read()) { | ||||
|     if (millis() - start > TRANSMIT_TIMEOUT_MS) { | ||||
|       ESP_LOGE(TAG, "Transmit packet failure"); | ||||
|       ret = SX126xError::TIMEOUT; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   uint8_t buf[2]; | ||||
|   buf[0] = 0xFF; | ||||
|   buf[1] = 0xFF; | ||||
|   this->write_opcode_(RADIO_CLR_IRQSTATUS, buf, 2); | ||||
|   if (this->rx_start_) { | ||||
|     this->set_mode_rx(); | ||||
|   } else { | ||||
|     this->set_mode_sleep(); | ||||
|   } | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| void SX126x::call_listeners_(const std::vector<uint8_t> &packet, float rssi, float snr) { | ||||
|   for (auto &listener : this->listeners_) { | ||||
|     listener->on_packet(packet, rssi, snr); | ||||
|   } | ||||
|   this->packet_trigger_->trigger(packet, rssi, snr); | ||||
| } | ||||
|  | ||||
| void SX126x::loop() { | ||||
|   if (!this->dio1_pin_->digital_read()) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   uint16_t status; | ||||
|   uint8_t buf[3]; | ||||
|   uint8_t rssi; | ||||
|   int8_t snr; | ||||
|   this->read_opcode_(RADIO_GET_IRQSTATUS, buf, 2); | ||||
|   this->write_opcode_(RADIO_CLR_IRQSTATUS, buf, 2); | ||||
|   status = (buf[0] << 8) | buf[1]; | ||||
|   if ((status & IRQ_RX_DONE) == IRQ_RX_DONE) { | ||||
|     if ((status & IRQ_CRC_ERROR) != IRQ_CRC_ERROR) { | ||||
|       this->read_opcode_(RADIO_GET_PACKETSTATUS, buf, 3); | ||||
|       if (this->modulation_ == PACKET_TYPE_LORA) { | ||||
|         rssi = buf[0]; | ||||
|         snr = buf[1]; | ||||
|       } else { | ||||
|         rssi = buf[2]; | ||||
|         snr = 0; | ||||
|       } | ||||
|       this->read_opcode_(RADIO_GET_RXBUFFERSTATUS, buf, 2); | ||||
|       this->packet_.resize(buf[0]); | ||||
|       this->read_fifo_(buf[1], this->packet_); | ||||
|       this->call_listeners_(this->packet_, (float) rssi / -2.0f, (float) snr / 4.0f); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void SX126x::run_image_cal() { | ||||
|   // the following values were taken from section 9.2.1 table 9-2 | ||||
|   // in rev 2.1 of the datasheet | ||||
|   uint8_t buf[2] = {0, 0}; | ||||
|   if (this->frequency_ > 900000000) { | ||||
|     buf[0] = 0xE1; | ||||
|     buf[1] = 0xE9; | ||||
|   } else if (this->frequency_ > 850000000) { | ||||
|     buf[0] = 0xD7; | ||||
|     buf[1] = 0xD8; | ||||
|   } else if (this->frequency_ > 770000000) { | ||||
|     buf[0] = 0xC1; | ||||
|     buf[1] = 0xC5; | ||||
|   } else if (this->frequency_ > 460000000) { | ||||
|     buf[0] = 0x75; | ||||
|     buf[1] = 0x81; | ||||
|   } else if (this->frequency_ > 425000000) { | ||||
|     buf[0] = 0x6B; | ||||
|     buf[1] = 0x6F; | ||||
|   } | ||||
|   if (buf[0] > 0 && buf[1] > 0) { | ||||
|     this->write_opcode_(RADIO_CALIBRATEIMAGE, buf, 2); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void SX126x::set_mode_rx() { | ||||
|   uint8_t buf[8]; | ||||
|  | ||||
|   // configure irq params | ||||
|   uint16_t irq = IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_CRC_ERROR; | ||||
|   buf[0] = (irq >> 8) & 0xFF; | ||||
|   buf[1] = (irq >> 0) & 0xFF; | ||||
|   buf[2] = (irq >> 8) & 0xFF; | ||||
|   buf[3] = (irq >> 0) & 0xFF; | ||||
|   buf[4] = (IRQ_RADIO_NONE >> 8) & 0xFF; | ||||
|   buf[5] = (IRQ_RADIO_NONE >> 0) & 0xFF; | ||||
|   buf[6] = (IRQ_RADIO_NONE >> 8) & 0xFF; | ||||
|   buf[7] = (IRQ_RADIO_NONE >> 0) & 0xFF; | ||||
|   this->write_opcode_(RADIO_SET_DIOIRQPARAMS, buf, 8); | ||||
|  | ||||
|   // set timeout to 0 | ||||
|   buf[0] = 0x00; | ||||
|   this->write_opcode_(RADIO_SET_LORASYMBTIMEOUT, buf, 1); | ||||
|  | ||||
|   // switch to continuous mode rx | ||||
|   buf[0] = 0xFF; | ||||
|   buf[1] = 0xFF; | ||||
|   buf[2] = 0xFF; | ||||
|   this->write_opcode_(RADIO_SET_RX, buf, 3); | ||||
| } | ||||
|  | ||||
| void SX126x::set_mode_tx() { | ||||
|   uint8_t buf[8]; | ||||
|  | ||||
|   // configure irq params | ||||
|   uint16_t irq = IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT; | ||||
|   buf[0] = (irq >> 8) & 0xFF; | ||||
|   buf[1] = (irq >> 0) & 0xFF; | ||||
|   buf[2] = (irq >> 8) & 0xFF; | ||||
|   buf[3] = (irq >> 0) & 0xFF; | ||||
|   buf[4] = (IRQ_RADIO_NONE >> 8) & 0xFF; | ||||
|   buf[5] = (IRQ_RADIO_NONE >> 0) & 0xFF; | ||||
|   buf[6] = (IRQ_RADIO_NONE >> 8) & 0xFF; | ||||
|   buf[7] = (IRQ_RADIO_NONE >> 0) & 0xFF; | ||||
|   this->write_opcode_(RADIO_SET_DIOIRQPARAMS, buf, 8); | ||||
|  | ||||
|   // switch to single mode tx | ||||
|   buf[0] = 0x00; | ||||
|   buf[1] = 0x00; | ||||
|   buf[2] = 0x00; | ||||
|   this->write_opcode_(RADIO_SET_TX, buf, 3); | ||||
| } | ||||
|  | ||||
| void SX126x::set_mode_sleep() { | ||||
|   uint8_t buf[1]; | ||||
|   buf[0] = 0x05; | ||||
|   this->write_opcode_(RADIO_SET_SLEEP, buf, 1); | ||||
| } | ||||
|  | ||||
| void SX126x::set_mode_standby(SX126xStandbyMode mode) { | ||||
|   uint8_t buf[1]; | ||||
|   buf[0] = mode; | ||||
|   this->write_opcode_(RADIO_SET_STANDBY, buf, 1); | ||||
| } | ||||
|  | ||||
| void SX126x::wait_busy_() { | ||||
|   // wait if the device is busy, the maximum delay is only be a few ms | ||||
|   // with most commands taking only a few us | ||||
|   uint32_t start = millis(); | ||||
|   while (this->busy_pin_->digital_read()) { | ||||
|     if (millis() - start > BUSY_TIMEOUT_MS) { | ||||
|       ESP_LOGE(TAG, "Wait busy timeout"); | ||||
|       this->mark_failed(); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void SX126x::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "SX126x:"); | ||||
|   LOG_PIN("  CS Pin: ", this->cs_); | ||||
|   LOG_PIN("  BUSY Pin: ", this->busy_pin_); | ||||
|   LOG_PIN("  RST Pin: ", this->rst_pin_); | ||||
|   LOG_PIN("  DIO1 Pin: ", this->dio1_pin_); | ||||
|   ESP_LOGCONFIG(TAG, | ||||
|                 "  HW Version: %15s\n" | ||||
|                 "  Frequency: %" PRIu32 " Hz\n" | ||||
|                 "  Bandwidth: %" PRIu32 " Hz\n" | ||||
|                 "  PA Power: %" PRId8 " dBm\n" | ||||
|                 "  PA Ramp: %" PRIu16 " us\n" | ||||
|                 "  Payload Length: %" PRIu32 "\n" | ||||
|                 "  CRC Enable: %s\n" | ||||
|                 "  Rx Start: %s", | ||||
|                 this->version_, this->frequency_, BW_HZ[this->bandwidth_], this->pa_power_, RAMP[this->pa_ramp_], | ||||
|                 this->payload_length_, TRUEFALSE(this->crc_enable_), TRUEFALSE(this->rx_start_)); | ||||
|   if (this->modulation_ == PACKET_TYPE_GFSK) { | ||||
|     const char *shaping = "NONE"; | ||||
|     if (this->shaping_ == GAUSSIAN_BT_0_3) { | ||||
|       shaping = "GAUSSIAN_BT_0_3"; | ||||
|     } else if (this->shaping_ == GAUSSIAN_BT_0_5) { | ||||
|       shaping = "GAUSSIAN_BT_0_5"; | ||||
|     } else if (this->shaping_ == GAUSSIAN_BT_0_7) { | ||||
|       shaping = "GAUSSIAN_BT_0_7"; | ||||
|     } else if (this->shaping_ == GAUSSIAN_BT_1_0) { | ||||
|       shaping = "GAUSSIAN_BT_1_0"; | ||||
|     } | ||||
|     ESP_LOGCONFIG(TAG, | ||||
|                   "  Modulation: FSK\n" | ||||
|                   "  Deviation: %" PRIu32 " Hz\n" | ||||
|                   "  Shaping: %s\n" | ||||
|                   "  Preamble Size: %" PRIu16 "\n" | ||||
|                   "  Preamble Detect: %" PRIu16 "\n" | ||||
|                   "  Bitrate: %" PRIu32 "b/s", | ||||
|                   this->deviation_, shaping, this->preamble_size_, this->preamble_detect_, this->bitrate_); | ||||
|   } else if (this->modulation_ == PACKET_TYPE_LORA) { | ||||
|     const char *cr = "4/8"; | ||||
|     if (this->coding_rate_ == LORA_CR_4_5) { | ||||
|       cr = "4/5"; | ||||
|     } else if (this->coding_rate_ == LORA_CR_4_6) { | ||||
|       cr = "4/6"; | ||||
|     } else if (this->coding_rate_ == LORA_CR_4_7) { | ||||
|       cr = "4/7"; | ||||
|     } | ||||
|     ESP_LOGCONFIG(TAG, | ||||
|                   "  Modulation: LORA\n" | ||||
|                   "  Spreading Factor: %" PRIu8 "\n" | ||||
|                   "  Coding Rate: %s\n" | ||||
|                   "  Preamble Size: %" PRIu16, | ||||
|                   this->spreading_factor_, cr, this->preamble_size_); | ||||
|   } | ||||
|   if (!this->sync_value_.empty()) { | ||||
|     ESP_LOGCONFIG(TAG, "  Sync Value: 0x%s", format_hex(this->sync_value_).c_str()); | ||||
|   } | ||||
|   if (this->is_failed()) { | ||||
|     ESP_LOGE(TAG, "Configuring SX126x failed"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace sx126x | ||||
| }  // namespace esphome | ||||
							
								
								
									
										140
									
								
								esphome/components/sx126x/sx126x.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								esphome/components/sx126x/sx126x.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/spi/spi.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "sx126x_reg.h" | ||||
| #include <utility> | ||||
| #include <vector> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace sx126x { | ||||
|  | ||||
| enum SX126xBw : uint8_t { | ||||
|   // FSK | ||||
|   SX126X_BW_4800, | ||||
|   SX126X_BW_5800, | ||||
|   SX126X_BW_7300, | ||||
|   SX126X_BW_9700, | ||||
|   SX126X_BW_11700, | ||||
|   SX126X_BW_14600, | ||||
|   SX126X_BW_19500, | ||||
|   SX126X_BW_23400, | ||||
|   SX126X_BW_29300, | ||||
|   SX126X_BW_39000, | ||||
|   SX126X_BW_46900, | ||||
|   SX126X_BW_58600, | ||||
|   SX126X_BW_78200, | ||||
|   SX126X_BW_93800, | ||||
|   SX126X_BW_117300, | ||||
|   SX126X_BW_156200, | ||||
|   SX126X_BW_187200, | ||||
|   SX126X_BW_234300, | ||||
|   SX126X_BW_312000, | ||||
|   SX126X_BW_373600, | ||||
|   SX126X_BW_467000, | ||||
|   // LORA | ||||
|   SX126X_BW_7810, | ||||
|   SX126X_BW_10420, | ||||
|   SX126X_BW_15630, | ||||
|   SX126X_BW_20830, | ||||
|   SX126X_BW_31250, | ||||
|   SX126X_BW_41670, | ||||
|   SX126X_BW_62500, | ||||
|   SX126X_BW_125000, | ||||
|   SX126X_BW_250000, | ||||
|   SX126X_BW_500000, | ||||
| }; | ||||
|  | ||||
| enum class SX126xError { NONE = 0, TIMEOUT, INVALID_PARAMS }; | ||||
|  | ||||
| class SX126xListener { | ||||
|  public: | ||||
|   virtual void on_packet(const std::vector<uint8_t> &packet, float rssi, float snr) = 0; | ||||
| }; | ||||
|  | ||||
| class SX126x : public Component, | ||||
|                public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING, | ||||
|                                      spi::DATA_RATE_8MHZ> { | ||||
|  public: | ||||
|   size_t get_max_packet_size(); | ||||
|   float get_setup_priority() const override { return setup_priority::PROCESSOR; } | ||||
|   void setup() override; | ||||
|   void loop() override; | ||||
|   void dump_config() override; | ||||
|   void set_bandwidth(SX126xBw bandwidth) { this->bandwidth_ = bandwidth; } | ||||
|   void set_bitrate(uint32_t bitrate) { this->bitrate_ = bitrate; } | ||||
|   void set_busy_pin(InternalGPIOPin *busy_pin) { this->busy_pin_ = busy_pin; } | ||||
|   void set_coding_rate(uint8_t coding_rate) { this->coding_rate_ = coding_rate; } | ||||
|   void set_crc_enable(bool crc_enable) { this->crc_enable_ = crc_enable; } | ||||
|   void set_deviation(uint32_t deviation) { this->deviation_ = deviation; } | ||||
|   void set_dio1_pin(InternalGPIOPin *dio1_pin) { this->dio1_pin_ = dio1_pin; } | ||||
|   void set_frequency(uint32_t frequency) { this->frequency_ = frequency; } | ||||
|   void set_hw_version(const std::string &hw_version) { this->hw_version_ = hw_version; } | ||||
|   void set_mode_rx(); | ||||
|   void set_mode_tx(); | ||||
|   void set_mode_standby(SX126xStandbyMode mode); | ||||
|   void set_mode_sleep(); | ||||
|   void set_modulation(uint8_t modulation) { this->modulation_ = modulation; } | ||||
|   void set_pa_power(int8_t power) { this->pa_power_ = power; } | ||||
|   void set_pa_ramp(uint8_t ramp) { this->pa_ramp_ = ramp; } | ||||
|   void set_payload_length(uint8_t payload_length) { this->payload_length_ = payload_length; } | ||||
|   void set_preamble_detect(uint16_t preamble_detect) { this->preamble_detect_ = preamble_detect; } | ||||
|   void set_preamble_size(uint16_t preamble_size) { this->preamble_size_ = preamble_size; } | ||||
|   void set_rst_pin(InternalGPIOPin *rst_pin) { this->rst_pin_ = rst_pin; } | ||||
|   void set_rx_start(bool rx_start) { this->rx_start_ = rx_start; } | ||||
|   void set_rf_switch(bool rf_switch) { this->rf_switch_ = rf_switch; } | ||||
|   void set_shaping(uint8_t shaping) { this->shaping_ = shaping; } | ||||
|   void set_spreading_factor(uint8_t spreading_factor) { this->spreading_factor_ = spreading_factor; } | ||||
|   void set_sync_value(const std::vector<uint8_t> &sync_value) { this->sync_value_ = sync_value; } | ||||
|   void set_tcxo_voltage(uint8_t tcxo_voltage) { this->tcxo_voltage_ = tcxo_voltage; } | ||||
|   void set_tcxo_delay(uint32_t tcxo_delay) { this->tcxo_delay_ = tcxo_delay; } | ||||
|   void run_image_cal(); | ||||
|   void configure(); | ||||
|   SX126xError transmit_packet(const std::vector<uint8_t> &packet); | ||||
|   void register_listener(SX126xListener *listener) { this->listeners_.push_back(listener); } | ||||
|   Trigger<std::vector<uint8_t>, float, float> *get_packet_trigger() const { return this->packet_trigger_; }; | ||||
|  | ||||
|  protected: | ||||
|   void configure_fsk_ook_(); | ||||
|   void configure_lora_(); | ||||
|   void set_packet_params_(uint8_t payload_length); | ||||
|   uint8_t read_fifo_(uint8_t offset, std::vector<uint8_t> &packet); | ||||
|   void write_fifo_(uint8_t offset, const std::vector<uint8_t> &packet); | ||||
|   void write_opcode_(uint8_t opcode, uint8_t *data, uint8_t size); | ||||
|   uint8_t read_opcode_(uint8_t opcode, uint8_t *data, uint8_t size); | ||||
|   void write_register_(uint16_t reg, uint8_t *data, uint8_t size); | ||||
|   void read_register_(uint16_t reg, uint8_t *data, uint8_t size); | ||||
|   void call_listeners_(const std::vector<uint8_t> &packet, float rssi, float snr); | ||||
|   void wait_busy_(); | ||||
|   Trigger<std::vector<uint8_t>, float, float> *packet_trigger_{new Trigger<std::vector<uint8_t>, float, float>()}; | ||||
|   std::vector<SX126xListener *> listeners_; | ||||
|   std::vector<uint8_t> packet_; | ||||
|   std::vector<uint8_t> sync_value_; | ||||
|   InternalGPIOPin *busy_pin_{nullptr}; | ||||
|   InternalGPIOPin *dio1_pin_{nullptr}; | ||||
|   InternalGPIOPin *rst_pin_{nullptr}; | ||||
|   std::string hw_version_; | ||||
|   char version_[16]; | ||||
|   SX126xBw bandwidth_{SX126X_BW_125000}; | ||||
|   uint32_t bitrate_{0}; | ||||
|   uint32_t deviation_{0}; | ||||
|   uint32_t frequency_{0}; | ||||
|   uint32_t payload_length_{0}; | ||||
|   uint32_t tcxo_delay_{0}; | ||||
|   uint16_t preamble_detect_{0}; | ||||
|   uint16_t preamble_size_{0}; | ||||
|   uint8_t tcxo_voltage_{0}; | ||||
|   uint8_t coding_rate_{0}; | ||||
|   uint8_t modulation_{PACKET_TYPE_LORA}; | ||||
|   uint8_t pa_ramp_{0}; | ||||
|   uint8_t shaping_{0}; | ||||
|   uint8_t spreading_factor_{0}; | ||||
|   int8_t pa_power_{0}; | ||||
|   bool crc_enable_{false}; | ||||
|   bool rx_start_{false}; | ||||
|   bool rf_switch_{false}; | ||||
| }; | ||||
|  | ||||
| }  // namespace sx126x | ||||
| }  // namespace esphome | ||||
							
								
								
									
										163
									
								
								esphome/components/sx126x/sx126x_reg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								esphome/components/sx126x/sx126x_reg.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,163 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/hal.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace sx126x { | ||||
|  | ||||
| static const uint32_t XTAL_FREQ = 32000000; | ||||
|  | ||||
| enum SX126xOpCode : uint8_t { | ||||
|   RADIO_GET_STATUS = 0xC0, | ||||
|   RADIO_WRITE_REGISTER = 0x0D, | ||||
|   RADIO_READ_REGISTER = 0x1D, | ||||
|   RADIO_WRITE_BUFFER = 0x0E, | ||||
|   RADIO_READ_BUFFER = 0x1E, | ||||
|   RADIO_SET_SLEEP = 0x84, | ||||
|   RADIO_SET_STANDBY = 0x80, | ||||
|   RADIO_SET_FS = 0xC1, | ||||
|   RADIO_SET_TX = 0x83, | ||||
|   RADIO_SET_RX = 0x82, | ||||
|   RADIO_SET_RXDUTYCYCLE = 0x94, | ||||
|   RADIO_SET_CAD = 0xC5, | ||||
|   RADIO_SET_TXCONTINUOUSWAVE = 0xD1, | ||||
|   RADIO_SET_TXCONTINUOUSPREAMBLE = 0xD2, | ||||
|   RADIO_SET_PACKETTYPE = 0x8A, | ||||
|   RADIO_GET_PACKETTYPE = 0x11, | ||||
|   RADIO_SET_RFFREQUENCY = 0x86, | ||||
|   RADIO_SET_TXPARAMS = 0x8E, | ||||
|   RADIO_SET_PACONFIG = 0x95, | ||||
|   RADIO_SET_CADPARAMS = 0x88, | ||||
|   RADIO_SET_BUFFERBASEADDRESS = 0x8F, | ||||
|   RADIO_SET_MODULATIONPARAMS = 0x8B, | ||||
|   RADIO_SET_PACKETPARAMS = 0x8C, | ||||
|   RADIO_GET_RXBUFFERSTATUS = 0x13, | ||||
|   RADIO_GET_PACKETSTATUS = 0x14, | ||||
|   RADIO_GET_RSSIINST = 0x15, | ||||
|   RADIO_GET_STATS = 0x10, | ||||
|   RADIO_RESET_STATS = 0x00, | ||||
|   RADIO_SET_DIOIRQPARAMS = 0x08, | ||||
|   RADIO_GET_IRQSTATUS = 0x12, | ||||
|   RADIO_CLR_IRQSTATUS = 0x02, | ||||
|   RADIO_CALIBRATE = 0x89, | ||||
|   RADIO_CALIBRATEIMAGE = 0x98, | ||||
|   RADIO_SET_REGULATORMODE = 0x96, | ||||
|   RADIO_GET_ERROR = 0x17, | ||||
|   RADIO_CLR_ERROR = 0x07, | ||||
|   RADIO_SET_TCXOMODE = 0x97, | ||||
|   RADIO_SET_TXFALLBACKMODE = 0x93, | ||||
|   RADIO_SET_RFSWITCHMODE = 0x9D, | ||||
|   RADIO_SET_STOPRXTIMERONPREAMBLE = 0x9F, | ||||
|   RADIO_SET_LORASYMBTIMEOUT = 0xA0, | ||||
| }; | ||||
|  | ||||
| enum SX126xRegister : uint16_t { | ||||
|   REG_VERSION_STRING = 0x0320, | ||||
|   REG_GFSK_SYNCWORD = 0x06C0, | ||||
|   REG_LORA_SYNCWORD = 0x0740, | ||||
|   REG_OCP = 0x08E7, | ||||
| }; | ||||
|  | ||||
| enum SX126xStandbyMode : uint8_t { | ||||
|   STDBY_RC = 0x00, | ||||
|   STDBY_XOSC = 0x01, | ||||
| }; | ||||
|  | ||||
| enum SX126xPacketType : uint8_t { | ||||
|   PACKET_TYPE_GFSK = 0x00, | ||||
|   PACKET_TYPE_LORA = 0x01, | ||||
|   PACKET_TYPE_LRHSS = 0x03, | ||||
| }; | ||||
|  | ||||
| enum SX126xFskBw : uint8_t { | ||||
|   FSK_BW_4800 = 0x1F, | ||||
|   FSK_BW_5800 = 0x17, | ||||
|   FSK_BW_7300 = 0x0F, | ||||
|   FSK_BW_9700 = 0x1E, | ||||
|   FSK_BW_11700 = 0x16, | ||||
|   FSK_BW_14600 = 0x0E, | ||||
|   FSK_BW_19500 = 0x1D, | ||||
|   FSK_BW_23400 = 0x15, | ||||
|   FSK_BW_29300 = 0x0D, | ||||
|   FSK_BW_39000 = 0x1C, | ||||
|   FSK_BW_46900 = 0x14, | ||||
|   FSK_BW_58600 = 0x0C, | ||||
|   FSK_BW_78200 = 0x1B, | ||||
|   FSK_BW_93800 = 0x13, | ||||
|   FSK_BW_117300 = 0x0B, | ||||
|   FSK_BW_156200 = 0x1A, | ||||
|   FSK_BW_187200 = 0x12, | ||||
|   FSK_BW_234300 = 0x0A, | ||||
|   FSK_BW_312000 = 0x19, | ||||
|   FSK_BW_373600 = 0x11, | ||||
|   FSK_BW_467000 = 0x09, | ||||
| }; | ||||
|  | ||||
| enum SX126xLoraBw : uint8_t { | ||||
|   LORA_BW_7810 = 0x00, | ||||
|   LORA_BW_10420 = 0x08, | ||||
|   LORA_BW_15630 = 0x01, | ||||
|   LORA_BW_20830 = 0x09, | ||||
|   LORA_BW_31250 = 0x02, | ||||
|   LORA_BW_41670 = 0x0A, | ||||
|   LORA_BW_62500 = 0x03, | ||||
|   LORA_BW_125000 = 0x04, | ||||
|   LORA_BW_250000 = 0x05, | ||||
|   LORA_BW_500000 = 0x06, | ||||
| }; | ||||
|  | ||||
| enum SX126xLoraCr : uint8_t { | ||||
|   LORA_CR_4_5 = 0x01, | ||||
|   LORA_CR_4_6 = 0x02, | ||||
|   LORA_CR_4_7 = 0x03, | ||||
|   LORA_CR_4_8 = 0x04, | ||||
| }; | ||||
|  | ||||
| enum SX126xIrqMasks : uint16_t { | ||||
|   IRQ_RADIO_NONE = 0x0000, | ||||
|   IRQ_TX_DONE = 0x0001, | ||||
|   IRQ_RX_DONE = 0x0002, | ||||
|   IRQ_PREAMBLE_DETECTED = 0x0004, | ||||
|   IRQ_SYNCWORD_VALID = 0x0008, | ||||
|   IRQ_HEADER_VALID = 0x0010, | ||||
|   IRQ_HEADER_ERROR = 0x0020, | ||||
|   IRQ_CRC_ERROR = 0x0040, | ||||
|   IRQ_CAD_DONE = 0x0080, | ||||
|   IRQ_CAD_ACTIVITY_DETECTED = 0x0100, | ||||
|   IRQ_RX_TX_TIMEOUT = 0x0200, | ||||
|   IRQ_RADIO_ALL = 0xFFFF, | ||||
| }; | ||||
|  | ||||
| enum SX126xTcxoCtrl : uint8_t { | ||||
|   TCXO_CTRL_1_6V = 0x00, | ||||
|   TCXO_CTRL_1_7V = 0x01, | ||||
|   TCXO_CTRL_1_8V = 0x02, | ||||
|   TCXO_CTRL_2_2V = 0x03, | ||||
|   TCXO_CTRL_2_4V = 0x04, | ||||
|   TCXO_CTRL_2_7V = 0x05, | ||||
|   TCXO_CTRL_3_0V = 0x06, | ||||
|   TCXO_CTRL_3_3V = 0x07, | ||||
|   TCXO_CTRL_NONE = 0xFF, | ||||
| }; | ||||
|  | ||||
| enum SX126xPulseShape : uint8_t { | ||||
|   NO_FILTER = 0x00, | ||||
|   GAUSSIAN_BT_0_3 = 0x08, | ||||
|   GAUSSIAN_BT_0_5 = 0x09, | ||||
|   GAUSSIAN_BT_0_7 = 0x0A, | ||||
|   GAUSSIAN_BT_1_0 = 0x0B, | ||||
| }; | ||||
|  | ||||
| enum SX126xRampTime : uint8_t { | ||||
|   PA_RAMP_10 = 0x00, | ||||
|   PA_RAMP_20 = 0x01, | ||||
|   PA_RAMP_40 = 0x02, | ||||
|   PA_RAMP_80 = 0x03, | ||||
|   PA_RAMP_200 = 0x04, | ||||
|   PA_RAMP_800 = 0x05, | ||||
|   PA_RAMP_1700 = 0x06, | ||||
|   PA_RAMP_3400 = 0x07, | ||||
| }; | ||||
|  | ||||
| }  // namespace sx126x | ||||
| }  // namespace esphome | ||||
							
								
								
									
										40
									
								
								tests/components/sx126x/common.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								tests/components/sx126x/common.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| spi: | ||||
|   clk_pin: ${clk_pin} | ||||
|   mosi_pin: ${mosi_pin} | ||||
|   miso_pin: ${miso_pin} | ||||
|  | ||||
| sx126x: | ||||
|   dio1_pin: ${dio1_pin} | ||||
|   cs_pin: ${cs_pin} | ||||
|   busy_pin: ${busy_pin} | ||||
|   rst_pin: ${rst_pin} | ||||
|   pa_power: 3 | ||||
|   bandwidth: 125_0kHz | ||||
|   crc_enable: true | ||||
|   frequency: 433920000 | ||||
|   modulation: LORA | ||||
|   rx_start: true | ||||
|   hw_version: sx1262 | ||||
|   rf_switch: true | ||||
|   sync_value: [0x14, 0x24] | ||||
|   preamble_size: 8 | ||||
|   spreading_factor: 7 | ||||
|   coding_rate: CR_4_6 | ||||
|   tcxo_voltage: 1_8V | ||||
|   tcxo_delay: 5ms | ||||
|   on_packet: | ||||
|     then: | ||||
|       - lambda: |- | ||||
|           ESP_LOGD("lambda", "packet %.2f %.2f %s", rssi, snr, format_hex(x).c_str()); | ||||
|  | ||||
| button: | ||||
|   - platform: template | ||||
|     name: "SX126x Button" | ||||
|     on_press: | ||||
|       then: | ||||
|         - sx126x.set_mode_standby | ||||
|         - sx126x.run_image_cal | ||||
|         - sx126x.set_mode_sleep | ||||
|         - sx126x.set_mode_rx | ||||
|         - sx126x.send_packet: | ||||
|             data: [0xC5, 0x51, 0x78, 0x82, 0xB7, 0xF9, 0x9C, 0x5C] | ||||
							
								
								
									
										10
									
								
								tests/components/sx126x/test.esp32-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/components/sx126x/test.esp32-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| substitutions: | ||||
|   clk_pin: GPIO5 | ||||
|   mosi_pin: GPIO27 | ||||
|   miso_pin: GPIO19 | ||||
|   cs_pin: GPIO18 | ||||
|   rst_pin: GPIO23 | ||||
|   busy_pin: GPIO25 | ||||
|   dio1_pin: GPIO26 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										10
									
								
								tests/components/sx126x/test.esp32-c3-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/components/sx126x/test.esp32-c3-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| substitutions: | ||||
|   clk_pin: GPIO5 | ||||
|   mosi_pin: GPIO18 | ||||
|   miso_pin: GPIO19 | ||||
|   cs_pin: GPIO1 | ||||
|   rst_pin: GPIO2 | ||||
|   busy_pin: GPIO4 | ||||
|   dio1_pin: GPIO3 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										10
									
								
								tests/components/sx126x/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/components/sx126x/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| substitutions: | ||||
|   clk_pin: GPIO5 | ||||
|   mosi_pin: GPIO18 | ||||
|   miso_pin: GPIO19 | ||||
|   cs_pin: GPIO1 | ||||
|   rst_pin: GPIO2 | ||||
|   busy_pin: GPIO4 | ||||
|   dio1_pin: GPIO3 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										10
									
								
								tests/components/sx126x/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/components/sx126x/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| substitutions: | ||||
|   clk_pin: GPIO5 | ||||
|   mosi_pin: GPIO27 | ||||
|   miso_pin: GPIO19 | ||||
|   cs_pin: GPIO18 | ||||
|   rst_pin: GPIO23 | ||||
|   busy_pin: GPIO25 | ||||
|   dio1_pin: GPIO26 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										10
									
								
								tests/components/sx126x/test.esp8266-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/components/sx126x/test.esp8266-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| substitutions: | ||||
|   clk_pin: GPIO5 | ||||
|   mosi_pin: GPIO13 | ||||
|   miso_pin: GPIO12 | ||||
|   cs_pin: GPIO1 | ||||
|   rst_pin: GPIO2 | ||||
|   busy_pin: GPIO4 | ||||
|   dio1_pin: GPIO3 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										10
									
								
								tests/components/sx126x/test.rp2040-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/components/sx126x/test.rp2040-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| substitutions: | ||||
|   clk_pin: GPIO2 | ||||
|   mosi_pin: GPIO3 | ||||
|   miso_pin: GPIO4 | ||||
|   cs_pin: GPIO5 | ||||
|   rst_pin: GPIO6 | ||||
|   busy_pin: GPIO8 | ||||
|   dio1_pin: GPIO7 | ||||
|  | ||||
| <<: !include common.yaml | ||||
		Reference in New Issue
	
	Block a user