├── doc └── images │ └── webserver-screenshot.png ├── secrets.yaml ├── esphome └── components │ ├── lora_sx126x │ ├── text_sensor.py │ ├── sensor.py │ ├── switch │ │ ├── lora_sx126x_switch.h │ │ ├── lora_sx126x_switch.cpp │ │ └── __init__.py │ ├── __init__.py │ ├── lora_sx126x.h │ └── lora_sx126x.cpp │ └── lorawan_sx126x │ ├── text_sensor.py │ ├── __init__.py │ ├── lorawan_sx126x.h │ ├── lorawan_sx126x.cpp │ └── LoRaWAN.ino ├── LICENSE ├── examples ├── lora-sx126x-config.yaml ├── lora-sx126x-battery.yaml ├── lora-sx126x.yaml └── lora-sx126x-switch.yaml ├── lora-receiver.yaml ├── lorawan-sx126x.yaml ├── .gitignore └── README.org /doc/images/webserver-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaulSchulz/esphome-lora-sx126x/HEAD/doc/images/webserver-screenshot.png -------------------------------------------------------------------------------- /secrets.yaml: -------------------------------------------------------------------------------- 1 | # Your Wi-Fi SSID and password 2 | # Please change these to reflect your local settings. 3 | wifi_ssid: "WIFI_SSID" 4 | wifi_password: "WIFI_PASSWORD" 5 | 6 | wifi_ap_password: "WIFI_AP_PASSWORD" 7 | 8 | api_encryption_key: "tILzbeeAMXW/67mOU3PHsRoSBW2BTsqyC1N1G1BGTwI=" 9 | ota_password: "OTA_PASSWORD" 10 | 11 | lorawan_app_eui: "0000000000000000" 12 | lorawan_app_key: "00000000000000000000000000000000" 13 | # lorawan_device_eui: "0000000000000000" 14 | -------------------------------------------------------------------------------- /esphome/components/lora_sx126x/text_sensor.py: -------------------------------------------------------------------------------- 1 | import esphome.codegen as cg 2 | import esphome.config_validation as cv 3 | from esphome.components import text_sensor 4 | 5 | #from esphome.const import ( 6 | # STATE_CLASS_MEASUREMENT, 7 | #) 8 | 9 | lora_sx126x_rssi_ns = cg.esphome_ns.namespace("lora_sx126x") 10 | # ^^^^^^^^^^^ 11 | # ^ 12 | # | 13 | # C++ namespace under "esphome::"" ----------------' 14 | 15 | PktSensor = lora_sx126x_rssi_ns.class_( 16 | "LoraSX126Xpkt", text_sensor.TextSensor, cg.Component 17 | ) 18 | 19 | CONFIG_SCHEMA = ( 20 | text_sensor.text_sensor_schema( 21 | PktSensor, 22 | ) 23 | ) 24 | 25 | async def to_code(config): 26 | var = await text_sensor.new_text_sensor(config) 27 | await cg.register_component(var, config) 28 | -------------------------------------------------------------------------------- /esphome/components/lorawan_sx126x/text_sensor.py: -------------------------------------------------------------------------------- 1 | import esphome.codegen as cg 2 | import esphome.config_validation as cv 3 | from esphome.components import text_sensor 4 | 5 | #from esphome.const import ( 6 | # STATE_CLASS_MEASUREMENT, 7 | #) 8 | 9 | lora_sx126x_rssi_ns = cg.esphome_ns.namespace("lora_sx126x") 10 | # ^^^^^^^^^^^ 11 | # ^ 12 | # | 13 | # C++ namespace under "esphome::"" ----------------' 14 | 15 | PktSensor = lora_sx126x_rssi_ns.class_( 16 | "LoraSX126Xpkt", text_sensor.TextSensor, cg.Component 17 | ) 18 | 19 | CONFIG_SCHEMA = ( 20 | text_sensor.text_sensor_schema( 21 | PktSensor, 22 | ) 23 | ) 24 | 25 | async def to_code(config): 26 | var = await text_sensor.new_text_sensor(config) 27 | await cg.register_component(var, config) 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Paul Schulz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /esphome/components/lora_sx126x/sensor.py: -------------------------------------------------------------------------------- 1 | import esphome.codegen as cg 2 | import esphome.config_validation as cv 3 | from esphome.components import sensor 4 | 5 | from esphome.const import ( 6 | STATE_CLASS_MEASUREMENT, 7 | ) 8 | 9 | lora_sx126x_rssi_ns = cg.esphome_ns.namespace("lora_sx126x") 10 | # ^^^^^^^^^^^ 11 | # ^ 12 | # | 13 | # C++ namespace under "esphome::"" ----------------' 14 | 15 | RSSISensor = lora_sx126x_rssi_ns.class_( 16 | "LoraSX126Xrssi", sensor.Sensor, cg.Component 17 | # ^^^^^^^^^ ^^^^^^^^^^^^^ 18 | # ^ ^ 19 | # | | 20 | # | `-- Inherenting from Sensor component 21 | # `----- Class name of object 22 | ) 23 | 24 | CONFIG_SCHEMA = ( 25 | sensor.sensor_schema( 26 | RSSISensor, 27 | accuracy_decimals=1, 28 | state_class=STATE_CLASS_MEASUREMENT, 29 | ) 30 | ) 31 | 32 | async def to_code(config): 33 | var = await sensor.new_sensor(config) 34 | await cg.register_component(var, config) 35 | -------------------------------------------------------------------------------- /examples/lora-sx126x-config.yaml: -------------------------------------------------------------------------------- 1 | 2 | # # Interface to radio chip 3 | # pin_lora_reset: 12 # LoRa Reset 4 | # pin-lora_dio_1: 14 # LoRa DIO_1 5 | # pin_lora_busy: 13 # LoRa SPI Busy 6 | # pin_lora_nss: 8 # LoRa SPI CS (Chip Select) 7 | # pin_lora_sclk: 9 # LoRa SPI SCLK 8 | # pin_lora_miso: 11 # LoRa SPI MISO (Master In, Slave Out) 9 | # pin_lora_mosi: 10 # LoRa SPI MOSI (Master Out, Slave In) 10 | # radio_txen: -1 # LoRa Antenna TX Enable, on some boards. 11 | # radio_rxen: -1 # LoRa Antenna RX Enable, on some boards. 12 | 13 | # LoRa Options 14 | # Setting these will enable the LoRa radio mode. 15 | rf_frequency: 915000000 # Hz - Manditory 16 | # tx_output_power: 22 # dBm 17 | # lora_bandwidth: 0 # [0: 1 25 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] 18 | # lora_spreading_factor: 7 # [SF7..SF12] 19 | # lora_codingrate: 1 # [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] 20 | # lora_preamble_length: 8 # Same for Tx and Rx 21 | # lora_symbol_timeout: 0 # Symbols 22 | # lora_fx_length_payload_on: -1 # Default: -1 (False) 23 | # lora_iq_inversion_on: -1 # Default: -1 (False) 24 | # rx_timeout_value: 3000 # ms 25 | # tx_timeout_value: 3000 # ms 26 | -------------------------------------------------------------------------------- /esphome/components/lora_sx126x/switch/lora_sx126x_switch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esphome/core/component.h" 4 | #include "esphome/core/automation.h" 5 | #include "esphome/components/switch/switch.h" 6 | 7 | namespace esphome { 8 | namespace lora_sx126x { 9 | class LoRaSX126XSwitch : public switch_::Switch, public Component { 10 | public: 11 | LoRaSX126XSwitch(); 12 | 13 | void setup() override; 14 | void dump_config() override; 15 | 16 | void set_state_lambda(std::function()> &&f); 17 | Trigger<> *get_turn_on_trigger() const; 18 | Trigger<> *get_turn_off_trigger() const; 19 | void set_optimistic(bool optimistic); 20 | void set_assumed_state(bool assumed_state); 21 | void loop() override; 22 | 23 | float get_setup_priority() const override; 24 | 25 | protected: 26 | bool assumed_state() override; 27 | 28 | void write_state(bool state) override; 29 | 30 | optional()>> f_; 31 | bool optimistic_{false}; 32 | bool assumed_state_{false}; 33 | Trigger<> *turn_on_trigger_; 34 | Trigger<> *turn_off_trigger_; 35 | Trigger<> *prev_trigger_{nullptr}; 36 | }; 37 | 38 | } // namespace lora_sx126x 39 | } // namespace esphome 40 | -------------------------------------------------------------------------------- /lora-receiver.yaml: -------------------------------------------------------------------------------- 1 | # This file is an example with a setup for using the SX126X LoRa radio chip. 2 | esphome: 3 | name: "lora-receiver" 4 | libraries: 5 | - "SPI" 6 | - "Ticker" 7 | - "SX126x-Arduino" 8 | 9 | esp32: 10 | board: esp32-s3-devkitc-1 11 | framework: 12 | type: arduino 13 | 14 | external_components: 15 | - source: 16 | type: local 17 | path: esphome/components 18 | #type: git 19 | #url: github://PaulSchulz/esphome-lora-sx126x 20 | #ref: main 21 | components: ["lora_sx126x"] 22 | 23 | # Enable logging 24 | logger: 25 | hardware_uart: "UART0" # Heltec V3 boards 26 | 27 | wifi: 28 | networks: 29 | - ssid: !secret wifi_ssid 30 | password: !secret wifi_password 31 | 32 | ota: 33 | platform: esphome 34 | password: !secret ota_password 35 | 36 | # Enable Home Assistant API 37 | api: 38 | encryption: 39 | key: !secret api_encryption_key 40 | 41 | web_server: 42 | port: 80 43 | 44 | # Setup LoRa Radio 45 | lora_sx126x: 46 | name: "LoRa Radio" 47 | rf_frequency: 915000000 # Hz - Required 48 | 49 | # Report on the received radio signal power 50 | sensor: 51 | - platform: lora_sx126x 52 | id: lorarssi 53 | name: lorarssi # Required for publishing into API and HA 54 | 55 | # Read packet data from radio. 56 | text_sensor: 57 | - platform: lora_sx126x 58 | id: packet_in 59 | name: packet_in 60 | -------------------------------------------------------------------------------- /esphome/components/lora_sx126x/switch/lora_sx126x_switch.cpp: -------------------------------------------------------------------------------- 1 | #include "lora_sx126x_switch.h" 2 | #include "esphome/core/log.h" 3 | 4 | namespace esphome { 5 | namespace lora_sx126x { 6 | 7 | static const char *const TAG = "lora_sx126x.switch"; 8 | 9 | LoRaSX126XSwitch::LoRaSX126XSwitch() : turn_on_trigger_(new Trigger<>()), turn_off_trigger_(new Trigger<>()) {} 10 | 11 | void LoRaSX126XSwitch::loop() { 12 | if (!this->f_.has_value()) 13 | return; 14 | auto s = (*this->f_)(); 15 | if (!s.has_value()) 16 | return; 17 | 18 | this->publish_state(*s); 19 | } 20 | void LoRaSX126XSwitch::write_state(bool state) { 21 | if (this->prev_trigger_ != nullptr) { 22 | this->prev_trigger_->stop_action(); 23 | } 24 | 25 | if (state) { 26 | this->prev_trigger_ = this->turn_on_trigger_; 27 | this->turn_on_trigger_->trigger(); 28 | } else { 29 | this->prev_trigger_ = this->turn_off_trigger_; 30 | this->turn_off_trigger_->trigger(); 31 | } 32 | 33 | if (this->optimistic_) 34 | this->publish_state(state); 35 | } 36 | void LoRaSX126XSwitch::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } 37 | bool LoRaSX126XSwitch::assumed_state() { return this->assumed_state_; } 38 | void LoRaSX126XSwitch::set_state_lambda(std::function()> &&f) { this->f_ = f; } 39 | float LoRaSX126XSwitch::get_setup_priority() const { return setup_priority::HARDWARE - 2.0f; } 40 | Trigger<> *LoRaSX126XSwitch::get_turn_on_trigger() const { return this->turn_on_trigger_; } 41 | Trigger<> *LoRaSX126XSwitch::get_turn_off_trigger() const { return this->turn_off_trigger_; } 42 | void LoRaSX126XSwitch::setup() { 43 | optional initial_state = this->get_initial_state_with_restore_mode(); 44 | 45 | if (initial_state.has_value()) { 46 | ESP_LOGD(TAG, " Restored state %s", ONOFF(initial_state.value())); 47 | // if it has a value, restore_mode is not "DISABLED", therefore act on the switch: 48 | if (initial_state.value()) { 49 | this->turn_on(); 50 | } else { 51 | this->turn_off(); 52 | } 53 | } 54 | } 55 | void LoRaSX126XSwitch::dump_config() { 56 | LOG_SWITCH("", "Template Switch", this); 57 | ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_)); 58 | } 59 | void LoRaSX126XSwitch::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; } 60 | 61 | } // namespace lora_sx126x 62 | } // namespace esphome 63 | -------------------------------------------------------------------------------- /examples/lora-sx126x-battery.yaml: -------------------------------------------------------------------------------- 1 | # This file is an example with a setup for using the SX126X LoRa radio chip. 2 | 3 | esphome: 4 | name: "lora-sx126x" 5 | libraries: 6 | - "SPI" 7 | - "Ticker" 8 | - "SX126x-Arduino" 9 | 10 | esp32: 11 | board: esp32-s3-devkitc-1 12 | framework: 13 | type: arduino 14 | 15 | external_components: 16 | - source: 17 | type: local 18 | path: esphome/components 19 | components: ["lora_sx126x"] 20 | 21 | - source: 22 | type: local 23 | path: ../esphome-heltec-battery/components 24 | components: ["empty_sensor"] 25 | 26 | # Enable logging 27 | logger: 28 | hardware_uart: "UART0" # Heltec V3 boards 29 | 30 | wifi: 31 | networks: 32 | - ssid: !secret wifi_ssid 33 | password: !secret wifi_password 34 | 35 | ota: 36 | platform: esphome 37 | password: !secret ota_password 38 | 39 | # Enable Home Assistant API 40 | api: 41 | encryption: 42 | key: !secret api_encryption_key 43 | 44 | # 45 | web_server: 46 | port: 80 47 | 48 | #spi:i 49 | # clk_pin: GPIO9 50 | # mosi_pin: GPIO10s 51 | # miso_pin: GPIO11 52 | 53 | #i2c: 54 | # - id: bus_a 55 | # sda: 17 56 | # scl: 18 57 | 58 | #lora_sx126x: 59 | # name: "LoRa Radio" 60 | # # include LoRa radio configuration 61 | 62 | # lora_bandwidth: 1 # [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] 63 | # lora_spreading_factor: 11 # [SF7..SF12] 64 | # lora_codingrate: 1 # [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] 65 | # lora_preamble_length: 8 # same for Tx and Rx 66 | 67 | sensor: 68 | # - platform: lora_sx126x 69 | # id: lorarssi 70 | # name: lorarssi # Required for publishing into API and HA 71 | 72 | - platform: empty_sensor 73 | name: Empty Sensor 74 | 75 | text_sensor: 76 | - platform: wifi_info 77 | ip_address: 78 | name: ESP IP Address 79 | - platform: version 80 | name: "ESPHome Version" 81 | - platform: template 82 | name: "Template Text Sensor" 83 | lambda: |- 84 | return {"Hello World"}; 85 | update_interval: 60s 86 | - platform: lora_sx126x 87 | id: packet_in 88 | name: packet_in 89 | 90 | binary_sensor: 91 | - platform: gpio 92 | pin: 5 93 | id: trigger 94 | name: "Trigger (In)" 95 | on_press: 96 | then: 97 | - switch.turn_on: monitor_switch 98 | on_release: 99 | then: 100 | - switch.turn_off: monitor_switch 101 | 102 | output: 103 | - platform: gpio 104 | pin: 6 105 | id: monitor 106 | - platform: gpio 107 | pin: 35 108 | id: monitor_led 109 | 110 | switch: 111 | - platform: output 112 | id: monitor_switch 113 | name: "Monitor Output (Out)" 114 | output: 115 | # monitor 116 | monitor_led 117 | 118 | 119 | -------------------------------------------------------------------------------- /lorawan-sx126x.yaml: -------------------------------------------------------------------------------- 1 | # This file is an example with a setup for using the SX126X LoRa radio chip as LoRaWAN 2 | 3 | esphome: 4 | name: "lora-sx126x" 5 | libraries: 6 | - "SPI" 7 | - "Ticker" 8 | - "SX126x-Arduino" 9 | 10 | esp32: 11 | board: esp32-s3-devkitc-1 12 | framework: 13 | type: arduino 14 | 15 | external_components: 16 | - source: 17 | type: local 18 | path: esphome/components 19 | components: ["lorawan_sx126x"] 20 | 21 | # Enable logging 22 | logger: 23 | 24 | wifi: 25 | networks: 26 | - ssid: !secret wifi_ssid 27 | password: !secret wifi_password 28 | 29 | ota: 30 | platform: esphome 31 | password: !secret ota_password 32 | 33 | # Enable Home Assistant API 34 | api: 35 | encryption: 36 | key: !secret api_encryption_key 37 | 38 | # 39 | web_server: 40 | port: 80 41 | 42 | #spi: 43 | # clk_pin: GPIO9 44 | # mosi_pin: GPIO10 45 | # miso_pin: GPIO11 46 | 47 | #i2c: 48 | # - id: bus_a 49 | # sda: 17 50 | # scl: 18 51 | 52 | # LoRaWAN Specific Oprions 53 | # Setting these will enable the LoRaWAN radio mode 54 | lorawan_sx126x: 55 | 56 | # # Interface to radio chip 57 | # pin_lora_reset: 12 # LoRa Reset 58 | # pin-lora_dio_1: 14 # LoRa DIO_1 59 | # pin_lora_busy: 13 # LoRa SPI Busy 60 | # pin_lora_nss: 8 # LoRa SPI CS (Chip Select) 61 | # pin_lora_sclk: 9 # LoRa SPI SCLK 62 | # pin_lora_miso: 11 # LoRa SPI MISO (Master In, Slave Out) 63 | # pin_lora_mosi: 10 # LoRa SPI MOSI (Master Out, Slave In) 64 | # radio_txen: -1 # LoRa Antenna TX Enable, on some boards. 65 | # radio_rxen: -1 # LoRa Antenna RX Enable, on some boards. 66 | 67 | region: AU915 68 | # # AS923 69 | # # AU915 70 | # # CN470 71 | # # CN779 72 | # # EU433 73 | # # EU868 74 | # # IN865 75 | # # KR920 76 | # # US915 77 | # # AS923_2 78 | # # AS923_3 79 | # # AS923_4 80 | # # RU864 81 | subchannel: 2 82 | device_type: CLASS_A 83 | # # CLASS_B 84 | # # CLASS_C 85 | 86 | authentication: OTAA 87 | # OTAA - Over the Air 88 | # ABP - Activation by Personalization 89 | 90 | # device_eui: !secret lorawan_device_eui # 8 bytes 91 | # The Device EUI is unique to the device, It can be set here, or will be set 92 | # using the WiFi MAC address, If it is set automatically, then this code 93 | # won't need to be recompiled for every device. 94 | 95 | app_eui: !secret lorawan_join_eui # 8 bytes 96 | # Also referred to as 'join_eui' 97 | 98 | # The following is used for Over The Air Authentication (OTAA) 99 | app_key: !secret lorawan_app_key # 16 bytes 100 | 101 | # # The following is required for Activation By Personalization (ABP) - Untested 102 | # nwks_key: "00000000000000000000000000000000" # 16 bytes 103 | # apps_key: "00000000000000000000000000000000" # 16 bytes 104 | # dev_addr: "00000000000000000000000000000000" # 16 bytes 105 | -------------------------------------------------------------------------------- /esphome/components/lorawan_sx126x/__init__.py: -------------------------------------------------------------------------------- 1 | import esphome.codegen as cg 2 | import esphome.config_validation as cv 3 | from esphome.const import CONF_ID 4 | 5 | CODEOWNERS = ["@PaulSchulz"] 6 | AUTO_LOAD = [ "sensor","text_sensor"] 7 | DEPENDANCIES = ["spi"] 8 | 9 | lorawan_sx126x_ns = cg.esphome_ns.namespace("lorawan_sx126x") 10 | 11 | # empty_component_ns = cg.esphome_ns.namespace('empty_component') 12 | LoRaWANSX126X = lorawan_sx126x_ns.class_('LoRaWANSX126X', cg.Component) 13 | 14 | # Hardware 15 | # Heltec Wifi LoRa 32 (V3) - SX126x pin configuration 16 | PIN_LORA_RESET = 12 # LORA RESET 17 | PIN_LORA_DIO_1 = 14 # LORA DIO_1 18 | PIN_LORA_BUSY = 13 # LORA SPI BUSY 19 | PIN_LORA_NSS = 8 # LORA SPI CS 20 | PIN_LORA_SCLK = 9 # LORA SPI CLK 21 | PIN_LORA_MISO = 11 # LORA SPI MISO 22 | PIN_LORA_MOSI = 10 # LORA SPI MOSI 23 | RADIO_TXEN = -1 # LORA ANTENNA TX ENABLE 24 | RADIO_RXEN = -1 # LORA ANTENNA RX ENABLE 25 | 26 | # LoRaWAN Radio Parameters 27 | CONF_LORAWAN_DEVICE_EUI = "" 28 | 29 | CONFIG_SCHEMA = cv.Schema({ 30 | cv.GenerateID(): cv.declare_id(LoraSX126X), 31 | cv.Optional('name'): cv.string, 32 | 33 | cv.Optional('pin_lora_reset', default=PIN_LORA_RESET): cv.int_, 34 | cv.Optional('pin_lora_dio_1', default=PIN_LORA_DIO_1): cv.int_, 35 | cv.Optional('pin_lora_busy', default=PIN_LORA_BUSY): cv.int_, 36 | cv.Optional('pin_lora_nss', default=PIN_LORA_NSS): cv.int_, 37 | cv.Optional('pin_lora_sclk', default=PIN_LORA_SCLK): cv.int_, 38 | cv.Optional('pin_lora_miso', default=PIN_LORA_MISO): cv.int_, 39 | cv.Optional('pin_lora_mosi', default=PIN_LORA_MOSI): cv.int_, 40 | cv.Optional('radio_txen', default=RADIO_TXEN): cv.int_, 41 | cv.Optional('radio_rxen', default=RADIO_RXEN): cv.int_, 42 | 43 | cv.Required('region'): cv.string, 44 | cv.Required('authtype'): cv.string, 45 | cv.Optional('device_eui', default=CONF_LORAWAN_DEVICE_EUI): cv.string, 46 | cv.Required('app_eui'): cv.string, 47 | cv.Required('app_key'): cv.string, 48 | 49 | }).extend(cv.COMPONENT_SCHEMA) 50 | 51 | def to_code(config): 52 | var = cg.new_Pvariable(config[CONF_ID]) 53 | yield cg.register_component(var, config) 54 | 55 | cg.add(var.set_pin_lora_reset(config['pin_lora_reset'])) 56 | cg.add(var.set_pin_lora_dio_1(config['pin_lora_dio_1'])) 57 | cg.add(var.set_pin_lora_busy(config['pin_lora_busy'])) 58 | cg.add(var.set_pin_lora_nss(config['pin_lora_nss'])) 59 | cg.add(var.set_pin_lora_sclk(config['pin_lora_sclk'])) 60 | cg.add(var.set_pin_lora_miso(config['pin_lora_miso'])) 61 | cg.add(var.set_pin_lora_mosi(config['pin_lora_mosi'])) 62 | cg.add(var.set_radio_txen(config['radio_txen'])) 63 | cg.add(var.set_radio_rxen(config['radio_rxen'])) 64 | 65 | cg.add(var.set_lorawan_region(config['region'])) 66 | cg.add(var.set_lorawan_authtype(config['authtype'])) 67 | cg.add(var.set_lorawan_device_eui(config['device_eui'])) 68 | cg.add(var.set_lorawan_app_eui(config['app_eui'])) 69 | cg.add(var.set_lorawan_app_key(config['app_key'])) 70 | -------------------------------------------------------------------------------- /esphome/components/lora_sx126x/switch/__init__.py: -------------------------------------------------------------------------------- 1 | import esphome.codegen as cg 2 | import esphome.config_validation as cv 3 | from esphome import automation 4 | from esphome.components import switch 5 | from esphome.const import ( 6 | CONF_ASSUMED_STATE, 7 | CONF_ID, 8 | CONF_LAMBDA, 9 | CONF_OPTIMISTIC, 10 | CONF_RESTORE_STATE, 11 | CONF_STATE, 12 | CONF_TURN_OFF_ACTION, 13 | CONF_TURN_ON_ACTION, 14 | ) 15 | 16 | from .. import lora_sx126x_ns 17 | 18 | LoRaSX126XSwitch = lora_sx126x_ns.class_("LoRaSX126XSwitch", switch.Switch, cg.Component) 19 | 20 | def validate(config): 21 | if ( 22 | not config[CONF_OPTIMISTIC] 23 | and CONF_TURN_ON_ACTION not in config 24 | and CONF_TURN_OFF_ACTION not in config 25 | ): 26 | raise cv.Invalid( 27 | "Either optimistic mode must be enabled, or turn_on_action or turn_off_action must be set, " 28 | "to handle the switch being set." 29 | ) 30 | return config 31 | 32 | 33 | CONFIG_SCHEMA = cv.All( 34 | switch.switch_schema(LoRaSX126XSwitch) 35 | .extend( 36 | { 37 | cv.Optional(CONF_LAMBDA): cv.returning_lambda, 38 | cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, 39 | cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean, 40 | cv.Optional(CONF_TURN_OFF_ACTION): automation.validate_automation( 41 | single=True 42 | ), 43 | cv.Optional(CONF_TURN_ON_ACTION): automation.validate_automation( 44 | single=True 45 | ), 46 | cv.Optional(CONF_RESTORE_STATE): cv.invalid( 47 | "The restore_state option has been removed in 2023.7.0. Use the restore_mode option instead" 48 | ), 49 | } 50 | ) 51 | .extend(cv.COMPONENT_SCHEMA), 52 | validate, 53 | ) 54 | 55 | 56 | async def to_code(config): 57 | var = await switch.new_switch(config) 58 | await cg.register_component(var, config) 59 | 60 | if CONF_LAMBDA in config: 61 | template_ = await cg.process_lambda( 62 | config[CONF_LAMBDA], [], return_type=cg.optional.template(bool) 63 | ) 64 | cg.add(var.set_state_lambda(template_)) 65 | if CONF_TURN_OFF_ACTION in config: 66 | await automation.build_automation( 67 | var.get_turn_off_trigger(), [], config[CONF_TURN_OFF_ACTION] 68 | ) 69 | if CONF_TURN_ON_ACTION in config: 70 | await automation.build_automation( 71 | var.get_turn_on_trigger(), [], config[CONF_TURN_ON_ACTION] 72 | ) 73 | cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) 74 | cg.add(var.set_assumed_state(config[CONF_ASSUMED_STATE])) 75 | 76 | 77 | @automation.register_action( 78 | "switch.lora_sx126x.publish", 79 | switch.SwitchPublishAction, 80 | cv.Schema( 81 | { 82 | cv.Required(CONF_ID): cv.use_id(switch.Switch), 83 | cv.Required(CONF_STATE): cv.templatable(cv.boolean), 84 | } 85 | ), 86 | ) 87 | async def switch_lora_sx126x_publish_to_code(config, action_id, template_arg, args): 88 | paren = await cg.get_variable(config[CONF_ID]) 89 | var = cg.new_Pvariable(action_id, template_arg, paren) 90 | template_ = await cg.templatable(config[CONF_STATE], args, bool) 91 | cg.add(var.set_state(template_)) 92 | return var 93 | -------------------------------------------------------------------------------- /examples/lora-sx126x.yaml: -------------------------------------------------------------------------------- 1 | # This file is an example with a setup for using the SX126X LoRa radio chip. 2 | 3 | esphome: 4 | name: "lora-sx126x" 5 | libraries: 6 | - "SPI" 7 | - "Ticker" 8 | - "SX126x-Arduino" 9 | 10 | esp32: 11 | board: esp32-s3-devkitc-1 12 | framework: 13 | type: arduino 14 | 15 | external_components: 16 | - source: 17 | type: local 18 | path: esphome/components 19 | components: ["lora_sx126x"] 20 | 21 | # Enable logging 22 | logger: 23 | 24 | wifi: 25 | networks: 26 | - ssid: !secret wifi_ssid 27 | password: !secret wifi_password 28 | 29 | ota: 30 | platform: esphome 31 | password: !secret ota_password 32 | 33 | # Enable Home Assistant API 34 | api: 35 | encryption: 36 | key: !secret api_encryption_key 37 | 38 | # 39 | web_server: 40 | port: 80 41 | 42 | #spi: 43 | # clk_pin: GPIO9 44 | # mosi_pin: GPIO10 45 | # miso_pin: GPIO11 46 | 47 | #i2c: 48 | # - id: bus_a 49 | # sda: 17 50 | # scl: 18 51 | 52 | lora_sx126x: 53 | name: "LoRa Radio" 54 | 55 | # # Interface to radio chip 56 | # pin_lora_reset: 12 # LoRa Reset 57 | # pin-lora_dio_1: 14 # LoRa DIO_1 58 | # pin_lora_busy: 13 # LoRa SPI Busy 59 | # pin_lora_nss: 8 # LoRa SPI CS (Chip Select) 60 | # pin_lora_sclk: 9 # LoRa SPI SCLK 61 | # pin_lora_miso: 11 # LoRa SPI MISO (Master In, Slave Out) 62 | # pin_lora_mosi: 10 # LoRa SPI MOSI (Master Out, Slave In) 63 | # radio_txen: -1 # LoRa Antenna TX Enable, on some boards. 64 | # radio_rxen: -1 # LoRa Antenna RX Enable, on some boards. 65 | 66 | # LoRa Options 67 | # Setting these will enable the LoRa radio mode. 68 | rf_frequency: 915000000 # Hz - Manditory 69 | # tx_output_power: 22 # dBm 70 | # lora_bandwidth: 0 # [0: 1 25 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] 71 | # lora_spreading_factor: 7 # [SF7..SF12] 72 | # lora_codingrate: 1 # [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] 73 | # lora_preamble_length: 8 # Same for Tx and Rx 74 | # lora_symbol_timeout: 0 # Symbols 75 | # lora_fx_length_payload_on: -1 # Default: -1 (False) 76 | # lora_iq_inversion_on: -1 # Default: -1 (False) 77 | # rx_timeout_value: 3000 # ms 78 | # tx_timeout_value: 3000 # ms 79 | 80 | sensor: 81 | - platform: lora_sx126x 82 | id: lorarssi 83 | name: lorarssi # Required for publishing into API and HA 84 | 85 | text_sensor: 86 | - platform: wifi_info 87 | ip_address: 88 | name: ESP IP Address 89 | - platform: version 90 | name: "ESPHome Version" 91 | - platform: template 92 | name: "Template Text Sensor" 93 | lambda: |- 94 | return {"Hello World"}; 95 | update_interval: 60s 96 | 97 | - platform: lora_sx126x 98 | id: packet_in 99 | name: packet_in 100 | 101 | binary_sensor: 102 | - platform: gpio 103 | pin: 5 104 | id: trigger 105 | name: "Trigger (In)" 106 | on_press: 107 | then: 108 | - switch.turn_on: monitor_switch 109 | on_release: 110 | then: 111 | - switch.turn_off: monitor_switch 112 | 113 | output: 114 | - platform: gpio 115 | pin: 6 116 | id: monitor 117 | - platform: gpio 118 | pin: 35 119 | id: monitor_led 120 | 121 | switch: 122 | - platform: output 123 | id: monitor_switch 124 | name: "Monitor Output (Out)" 125 | output: 126 | # monitor 127 | monitor_led 128 | 129 | 130 | -------------------------------------------------------------------------------- /examples/lora-sx126x-switch.yaml: -------------------------------------------------------------------------------- 1 | # This file is an example with a setup for using the SX126X LoRa radio chip. 2 | 3 | esphome: 4 | name: "lora-sx126x" 5 | libraries: 6 | - "SPI" 7 | - "Ticker" 8 | - "SX126x-Arduino" 9 | 10 | esp32: 11 | board: esp32-s3-devkitc-1 12 | framework: 13 | type: arduino 14 | 15 | external_components: 16 | - source: 17 | type: local 18 | path: esphome/components 19 | components: ["lora_sx126x"] 20 | 21 | # Enable logging 22 | logger: 23 | 24 | wifi: 25 | networks: 26 | - ssid: !secret wifi_ssid 27 | password: !secret wifi_password 28 | 29 | ota: 30 | platform: esphome 31 | password: !secret ota_password 32 | 33 | # Enable Home Assistant API 34 | api: 35 | encryption: 36 | key: !secret api_encryption_key 37 | 38 | # 39 | web_server: 40 | port: 80 41 | 42 | #spi: 43 | # clk_pin: GPIO9 44 | # mosi_pin: GPIO10 45 | # miso_pin: GPIO11 46 | 47 | #i2c: 48 | # - id: bus_a 49 | # sda: 17 50 | # scl: 18 51 | 52 | lora_sx126x: 53 | name: "LoRa Radio" 54 | 55 | # # Interface to radio chip 56 | # pin_lora_reset: 12 # LoRa Reset 57 | # pin-lora_dio_1: 14 # LoRa DIO_1 58 | # pin_lora_busy: 13 # LoRa SPI Busy 59 | # pin_lora_nss: 8 # LoRa SPI CS (Chip Select) 60 | # pin_lora_sclk: 9 # LoRa SPI SCLK 61 | # pin_lora_miso: 11 # LoRa SPI MISO (Master In, Slave Out) 62 | # pin_lora_mosi: 10 # LoRa SPI MOSI (Master Out, Slave In) 63 | # radio_txen: -1 # LoRa Antenna TX Enable, on some boards. 64 | # radio_rxen: -1 # LoRa Antenna RX Enable, on some boards. 65 | 66 | # LoRa Options 67 | # Setting these will enable the LoRa radio mode. 68 | rf_frequency: 915000000 # Hz - Manditory 69 | # tx_output_power: 22 # dBm 70 | # lora_bandwidth: 0 # [0: 1 25 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] 71 | # lora_spreading_factor: 7 # [SF7..SF12] 72 | # lora_codingrate: 1 # [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] 73 | # lora_preamble_length: 8 # Same for Tx and Rx 74 | # lora_symbol_timeout: 0 # Symbols 75 | # lora_fx_length_payload_on: -1 # Default: -1 (False) 76 | # lora_iq_inversion_on: -1 # Default: -1 (False) 77 | # rx_timeout_value: 3000 # ms 78 | # tx_timeout_value: 3000 # ms 79 | 80 | sensor: 81 | - platform: lora_sx126x 82 | id: lorarssi 83 | name: lorarssi # Required for publishing into API and HA 84 | 85 | text_sensor: 86 | - platform: wifi_info 87 | ip_address: 88 | name: ESP IP Address 89 | - platform: version 90 | name: "ESPHome Version" 91 | - platform: template 92 | name: "Template Text Sensor" 93 | lambda: |- 94 | return {"Hello World"}; 95 | update_interval: 60s 96 | 97 | - platform: lora_sx126x 98 | id: packet_in 99 | name: packet_in 100 | 101 | binary_sensor: 102 | - platform: gpio 103 | pin: 5 104 | id: trigger 105 | name: "Trigger (In)" 106 | on_press: 107 | then: 108 | - switch.turn_on: monitor_switch 109 | on_release: 110 | then: 111 | - switch.turn_off: monitor_switch 112 | 113 | output: 114 | - platform: gpio 115 | pin: 6 116 | id: monitor 117 | - platform: gpio 118 | pin: 35 119 | id: monitor_led 120 | 121 | switch: 122 | - platform: output 123 | id: monitor_switch 124 | name: "Monitor Output (Out)" 125 | output: 126 | monitor_led 127 | 128 | - platform: lora_sx126x 129 | name: "LoRa Switch" 130 | on_msg: "@^&^" 131 | off_msg: "@&^&" 132 | # optimistic: true 133 | turn_on_action: 134 | - logger.log: "Switch Turned On!" 135 | turn_off_action: 136 | - logger.log: "Switch Turned Off!" 137 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | .esphome/ 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # poetry 99 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 100 | # This is especially recommended for binary packages to ensure reproducibility, and is more 101 | # commonly ignored for libraries. 102 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 103 | #poetry.lock 104 | 105 | # pdm 106 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 107 | #pdm.lock 108 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 109 | # in version control. 110 | # https://pdm.fming.dev/#use-with-ide 111 | .pdm.toml 112 | 113 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 114 | __pypackages__/ 115 | 116 | # Celery stuff 117 | celerybeat-schedule 118 | celerybeat.pid 119 | 120 | # SageMath parsed files 121 | *.sage.py 122 | 123 | # Environments 124 | .env 125 | .venv 126 | env/ 127 | venv/ 128 | ENV/ 129 | env.bak/ 130 | venv.bak/ 131 | 132 | # Spyder project settings 133 | .spyderproject 134 | .spyproject 135 | 136 | # Rope project settings 137 | .ropeproject 138 | 139 | # mkdocs documentation 140 | /site 141 | 142 | # mypy 143 | .mypy_cache/ 144 | .dmypy.json 145 | dmypy.json 146 | 147 | # Pyre type checker 148 | .pyre/ 149 | 150 | # pytype static type analyzer 151 | .pytype/ 152 | 153 | # Cython debug symbols 154 | cython_debug/ 155 | 156 | # PyCharm 157 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 158 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 159 | # and can be added to the global gitignore or merged into this file. For a more nuclear 160 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 161 | #.idea/ 162 | -------------------------------------------------------------------------------- /esphome/components/lorawan_sx126x/lorawan_sx126x.h: -------------------------------------------------------------------------------- 1 | // -*- c++ -*- 2 | #pragma once 3 | 4 | #include "esphome/core/component.h" 5 | #include "esphome/components/sensor/sensor.h" 6 | #include 7 | // #include "esphome/componets/text_sensor/text_sensor.h" 8 | // #include "esphome/components/spi/spi.h" 9 | 10 | namespace esphome { 11 | namespace lorawan_sx126x { 12 | 13 | class LoRaWANSX126X : public sensor::Sensor, public Component { 14 | public: 15 | void setup() override; 16 | void loop() override; 17 | void dump_config() override; 18 | 19 | // Radio settings 20 | void set_pin_lora_reset (int16_t pin_lora_reset) { this->pin_lora_reset_ = pin_lora_reset; } 21 | void set_pin_lora_dio_1 (int16_t pin_lora_dio_1) { this->pin_lora_dio_1_ = pin_lora_dio_1; } 22 | void set_pin_lora_busy (int16_t pin_lora_busy) { this->pin_lora_busy_ = pin_lora_busy; } 23 | void set_pin_lora_nss (int16_t pin_lora_nss) { this->pin_lora_nss_ = pin_lora_nss; } 24 | void set_pin_lora_sclk (int16_t pin_lora_sclk) { this->pin_lora_sclk_ = pin_lora_sclk; } 25 | void set_pin_lora_miso (int16_t pin_lora_miso) { this->pin_lora_miso_ = pin_lora_miso; } 26 | void set_pin_lora_mosi (int16_t pin_lora_mosi) { this->pin_lora_mosi_ = pin_lora_mosi; } 27 | void set_radio_txen (int16_t radio_txen) { this->radio_txen_ = radio_txen; } 28 | void set_radio_rxen (int16_t radio_rxen) { this->radio_rxen_ = radio_rxen; } 29 | 30 | // void set_device_id(uint8_t *device_id); 31 | void set_tx_output_power (uint8_t tx_output_power) { this->tx_output_power_ = tx_output_power; } 32 | int16_t get_rx_timeout_value (void) { return this->rx_timeout_value_; } 33 | int16_t get_tx_timeout_value (void) { return this->tx_timeout_value_; } 34 | 35 | // TODO 36 | void set_lorawan_region (std::string lorawan_region) { 37 | this->lorawan_region_ = lorawan_region; } 38 | void set_lorawan_authtype (std::string lorawan_authtype) { 39 | this->lorawan_authtype_ = lorawan_authtype; } 40 | void set_lorawan_device_eui (std::string lorawan_device_id) { 41 | this->lorawan_device_eui_ = lorawan_device_id; } 42 | void set_lorawan_app_eui (std::string lorawan_app_eui) { 43 | this->lorawan_app_eui = lorawan_app_eui; } 44 | void set_lorawan_app_key (std:string lorawan_app_key) { 45 | this->lorawan_app_key = lorawan_app_key; } 46 | 47 | void packets_rx_zero(void) { this->lora_packets_rx_ = 0; } 48 | void packets_rx_incrument(void) { this->lora_packets_rx_++; } 49 | uint16_t packets_rx(void) { return lora_packets_rx_; } 50 | 51 | void packets_tx_zero(void) { this->lora_packets_tx_ = 0; } 52 | void packets_tx_incrument(void) { this->lora_packets_tx_++; } 53 | uint16_t packets_tx(void) { return lora_packets_tx_; } 54 | 55 | protected: 56 | // Radio Configuration 57 | int8_t pin_lora_reset_; 58 | int8_t pin_lora_dio_1_; 59 | int8_t pin_lora_busy_; 60 | int8_t pin_lora_nss_; 61 | int8_t pin_lora_sclk_; 62 | int8_t pin_lora_miso_; 63 | int8_t pin_lora_mosi_; 64 | int8_t radio_txen_; 65 | int8_t radio_rxen_; 66 | 67 | // LoRaWAN configuration 68 | std::string lorawan_region_; 69 | std::string lorawan_authtype_; 70 | std::string lorawan_device_eui_; 71 | std::string lorawan_app_eui_; 72 | std::string lorawan_app_key_; 73 | 74 | // Statistics 75 | uint16_t lora_packets_rx_; 76 | uint16_t lora_packets_tx_; 77 | 78 | }; // class LoraSX126X 79 | 80 | class LoRaWANSX126Xrssi : public sensor::Sensor, public Component { 81 | public: 82 | void setup() override; 83 | void publsh (float_t rssi) { this->publish_state(rssi); } 84 | }; // class LoraSX126Xrssi 85 | 86 | class LoRaWANSX126Xpkt : public text_sensor::TextSensor, public Component { 87 | public: 88 | void setup() override; 89 | void publish (char * val) { this->publish_state(val); } 90 | }; // class LoraSX126Xpkt 91 | 92 | 93 | } // namespace lora_sx126x 94 | } // namespace esphome 95 | -------------------------------------------------------------------------------- /esphome/components/lora_sx126x/__init__.py: -------------------------------------------------------------------------------- 1 | import esphome.codegen as cg 2 | import esphome.config_validation as cv 3 | from esphome.const import CONF_ID 4 | 5 | CODEOWNERS = ["@PaulSchulz"] 6 | AUTO_LOAD = [ "sensor","text_sensor"] 7 | DEPENDANCIES = ["spi"] 8 | 9 | lora_sx126x_ns = cg.esphome_ns.namespace("lora_sx126x") 10 | 11 | # empty_component_ns = cg.esphome_ns.namespace('empty_component') 12 | LoraSX126X = lora_sx126x_ns.class_('LoraSX126X', cg.Component) 13 | 14 | # Hardware 15 | # Heltec Wifi LoRa 32 (V3) - SX126x pin configuration 16 | PIN_LORA_RESET = 12 # LORA RESET 17 | PIN_LORA_DIO_1 = 14 # LORA DIO_1 18 | PIN_LORA_BUSY = 13 # LORA SPI BUSY 19 | PIN_LORA_NSS = 8 # LORA SPI CS 20 | PIN_LORA_SCLK = 9 # LORA SPI CLK 21 | PIN_LORA_MISO = 11 # LORA SPI MISO 22 | PIN_LORA_MOSI = 10 # LORA SPI MOSI 23 | RADIO_TXEN = -1 # LORA ANTENNA TX ENABLE 24 | RADIO_RXEN = -1 # LORA ANTENNA RX ENABLE 25 | 26 | # LoRa Radio Parameters 27 | CONF_RF_FREQUENCY = 915000000 # Hz 28 | CONF_TX_OUTPUT_POWER = 22 # dBm 29 | CONF_LORA_BANDWIDTH = 0 # [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] 30 | CONF_LORA_SPREADING_FACTOR = 7 # [SF7..SF12] 31 | CONF_LORA_CODINGRATE = 1 # [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] 32 | CONF_LORA_PREAMBLE_LENGTH = 8 # Same for Tx and Rx 33 | CONF_LORA_SYMBOL_TIMEOUT = 0 # Symbols 34 | CONF_LORA_FIX_LENGTH_PAYLOAD_ON = 0 # Default: False (0) 35 | CONF_LORA_IQ_INVERSION_ON = 0 # Default: False (0) 36 | CONF_RX_TIMEOUT_VALUE = 3000 37 | CONF_TX_TIMEOUT_VALUE = 3000 38 | 39 | CONFIG_SCHEMA = cv.Schema({ 40 | cv.GenerateID(): cv.declare_id(LoraSX126X), 41 | cv.Optional('name'): cv.string, 42 | 43 | cv.Optional('pin_lora_reset', default=PIN_LORA_RESET): cv.int_, 44 | cv.Optional('pin_lora_dio_1', default=PIN_LORA_DIO_1): cv.int_, 45 | cv.Optional('pin_lora_busy', default=PIN_LORA_BUSY): cv.int_, 46 | cv.Optional('pin_lora_nss', default=PIN_LORA_NSS): cv.int_, 47 | cv.Optional('pin_lora_sclk', default=PIN_LORA_SCLK): cv.int_, 48 | cv.Optional('pin_lora_miso', default=PIN_LORA_MISO): cv.int_, 49 | cv.Optional('pin_lora_mosi', default=PIN_LORA_MOSI): cv.int_, 50 | cv.Optional('radio_txen', default=RADIO_TXEN): cv.int_, 51 | cv.Optional('radio_rxen', default=RADIO_RXEN): cv.int_, 52 | 53 | cv.Optional('rf_frequency', default=CONF_RF_FREQUENCY): cv.int_, 54 | 55 | cv.Optional('tx_output_power', default=CONF_TX_OUTPUT_POWER): cv.int_, 56 | cv.Optional('lora_bandwidth', default=CONF_LORA_BANDWIDTH): cv.int_, 57 | cv.Optional('lora_spreading_factor', default=CONF_LORA_SPREADING_FACTOR): cv.int_, 58 | cv.Optional('lora_codingrate', default=CONF_LORA_CODINGRATE): cv.int_, 59 | cv.Optional('lora_preamble_length', default=CONF_LORA_PREAMBLE_LENGTH): cv.int_, 60 | cv.Optional('lora_symbol_timeout', default=CONF_LORA_SYMBOL_TIMEOUT): cv.int_, 61 | cv.Optional('lora_fix_length_payload_on', default=CONF_LORA_FIX_LENGTH_PAYLOAD_ON): cv.int_, 62 | cv.Optional('lora_iq_inversion_on', default=CONF_LORA_IQ_INVERSION_ON): cv.int_, 63 | cv.Optional('rx_timeout_value', default=CONF_RX_TIMEOUT_VALUE): cv.int_, 64 | cv.Optional('tx_timeout_value', default=CONF_TX_TIMEOUT_VALUE): cv.int_, 65 | 66 | }).extend(cv.COMPONENT_SCHEMA) 67 | 68 | def to_code(config): 69 | var = cg.new_Pvariable(config[CONF_ID]) 70 | yield cg.register_component(var, config) 71 | 72 | cg.add(var.set_pin_lora_reset(config['pin_lora_reset'])) 73 | cg.add(var.set_pin_lora_dio_1(config['pin_lora_dio_1'])) 74 | cg.add(var.set_pin_lora_busy(config['pin_lora_busy'])) 75 | cg.add(var.set_pin_lora_nss(config['pin_lora_nss'])) 76 | cg.add(var.set_pin_lora_sclk(config['pin_lora_sclk'])) 77 | cg.add(var.set_pin_lora_miso(config['pin_lora_miso'])) 78 | cg.add(var.set_pin_lora_mosi(config['pin_lora_mosi'])) 79 | cg.add(var.set_radio_txen(config['radio_txen'])) 80 | cg.add(var.set_radio_rxen(config['radio_rxen'])) 81 | 82 | cg.add(var.set_rf_frequency(config['rf_frequency'])) 83 | 84 | cg.add(var.set_tx_output_power(config['tx_output_power'])) 85 | cg.add(var.set_lora_bandwidth(config['lora_bandwidth'])) 86 | cg.add(var.set_lora_spreading_factor(config['lora_spreading_factor'])) 87 | cg.add(var.set_lora_codingrate(config['lora_codingrate'])) 88 | cg.add(var.set_lora_preamble_length(config['lora_preamble_length'])) 89 | cg.add(var.set_lora_symbol_timeout(config['lora_symbol_timeout'])) 90 | cg.add(var.set_lora_fix_length_payload_on(config['lora_fix_length_payload_on'])) 91 | cg.add(var.set_lora_iq_inversion_on(config['lora_iq_inversion_on'])) 92 | cg.add(var.set_rx_timeout_value(config['rx_timeout_value'])) 93 | cg.add(var.set_tx_timeout_value(config['tx_timeout_value'])) 94 | -------------------------------------------------------------------------------- /esphome/components/lora_sx126x/lora_sx126x.h: -------------------------------------------------------------------------------- 1 | // -*- c++ -*- 2 | #pragma once 3 | 4 | #include "esphome/core/component.h" 5 | #include "esphome/components/sensor/sensor.h" 6 | #include 7 | // #include "esphome/componets/text_sensor/text_sensor.h" 8 | // #include "esphome/components/spi/spi.h" 9 | 10 | namespace esphome { 11 | namespace lora_sx126x { 12 | 13 | class LoraSX126X : public sensor::Sensor, public Component { 14 | public: 15 | void setup() override; 16 | void loop() override; 17 | void dump_config() override; 18 | 19 | // void set_device_id(uint8_t *device_id); 20 | void set_rf_frequency (uint32_t rf_frequency) { this->rf_frequency_ = rf_frequency; } 21 | void set_tx_output_power (uint8_t tx_output_power) { this->tx_output_power_ = tx_output_power; } 22 | void set_lora_bandwidth (uint8_t lora_bandwidth) { this->lora_bandwidth_ = lora_bandwidth; } 23 | void set_lora_spreading_factor (uint8_t lora_spreading_factor) { 24 | this->lora_spreading_factor_ = lora_spreading_factor; } 25 | void set_lora_codingrate (int8_t lora_codingrate) { this->lora_codingrate_ = lora_codingrate; } 26 | void set_lora_preamble_length (uint8_t lora_preamble_length) { 27 | this->lora_preamble_length_ = lora_preamble_length; } 28 | void set_lora_symbol_timeout (int8_t lora_symbol_timeout) { 29 | this->lora_symbol_timeout_ = lora_symbol_timeout; } 30 | void set_lora_fix_length_payload_on (int8_t lora_fix_length_payload_on) { 31 | this->lora_fix_length_payload_on_ = lora_fix_length_payload_on; } 32 | void set_lora_iq_inversion_on (int8_t lora_iq_inversion_on) { 33 | this->lora_iq_inversion_on_ = lora_iq_inversion_on; } 34 | void set_rx_timeout_value (int16_t rx_timeout_value) { 35 | this->rx_timeout_value_ = rx_timeout_value; } 36 | void set_tx_timeout_value (int16_t tx_timeout_value) { 37 | this->tx_timeout_value_ = tx_timeout_value; } 38 | 39 | int16_t get_rx_timeout_value (void) { return this->rx_timeout_value_; } 40 | int16_t get_tx_timeout_value (void) { return this->tx_timeout_value_; } 41 | 42 | void set_pin_lora_reset (int16_t pin_lora_reset) { this->pin_lora_reset_ = pin_lora_reset; } 43 | void set_pin_lora_dio_1 (int16_t pin_lora_dio_1) { this->pin_lora_dio_1_ = pin_lora_dio_1; } 44 | void set_pin_lora_busy (int16_t pin_lora_busy) { this->pin_lora_busy_ = pin_lora_busy; } 45 | void set_pin_lora_nss (int16_t pin_lora_nss) { this->pin_lora_nss_ = pin_lora_nss; } 46 | void set_pin_lora_sclk (int16_t pin_lora_sclk) { this->pin_lora_sclk_ = pin_lora_sclk; } 47 | void set_pin_lora_miso (int16_t pin_lora_miso) { this->pin_lora_miso_ = pin_lora_miso; } 48 | void set_pin_lora_mosi (int16_t pin_lora_mosi) { this->pin_lora_mosi_ = pin_lora_mosi; } 49 | void set_radio_txen (int16_t radio_txen) { this->radio_txen_ = radio_txen; } 50 | void set_radio_rxen (int16_t radio_rxen) { this->radio_rxen_ = radio_rxen; } 51 | 52 | void packets_rx_zero(void) { this->lora_packets_rx_ = 0; } 53 | void packets_rx_incrument(void) { this->lora_packets_rx_++; } 54 | uint16_t packets_rx(void) { return lora_packets_rx_; } 55 | 56 | void packets_tx_zero(void) { this->lora_packets_tx_ = 0; } 57 | void packets_tx_incrument(void) { this->lora_packets_tx_++; } 58 | uint16_t packets_tx(void) { return lora_packets_tx_; } 59 | 60 | protected: 61 | int8_t pin_lora_reset_; 62 | int8_t pin_lora_dio_1_; 63 | int8_t pin_lora_busy_; 64 | int8_t pin_lora_nss_; 65 | int8_t pin_lora_sclk_; 66 | int8_t pin_lora_miso_; 67 | int8_t pin_lora_mosi_; 68 | int8_t radio_txen_; 69 | int8_t radio_rxen_; 70 | 71 | // lora configuration 72 | uint32_t rf_frequency_; 73 | uint8_t tx_output_power_; 74 | uint8_t lora_bandwidth_; 75 | uint8_t lora_spreading_factor_; 76 | uint8_t lora_codingrate_; 77 | uint8_t lora_preamble_length_; 78 | uint8_t lora_symbol_timeout_; 79 | int8_t lora_fix_length_payload_on_; 80 | uint8_t lora_iq_inversion_on_; 81 | uint16_t rx_timeout_value_; 82 | uint16_t tx_timeout_value_; 83 | 84 | uint8_t deviceId[8]; 85 | 86 | uint16_t lora_packets_rx_; 87 | uint16_t lora_packets_tx_; 88 | 89 | }; // class LoraSX126X 90 | 91 | class LoraSX126Xrssi : public sensor::Sensor, public Component { 92 | public: 93 | void setup() override; 94 | void publsh (float_t rssi) { this->publish_state(rssi); } 95 | }; // class LoraSX126Xrssi 96 | 97 | class LoraSX126Xpkt : public text_sensor::TextSensor, public Component { 98 | public: 99 | void setup() override; 100 | void publish (char * val) { this->publish_state(val); } 101 | }; // class LoraSX126Xpkt 102 | 103 | 104 | } // namespace lora_sx126x 105 | } // namespace esphome 106 | -------------------------------------------------------------------------------- /esphome/components/lorawan_sx126x/lorawan_sx126x.cpp: -------------------------------------------------------------------------------- 1 | #include "esphome.h" 2 | 3 | #include "lorawan_sx126x.h" 4 | 5 | #include 6 | #include 7 | 8 | // Tag for log output. 9 | // The following is required to use log macros outside of the 'esphome' namespace. 10 | // See: https://github.com/esphome/issues/issues/4751 11 | using esphome::esp_log_printf_; 12 | static const char *TAG = "lorawan_sx126x"; 13 | 14 | #define BUFFER_SIZE 64 // Define the payload size here 15 | 16 | namespace esphome { 17 | namespace lorawan_sx126x { 18 | 19 | hw_config hwConfig; 20 | static RadioEvents_t RadioEvents; 21 | char rxpacket[BUFFER_SIZE]; 22 | 23 | // Object references 24 | LoRaWANSX126X* radiolib; 25 | LoRaWANSX126Xrssi* radiolibrssi; 26 | LoRaWANSX126Xpkt* radiolibpkt; 27 | 28 | ////////////////////////////////////////////////////////////////////// 29 | // LoRa Radio Functions 30 | void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr) { 31 | memcpy(rxpacket, payload, size); 32 | rxpacket[size]='\0'; 33 | 34 | Radio.Sleep(); 35 | ESP_LOGD(TAG, "Received packet \"%s\" with rssi:%d length:%d",rxpacket,rssi,size); 36 | 37 | radiolib->packets_rx_incrument(); 38 | ESP_LOGD(TAG, "Packet count: %d",radiolib->packets_rx()); 39 | 40 | // Publish details to Sensor API 41 | radiolibrssi->publish_state(1.0 * rssi); 42 | radiolibpkt->publish_state(rxpacket); 43 | 44 | // Set Radio to receive next packet 45 | Radio.Rx(radiolib->get_rx_timeout_value()); 46 | } 47 | 48 | ////////////////////////////////////////////////////////////////////// 49 | // ESPHome Methods 50 | void LoRaWANSX126X::setup() { 51 | int result; 52 | // The following is required to access object from callbacks 53 | radiolib = this; 54 | 55 | ESP_LOGD(TAG, "LoRa SX126X Setup (SX1262)"); 56 | 57 | // Define the HW configuration between MCU and SX126x 58 | hwConfig.CHIP_TYPE = SX1262_CHIP; // Example uses an eByte E22 module with an SX1262 59 | hwConfig.PIN_LORA_RESET = pin_lora_reset_; // LORA RESET 60 | hwConfig.PIN_LORA_NSS = pin_lora_nss_; // LORA SPI CS 61 | hwConfig.PIN_LORA_SCLK = pin_lora_sclk_; // LORA SPI CLK 62 | hwConfig.PIN_LORA_MISO = pin_lora_miso_; // LORA SPI MISO 63 | hwConfig.PIN_LORA_DIO_1 = pin_lora_dio_1_; // LORA DIO_1 64 | hwConfig.PIN_LORA_BUSY = pin_lora_busy_; // LORA SPI BUSY 65 | hwConfig.PIN_LORA_MOSI = pin_lora_mosi_; // LORA SPI MOSI 66 | hwConfig.RADIO_TXEN = radio_txen_; // LORA ANTENNA TX ENABLE 67 | hwConfig.RADIO_RXEN = radio_rxen_; // LORA ANTENNA RX ENABLE 68 | hwConfig.USE_DIO2_ANT_SWITCH = true; 69 | hwConfig.USE_DIO3_TCXO = true; 70 | hwConfig.USE_DIO3_ANT_SWITCH = false; 71 | 72 | // uint8_t deviceId[8]; 73 | BoardGetUniqueId(deviceId); 74 | ESP_LOGD(TAG, "BoardId: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X", 75 | deviceId[0], 76 | deviceId[1], 77 | deviceId[2], 78 | deviceId[3], 79 | deviceId[4], 80 | deviceId[5], 81 | deviceId[6], 82 | deviceId[7]); 83 | 84 | // Radio Statistics 85 | this->packets_rx_zero(); 86 | this->packets_tx_zero(); 87 | 88 | // Initialize the LoRa chip 89 | ESP_LOGD(TAG, "Calling lora_hardware_init()"); 90 | uint32_t err_code = lora_hardware_init(hwConfig); 91 | if (err_code != 0) { 92 | ESP_LOGD(TAG, "ERROR: lora_hardware_init failed - %d\n", err_code); 93 | } 94 | 95 | // Setup LoRa Radio Mode 96 | ESP_LOGD(TAG, "Starting LoRa Radio Mode"); 97 | // Initialize the Radio callbacks 98 | RadioEvents.TxDone = NULL; // OnTxDone; 99 | RadioEvents.RxDone = OnRxDone; // OnRxDone; 100 | RadioEvents.TxTimeout = NULL; // OnTxTimeout; 101 | RadioEvents.RxTimeout = NULL; // OnRxTimeout; 102 | RadioEvents.RxError = NULL; // OnRxError; 103 | RadioEvents.CadDone = NULL; // OnCadDone; 104 | 105 | // Initialize the Radio 106 | Radio.Init(&RadioEvents); 107 | 108 | // Set Radio channel 109 | Radio.SetChannel(rf_frequency_); 110 | 111 | // Set Radio RX configuration 112 | ESP_LOGD(TAG, "Calling Radio.SetRxConfig()"); 113 | Radio.SetRxConfig(MODEM_LORA, lora_bandwidth_, lora_spreading_factor_, 114 | lora_codingrate_, lora_bandwidth_, lora_preamble_length_, 115 | lora_symbol_timeout_, lora_fix_length_payload_on_, 116 | 0, true, 0, 0, lora_iq_inversion_on_, true); 117 | 118 | // Start LoRa 119 | ESP_LOGD(TAG, "Calling Radio.Rx()"); 120 | Radio.Rx(rx_timeout_value_); 121 | } 122 | 123 | unsigned long previousMillis = 0; 124 | unsigned long interval = 10000UL; 125 | 126 | void LoRaWANSX126X::loop() { 127 | // This will be called very often after setup time. 128 | unsigned long currentMillis = millis(); 129 | if(currentMillis - previousMillis > interval) { 130 | previousMillis = currentMillis; 131 | ESP_LOGD(TAG, "Tick"); 132 | } 133 | 134 | } 135 | 136 | void LoRaWANSX126X::dump_config() { 137 | ESP_LOGCONFIG(TAG, "SX126X Config"); 138 | ESP_LOGCONFIG(TAG, " Pin LoRa Reset: %2d", pin_lora_reset_); 139 | ESP_LOGCONFIG(TAG, " Pin LoRa DIO 1: %2d", pin_lora_dio_1_); 140 | ESP_LOGCONFIG(TAG, " Pin LoRa Busy: %2d", pin_lora_busy_); 141 | ESP_LOGCONFIG(TAG, " Pin LoRa NSS: %2d", pin_lora_nss_); 142 | ESP_LOGCONFIG(TAG, " Pin LoRa SCLK: %2d", pin_lora_sclk_); 143 | ESP_LOGCONFIG(TAG, " Pin LoRa MISO: %2d", pin_lora_miso_); 144 | ESP_LOGCONFIG(TAG, " Pin LoRa MOSI: %2d", pin_lora_mosi_); 145 | ESP_LOGCONFIG(TAG, " Radio TXEN: %2d", radio_txen_); 146 | ESP_LOGCONFIG(TAG, " Radio RXEN: %2d", radio_rxen_); 147 | ESP_LOGCONFIG(TAG, ""); 148 | 149 | ESP_LOGCONFIG(TAG, "LoRaWAN Configuration"); 150 | ESP_LOGCONFIG(TAG, " Region: %s", lorawan_region_.c_str()); 151 | ESP_LOGCONFIG(TAG, " Authentiation Type: %s", lorawan_authtype_.c_str()); 152 | ESP_LOGCONFIG(TAG, " Device EUI: %s", lorawan_device_eui_.c_str()); 153 | ESP_LOGCONFIG(TAG, " Application EUI: %s", lorawan_app_eui_.c_str()); 154 | ESP_LOGCONFIG(TAG, " Application Key: %s", lorawan_app_key_.c_str()); 155 | ESP_LOGCONFIG(TAG, ""); 156 | } 157 | 158 | void LoRaWANSX126Xrssi::setup() { 159 | // The following is required to access object from callbacks 160 | radiolibrssi = this; 161 | } 162 | 163 | void LoRaWANSX126Xpkt::setup() { 164 | // The following is required to access object from callbacks 165 | radiolibpkt = this; 166 | } 167 | 168 | } // namespace lora_sx126x 169 | } // namespace esphome 170 | -------------------------------------------------------------------------------- /esphome/components/lora_sx126x/lora_sx126x.cpp: -------------------------------------------------------------------------------- 1 | #include "esphome.h" 2 | 3 | #include "lora_sx126x.h" 4 | 5 | #include 6 | #include 7 | 8 | // Tag for log output. 9 | // The following is required to use log macros outside of the 'esphome' namespace. 10 | // See: https://github.com/esphome/issues/issues/4751 11 | using esphome::esp_log_printf_; 12 | static const char *TAG = "lora_sx126x"; 13 | 14 | #define BUFFER_SIZE 64 // Define the payload size here 15 | 16 | namespace esphome { 17 | namespace lora_sx126x { 18 | 19 | hw_config hwConfig; 20 | static RadioEvents_t RadioEvents; 21 | char rxpacket[BUFFER_SIZE]; 22 | 23 | // Object references 24 | LoraSX126X* radiolib; 25 | LoraSX126Xrssi* radiolibrssi; 26 | LoraSX126Xpkt* radiolibpkt; 27 | 28 | ////////////////////////////////////////////////////////////////////// 29 | // LoRa Radio Functions 30 | void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr) { 31 | memcpy(rxpacket, payload, size); 32 | rxpacket[size]='\0'; 33 | 34 | Radio.Sleep(); 35 | ESP_LOGD(TAG, "Received packet \"%s\" with rssi:%d length:%d",rxpacket,rssi,size); 36 | 37 | radiolib->packets_rx_incrument(); 38 | ESP_LOGD(TAG, "Packet count: %d",radiolib->packets_rx()); 39 | 40 | // Publish details to Sensor API 41 | radiolibrssi->publish_state(1.0 * rssi); 42 | radiolibpkt->publish_state(rxpacket); 43 | 44 | // Set Radio to receive next packet 45 | Radio.Rx(radiolib->get_rx_timeout_value()); 46 | } 47 | 48 | ////////////////////////////////////////////////////////////////////// 49 | // ESPHome Methods 50 | void LoraSX126X::setup() { 51 | int result; 52 | // The following is required to access object from callbacks 53 | radiolib = this; 54 | 55 | ESP_LOGD(TAG, "LoRa SX126X Setup (SX1262)"); 56 | 57 | // Define the HW configuration between MCU and SX126x 58 | hwConfig.CHIP_TYPE = SX1262_CHIP; // Example uses an eByte E22 module with an SX1262 59 | hwConfig.PIN_LORA_RESET = pin_lora_reset_; // LORA RESET 60 | hwConfig.PIN_LORA_NSS = pin_lora_nss_; // LORA SPI CS 61 | hwConfig.PIN_LORA_SCLK = pin_lora_sclk_; // LORA SPI CLK 62 | hwConfig.PIN_LORA_MISO = pin_lora_miso_; // LORA SPI MISO 63 | hwConfig.PIN_LORA_DIO_1 = pin_lora_dio_1_; // LORA DIO_1 64 | hwConfig.PIN_LORA_BUSY = pin_lora_busy_; // LORA SPI BUSY 65 | hwConfig.PIN_LORA_MOSI = pin_lora_mosi_; // LORA SPI MOSI 66 | hwConfig.RADIO_TXEN = radio_txen_; // LORA ANTENNA TX ENABLE 67 | hwConfig.RADIO_RXEN = radio_rxen_; // LORA ANTENNA RX ENABLE 68 | hwConfig.USE_DIO2_ANT_SWITCH = true; 69 | hwConfig.USE_DIO3_TCXO = true; 70 | hwConfig.USE_DIO3_ANT_SWITCH = false; 71 | 72 | // uint8_t deviceId[8]; 73 | BoardGetUniqueId(deviceId); 74 | ESP_LOGD(TAG, "BoardId: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X", 75 | deviceId[0], 76 | deviceId[1], 77 | deviceId[2], 78 | deviceId[3], 79 | deviceId[4], 80 | deviceId[5], 81 | deviceId[6], 82 | deviceId[7]); 83 | 84 | // Radio Statistics 85 | this->packets_rx_zero(); 86 | this->packets_tx_zero(); 87 | 88 | // Initialize the LoRa chip 89 | ESP_LOGD(TAG, "Calling lora_hardware_init()"); 90 | uint32_t err_code = lora_hardware_init(hwConfig); 91 | if (err_code != 0) { 92 | ESP_LOGD(TAG, "ERROR: lora_hardware_init failed - %d\n", err_code); 93 | } 94 | 95 | // Setup LoRa Radio Mode 96 | ESP_LOGD(TAG, "Starting LoRa Radio Mode"); 97 | // Initialize the Radio callbacks 98 | RadioEvents.TxDone = NULL; // OnTxDone; 99 | RadioEvents.RxDone = OnRxDone; // OnRxDone; 100 | RadioEvents.TxTimeout = NULL; // OnTxTimeout; 101 | RadioEvents.RxTimeout = NULL; // OnRxTimeout; 102 | RadioEvents.RxError = NULL; // OnRxError; 103 | RadioEvents.CadDone = NULL; // OnCadDone; 104 | 105 | // Initialize the Radio 106 | Radio.Init(&RadioEvents); 107 | 108 | // Set Radio channel 109 | Radio.SetChannel(rf_frequency_); 110 | 111 | // Set Radio RX configuration 112 | ESP_LOGD(TAG, "Calling Radio.SetRxConfig()"); 113 | Radio.SetRxConfig(MODEM_LORA, lora_bandwidth_, lora_spreading_factor_, 114 | lora_codingrate_, lora_bandwidth_, lora_preamble_length_, 115 | lora_symbol_timeout_, lora_fix_length_payload_on_, 116 | 0, true, 0, 0, lora_iq_inversion_on_, true); 117 | 118 | // Start LoRa 119 | ESP_LOGD(TAG, "Calling Radio.Rx()"); 120 | Radio.Rx(rx_timeout_value_); 121 | } 122 | 123 | unsigned long previousMillis = 0; 124 | unsigned long interval = 10000UL; 125 | 126 | void LoraSX126X::loop() { 127 | // This will be called very often after setup time. 128 | } 129 | 130 | void LoraSX126X::dump_config() { 131 | ESP_LOGCONFIG(TAG, "SX126X Config"); 132 | ESP_LOGCONFIG(TAG, " Pin LoRa Reset: %2d", pin_lora_reset_); 133 | ESP_LOGCONFIG(TAG, " Pin LoRa DIO 1: %2d", pin_lora_dio_1_); 134 | ESP_LOGCONFIG(TAG, " Pin LoRa Busy: %2d", pin_lora_busy_); 135 | ESP_LOGCONFIG(TAG, " Pin LoRa NSS: %2d", pin_lora_nss_); 136 | ESP_LOGCONFIG(TAG, " Pin LoRa SCLK: %2d", pin_lora_sclk_); 137 | ESP_LOGCONFIG(TAG, " Pin LoRa MISO: %2d", pin_lora_miso_); 138 | ESP_LOGCONFIG(TAG, " Pin LoRa MOSI: %2d", pin_lora_mosi_); 139 | ESP_LOGCONFIG(TAG, " Radio TXEN: %2d", radio_txen_); 140 | ESP_LOGCONFIG(TAG, " Radio RXEN: %2d", radio_rxen_); 141 | ESP_LOGCONFIG(TAG, ""); 142 | 143 | ESP_LOGCONFIG(TAG, "LoRa Configuration"); 144 | ESP_LOGCONFIG(TAG, " Frequency: %9d Hz", rf_frequency_); 145 | ESP_LOGCONFIG(TAG, " Tx Output Power: %3d dBm", tx_output_power_); 146 | ESP_LOGCONFIG(TAG, " LoRa Bandwidth: %3d", lora_bandwidth_); 147 | ESP_LOGCONFIG(TAG, " LoRa Spreading Factor: %3d", lora_spreading_factor_); 148 | ESP_LOGCONFIG(TAG, " LoRa Codingrate: %3d", lora_codingrate_); 149 | ESP_LOGCONFIG(TAG, " LoRa Preable Length: %3d", lora_preamble_length_); 150 | ESP_LOGCONFIG(TAG, " LoRa Symbol Timeout: %3d", lora_symbol_timeout_); 151 | ESP_LOGCONFIG(TAG, " LoRa Fix Length Payload On: %d", lora_fix_length_payload_on_); 152 | ESP_LOGCONFIG(TAG, " LoRa IQ Inversion On: %d", lora_iq_inversion_on_); 153 | ESP_LOGCONFIG(TAG, " Rx Timeout Value: %5d ms", rx_timeout_value_); 154 | ESP_LOGCONFIG(TAG, " Tx Timeout Value: %5d ms", tx_timeout_value_); 155 | ESP_LOGCONFIG(TAG, ""); 156 | } 157 | 158 | void LoraSX126Xrssi::setup() { 159 | // The following is required to access object from callbacks 160 | radiolibrssi = this; 161 | } 162 | 163 | void LoraSX126Xpkt::setup() { 164 | // The following is required to access object from callbacks 165 | radiolibpkt = this; 166 | } 167 | 168 | } // namespace lora_sx126x 169 | } // namespace esphome 170 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: ESPHome Component for SX126X LoRa Radio (sx136x) 2 | 3 | * Contents :TOC: 4 | - [[#introduction][Introduction]] 5 | - [[#hardware][Hardware]] 6 | - [[#arduino-library][Arduino Library]] 7 | - [[#usage][Usage]] 8 | - [[#development-comments][Development Comments]] 9 | - [[#example-yaml][Example YAML]] 10 | - [[#development][Development]] 11 | - [[#proposed-yaml][Proposed YAML]] 12 | - [[#considerations--things-to-fix][Considerations / Things to fix]] 13 | - [[#testing][Testing]] 14 | 15 | * Introduction 16 | This repository is "Work in Progress". Comments are most welcome. 17 | 18 | The aim is to make the LoRa radio available, in the direct LoRa radio mode of 19 | operation, in ESPHome. The goal is to make the use of SX126X radio chip as easy 20 | and simple as possible, using a configuration in the ESPHome YAML files, with as 21 | many sensible defaults as possible. 22 | 23 | *NOTE:* A LoRaWAN configuration is not yet available, but would become available as a 24 | separate component. 25 | 26 | ** Hardware 27 | This component is being developed on the Heltec V3 Development Boards which use 28 | the LoRa SX1262 chip. Namely: 29 | 30 | - [[https://heltec.org/project/wifi-lora-32-v3/][Heltec Wifi LoRa 32 (V3)]] - heltec_wifi_kit_32_V3 (in expressif) 31 | - [[https://heltec.org/project/wireless-stick-v3/][Heltec Wireless Stick (V3)]] - heltec_wireless_stick_V3 32 | - [[https://heltec.org/product/wireless-stick-lite-v3/][Heltec Wireless Stick Lite (V3)]] - heltec_wireless_stick_lite_V3 33 | 34 | At the present, these boards are not listed to be selected in the ESPHome YAML 35 | file (when using the Arduino framework). In order to make these boards work with 36 | the available version of ESPHome, the following YAML is required: 37 | 38 | #+begin_src yaml 39 | esp32: 40 | board: esp32-s3-devkitc-1 41 | framework: 42 | type: arduino 43 | #+end_src 44 | 45 | The place where these boards are defined in the platform-espessif32 project is 46 | [[https://github.com/platformio/platform-espressif32/tree/develop/boards][here]]. It is hoped that these configurations will be picked up when a new version 47 | of espressif32 is pull into ESPHome. 48 | 49 | ** Arduino Library 50 | The Arduino library being used is [[https://github.com/beegee-tokyo/SX126x-Arduino][beegee-tokyo/SX126X-Arduino]]. 51 | 52 | The following header file contains the descriptions of the fields used by the 53 | library: 54 | - https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/radio.h 55 | 56 | ** Usage 57 | The files *lora-sx126x.yaml* and *secret.yaml* provide a example of how to use the 58 | integration. To use with a working version of ESPHome: 59 | 60 | - Edit *lora-sx126x.yaml* and ensure that the correct frequency (and band) is 61 | being used for your board. This varies from region to region and board to board. 62 | 63 | - Edit *secret.yaml* and add your local Wifi details, API and OTA secrets; 64 | 65 | then run: 66 | 67 | #+begin_src bash 68 | esphome run lora-sx126x.yaml 69 | #+end_src 70 | 71 | * Development Comments 72 | This esphome component currently does some things which are not encouraged by 73 | the ESPHome Core and Component developers. 74 | 75 | A this stage it does not look like it will be possible to be included in the main esphome 76 | project as it is making direct use of an SPI library to directly access this hardware. 77 | 78 | It is always hoped that this component will become compliant enough to be 79 | included in ESPHome, but in the meantime, it can be included in your ESPHome 80 | build by either cloning this repository locally and adding an external_component 81 | with a local source; or by including the github repository directly as an 82 | external_component. See the [[https://esphome.io/components/external_components.html][External Components]] documentation. 83 | 84 | Local source for external component: 85 | #+begin_src yaml 86 | external_components: 87 | - source: 88 | type: local 89 | path: esphome/components 90 | components: ["lora-sx126x"] 91 | #+end_src 92 | 93 | or, Github source for external component 94 | #+begin_src yaml 95 | external_components: 96 | - source: 97 | type: git 98 | url: https://github.com/PaulSchulz/esphome-lora-sx126x 99 | ref: main 100 | components: ["lora-sx126x"] 101 | #+end_src 102 | 103 | * Example YAML 104 | 105 | Proposed example YAML configuration 106 | #+begin_src yaml 107 | esphome: 108 | name: "lora-sx126x" 109 | libraries: 110 | - "SPI" 111 | - "Ticker" 112 | - "SX126x-Arduino" 113 | 114 | ... 115 | 116 | external_components: 117 | - source: 118 | type: local 119 | path: esphome/components 120 | components: [lora-sx126x] 121 | 122 | ... 123 | 124 | lora-sx126x: 125 | rf_frequency: 915000000 # Manditory for user to set 126 | 127 | sensor: 128 | - platform: lora-sx126x 129 | id: lorarssi 130 | name: lorarssi # Required for publishing into API and HA 131 | 132 | text_sensor: 133 | - platform: lora-sx126x 134 | id: packet_in 135 | name: packet_in 136 | 137 | #+end_src 138 | 139 | [[file:doc/images/webserver-screenshot.png]] 140 | 141 | * Development 142 | ** Proposed YAML 143 | 144 | #+begin_src yaml 145 | sx126x: 146 | # optional, with sensile defaults, if possible from board id. 147 | pin_lora_reset: 12 148 | pin_lora_dio_1: 14 149 | pin_lora_busy: 13 150 | pin_lora_nss: 8 151 | pin_lora_sclk: 9 152 | pin_lora_miso: 11 153 | pin_lora_mosi: 10 154 | radio_txen: -1 155 | radio_rxen: -1 156 | use_dio2_ant_switch: true 157 | use_dio3_tcx0: true 158 | use_dxo3_ant_switch: false 159 | 160 | # required - depends on region and frequency band being used 161 | rf_frequency: 915000000 162 | # optional (sensible defaults) 163 | tx_output_power: 22 164 | lora_bandwidth: 0 165 | lora_spreading_factor: 7 166 | lora_codingrate: 1 167 | lora_preamble_length: 8 168 | lora_symbol_timeout: 0 169 | lora_fix_length_layload_on: false 170 | lora_iq_inversion_on: false 171 | rx_timeout_value: 3000 172 | tx_timeout_value: 3000 173 | 174 | text_sensor: 175 | - platform: sx126x 176 | id: message 177 | name: LoRa Message 178 | 179 | # Is there a component for this in ESPHome? 180 | # Sending a string to a component. 181 | text_message: 182 | - platform: sx126x 183 | id: send_message 184 | name: Send LoRa Message 185 | 186 | binary_sensor: 187 | - platform: sx126x 188 | id: lora_sensor 189 | name: LoRa Sensor 190 | on_string: "@+++" 191 | off_string: "@---" 192 | 193 | switch: 194 | - platform: sx126x 195 | id: lora_switch 196 | name: LoRa Switch 197 | on_string: "@+++" 198 | off_string: "@---" 199 | #+end_src 200 | 201 | ** Considerations / Things to fix 202 | *** Direct use of SPI and SX126x-Arduino libraries 203 | If possible, the SX126x-Arduino library needs to be implemented natively in 204 | ESPHome, to make use of the native ESPHome SPI code. 205 | 206 | It is uncertain at the moment whether this component can be used generally with 207 | other devices that use the same SPI interface. 208 | 209 | *** Port to use RadioLib library 210 | The SX126x-Arduino library does not allow the 'syncword' to be set on the radio 211 | and has hardcoded values for a private (0x1424) or public (0x3444) radio setup. 212 | 213 | Meshtastic, another LoRa radio protocol, uses the RadioLib library, which 214 | supports a wider range of LoRa chips and allows the 'syncword' to be configured. 215 | They are currently using '0x2b' (or 0x24b4 to be precise). 216 | 217 | It would be good to be able to also interoperate with the Meshtastic radio 218 | network. 219 | an 220 | ** Testing 221 | The following environment is used: 222 | - On Ubuntu 24.10 223 | 224 | - Check out the repository 225 | #+begin_src shell 226 | git clone https://github.com/PaulSchulz/esphome-lora-sx126x.git 227 | cd esphome-lora-sx126x 228 | #+end_src 229 | 230 | - Checkout the latest esphome 231 | #+begin_src shell 232 | python -m venv venv 233 | . venv/bin/activate 234 | pip install esphome 235 | #+end_src 236 | 237 | - (Optional) Edit 'secrets.yaml' file with local credentials 238 | 239 | - Compile example 240 | #+begin_src shell 241 | esphome compile lora-sx126x.yaml 242 | #+end_src 243 | 244 | -------------------------------------------------------------------------------- /esphome/components/lorawan_sx126x/LoRaWAN.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #define SCHED_MAX_EVENT_DATA_SIZE APP_TIMER_SCHED_EVENT_DATA_SIZE /**< Maximum size of scheduler events. */ 7 | #define SCHED_QUEUE_SIZE 60 /**< Maximum number of events in the scheduler queue. */ 8 | 9 | #define LORAWAN_APP_DATA_BUFF_SIZE 64 /**< Size of the data to be transmitted. */ 10 | #define LORAWAN_APP_TX_DUTYCYCLE 10000 /**< Defines the application data transmission duty cycle. 10s, value in [ms]. */ 11 | #define APP_TX_DUTYCYCLE_RND 1000 /**< Defines a random delay for application data transmission duty cycle. 1s, value in [ms]. */ 12 | #define JOINREQ_NBTRIALS 3 /**< Number of trials for the join request. */ 13 | 14 | hw_config hwConfig; 15 | 16 | int PIN_LORA_RESET = 12; // LORA RESET 17 | int PIN_LORA_NSS = 8; // LORA SPI CS 18 | int PIN_LORA_SCLK = 9; // LORA SPI CLK 19 | int PIN_LORA_MISO = 11; // LORA SPI MISO 20 | int PIN_LORA_DIO_1 = 14; // LORA DIO_1 21 | int PIN_LORA_BUSY = 13; // LORA SPI BUSY 22 | int PIN_LORA_MOSI = 10; // LORA SPI MOSI 23 | int RADIO_TXEN = -1; // LORA ANTENNA TX ENABLE 24 | int RADIO_RXEN = -1; // LORA ANTENNA RX ENABLE 25 | 26 | // Foward declaration 27 | static void lorawan_has_joined_handler(void); 28 | static void lorawan_rx_handler(lmh_app_data_t *app_data); 29 | static void lorawan_confirm_class_handler(DeviceClass_t Class); 30 | static void lorawan_join_failed_handler(void); 31 | static void send_lora_frame(void); 32 | static uint32_t timers_init(void); 33 | 34 | // APP_TIMER_DEF(lora_tx_timer_id); ///< LoRa tranfer timer instance. 35 | TimerEvent_t appTimer; ///< LoRa tranfer timer instance. 36 | static uint8_t m_lora_app_data_buffer[LORAWAN_APP_DATA_BUFF_SIZE]; ///< Lora user application data buffer. 37 | static lmh_app_data_t m_lora_app_data = {m_lora_app_data_buffer, 0, 0, 0, 0}; ///< Lora user application data structure. 38 | 39 | /**@brief Structure containing LoRaWan parameters, needed for lmh_init() 40 | */ 41 | static lmh_param_t lora_param_init = {LORAWAN_ADR_ON, LORAWAN_DEFAULT_DATARATE, LORAWAN_PUBLIC_NETWORK, JOINREQ_NBTRIALS, LORAWAN_DEFAULT_TX_POWER, LORAWAN_DUTYCYCLE_OFF}; 42 | 43 | /**@brief Structure containing LoRaWan callback functions, needed for lmh_init() 44 | */ 45 | static lmh_callback_t lora_callbacks = { 46 | BoardGetBatteryLevel, 47 | BoardGetUniqueId, 48 | BoardGetRandomSeed, 49 | lorawan_rx_handler, 50 | lorawan_has_joined_handler, 51 | lorawan_confirm_class_handler, 52 | lorawan_join_failed_handler 53 | }; 54 | 55 | //uint8_t nodeDeviceEUI[8] = {0x70, 0xB3, 0xD5, 0x7E, 0xD0, 0x06, 0x34, 0x61}; 56 | //uint8_t nodeAppEUI[8] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; 57 | //uint8_t nodeAppKey[16] = {0xAE, 0xC5, 0xB2, 0x64, 0x39, 0x00, 0x69, 0xF8, 0x7A, 0x09, 0xDA, 0xC5, 0x73, 0x0F, 0x6B, 0xED}; 58 | // uint8_t nodeAppKey[16] = {0x3B, 0xB6, 0xF6, 0xC2, 0x3A, 0xDF, 0x99, 0xB8, 0x0E, 0xDA, 0x9F, 0xCE, 0xE2, 0x28, 0xFF, 0x1B}; 59 | // uint8_t nodeAppKey[16] = {0x1B, 0xFF, 0x28, 0xE2, 0xCE, 0x9F, 0xDA, 0x0E, 0xB8, 0x99, 0xDF, 0x3A, 0xC2, 0xF6, 0xB6, 0x3B}; 60 | 61 | //uint8_t nodeNwsKey[16] = {0xAE, 0xC5, 0xB2, 0x64, 0x39, 0x00, 0x69, 0xF8, 0x7A, 0x09, 0xDA, 0xC5, 0x73, 0x0F, 0x6B, 0xED}; 62 | //uint32_t nodeDevAddr = 0x260116F8; 63 | //uint8_t nodeAppsKey[16] = {0xFB, 0xAC, 0xB6, 0x47, 0xF3, 0x58, 0x45, 0xC7, 0x50, 0x7D, 0xBF, 0x16, 0x8B, 0xA8, 0xC1, 0x7C}; 64 | 65 | //Set2 66 | uint8_t nodeDeviceEUI[8] = {0x70, 0xB3, 0xD5, 0x7E, 0xD0, 0x06, 0x35, 0x4C}; 67 | uint8_t nodeAppEUI[8] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; 68 | uint8_t nodeAppKey[16] = {0x4A, 0xB7, 0xA2, 0xE4, 0xDC, 0xCC, 0xE2, 0x13, 0xDE, 0x71, 0xE5, 0xB2, 0x19, 0x83, 0x71, 0x69}; 69 | //uint8_t nodeNwsKey[16] = {0x38, 0x08, 0xF5, 0xB9, 0x23, 0x3F, 0xDE, 0x96, 0x5D, 0x79, 0x98, 0xD6, 0x63, 0x7F, 0x45, 0x66}; 70 | 71 | void setup() 72 | { 73 | pinMode(LED_BUILTIN, OUTPUT); 74 | digitalWrite(LED_BUILTIN, LOW); 75 | 76 | // Define the HW configuration between MCU and SX126x 77 | hwConfig.CHIP_TYPE = SX1262_CHIP; // Example uses an eByte E22 module with an SX1262 78 | hwConfig.PIN_LORA_RESET = PIN_LORA_RESET; // LORA RESET 79 | hwConfig.PIN_LORA_NSS = PIN_LORA_NSS; // LORA SPI CS 80 | hwConfig.PIN_LORA_SCLK = PIN_LORA_SCLK; // LORA SPI CLK 81 | hwConfig.PIN_LORA_MISO = PIN_LORA_MISO; // LORA SPI MISO 82 | hwConfig.PIN_LORA_DIO_1 = PIN_LORA_DIO_1; // LORA DIO_1 83 | hwConfig.PIN_LORA_BUSY = PIN_LORA_BUSY; // LORA SPI BUSY 84 | hwConfig.PIN_LORA_MOSI = PIN_LORA_MOSI; // LORA SPI MOSI 85 | hwConfig.RADIO_TXEN = RADIO_TXEN; // LORA ANTENNA TX ENABLE 86 | hwConfig.RADIO_RXEN = RADIO_RXEN; // LORA ANTENNA RX ENABLE 87 | hwConfig.USE_DIO2_ANT_SWITCH = true; // Example uses an CircuitRocks Alora RFM1262 which uses DIO2 pins as antenna control 88 | hwConfig.USE_DIO3_TCXO = true; // Example uses an CircuitRocks Alora RFM1262 which uses DIO3 to control oscillator voltage 89 | hwConfig.USE_DIO3_ANT_SWITCH = false; // Only Insight ISP4520 module uses DIO3 as antenna control 90 | 91 | // Initialize Serial for debug output 92 | Serial.begin(115200); 93 | 94 | Serial.println("====================================="); 95 | Serial.println("SX126x LoRaWAN"); 96 | Serial.println("====================================="); 97 | 98 | // Initialize Scheduler and timer 99 | Serial.println("timers_init()"); 100 | uint32_t err_code = timers_init(); 101 | if (err_code != 0) 102 | { 103 | Serial.printf("timers_init failed - %d\n", err_code); 104 | } 105 | 106 | // Initialize LoRa chip. 107 | Serial.println("lora_hardware_init()"); 108 | err_code = lora_hardware_init(hwConfig); 109 | if (err_code != 0) 110 | { 111 | Serial.printf("lora_hardware_init failed - %d\n", err_code); 112 | } 113 | 114 | // Setup the EUIs and Keys 115 | lmh_setDevEui(nodeDeviceEUI); 116 | lmh_setAppEui(nodeAppEUI); 117 | lmh_setAppKey(nodeAppKey); 118 | //lmh_setNwkSKey(nodeNwsKey); 119 | 120 | //lmh_setAppSKey(nodeAppsKey); 121 | //lmh_setDevAddr(nodeDevAddr); 122 | 123 | // Initialize LoRaWan 124 | Serial.println("lmh_init()"); 125 | 126 | Serial.print("nodeDeviceEUI: "); 127 | for(int i=0; i<8; i++) { 128 | Serial.printf("%02X ", nodeDeviceEUI[i]); 129 | } 130 | Serial.println(); 131 | 132 | Serial.print("nodeAppEUI: "); 133 | for(int i=0; i<8; i++) { 134 | Serial.printf("%02X ", nodeAppEUI[i]); 135 | } 136 | Serial.println(); 137 | 138 | Serial.print("nodeAppKey: "); 139 | for(int i=0; i<16; i++) { 140 | Serial.printf("%02X ", nodeAppKey[i]); 141 | } 142 | Serial.println(); 143 | 144 | err_code = lmh_init(&lora_callbacks, lora_param_init, true, CLASS_A, LORAMAC_REGION_AU915); 145 | if (err_code != 0) 146 | { 147 | Serial.printf("lmh_init failed - %d\n", err_code); 148 | } 149 | 150 | //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 151 | // Use either 152 | // lmh_setSingleChannelGateway 153 | // or 154 | // lmh_setSubBandChannels 155 | // 156 | // DO NOT USE BOTH OR YOUR COMMUNICATION WILL MOST LIKELY NEVER WORK 157 | //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 158 | // Setup connection to a single channel gateway 159 | // lmh_setSingleChannelGateway(0, DR_3); 160 | 161 | // For some regions we might need to define the sub band the gateway is listening to 162 | // This must be called AFTER lmh_init() 163 | /// \todo This is for Dragino LPS8 gateway. How about other gateways??? 164 | if (!lmh_setSubBandChannels(2)) 165 | { 166 | Serial.println("lmh_setSubBandChannels failed. Wrong sub band requested?"); 167 | } 168 | 169 | // Start Join procedure 170 | Serial.println("lmh_join()"); 171 | lmh_join(); 172 | } 173 | 174 | void loop() 175 | { 176 | // Handle Radio events 177 | Radio.IrqProcess(); 178 | 179 | // We are on FreeRTOS, give other tasks a chance to run 180 | // delay(100); 181 | } 182 | 183 | /**@brief LoRa function for handling OTAA join failed 184 | */ 185 | static void lorawan_join_failed_handler(void) 186 | { 187 | Serial.println("OVER_THE_AIR_ACTIVATION failed!"); 188 | Serial.println("Check your EUI's and Keys's!"); 189 | Serial.println("Check if a Gateway is in range!"); 190 | } 191 | 192 | /**@brief LoRa function for handling HasJoined event. 193 | */ 194 | static void lorawan_has_joined_handler(void) 195 | { 196 | #if (OVER_THE_AIR_ACTIVATION != 0) 197 | Serial.println("Network Joined"); 198 | #else 199 | Serial.println("OVER_THE_AIR_ACTIVATION != 0"); 200 | 201 | #endif 202 | lmh_class_request(CLASS_A); 203 | 204 | TimerSetValue(&appTimer, LORAWAN_APP_TX_DUTYCYCLE); 205 | TimerStart(&appTimer); 206 | // app_timer_start(lora_tx_timer_id, APP_TIMER_TICKS(LORAWAN_APP_TX_DUTYCYCLE), NULL); 207 | } 208 | 209 | /**@brief Function for handling LoRaWan received data from Gateway 210 | 211 | @param[in] app_data Pointer to rx data 212 | */ 213 | static void lorawan_rx_handler(lmh_app_data_t *app_data) 214 | { 215 | Serial.printf("LoRa Packet received on port %d, size:%d, rssi:%d, snr:%d\n", 216 | app_data->port, app_data->buffsize, app_data->rssi, app_data->snr); 217 | 218 | switch (app_data->port) 219 | { 220 | case 3: 221 | // Port 3 switches the class 222 | if (app_data->buffsize == 1) 223 | { 224 | switch (app_data->buffer[0]) 225 | { 226 | case 0: 227 | lmh_class_request(CLASS_A); 228 | break; 229 | 230 | case 1: 231 | lmh_class_request(CLASS_B); 232 | break; 233 | 234 | case 2: 235 | lmh_class_request(CLASS_C); 236 | break; 237 | 238 | default: 239 | break; 240 | } 241 | } 242 | break; 243 | 244 | case LORAWAN_APP_PORT: 245 | // YOUR_JOB: Take action on received data 246 | break; 247 | 248 | default: 249 | break; 250 | } 251 | } 252 | 253 | static void lorawan_confirm_class_handler(DeviceClass_t Class) 254 | { 255 | Serial.printf("switch to class %c done\n", "ABC"[Class]); 256 | 257 | // Informs the server that switch has occurred ASAP 258 | m_lora_app_data.buffsize = 0; 259 | m_lora_app_data.port = LORAWAN_APP_PORT; 260 | lmh_send(&m_lora_app_data, LMH_UNCONFIRMED_MSG); 261 | } 262 | 263 | static void send_lora_frame(void) 264 | { 265 | if (lmh_join_status_get() != LMH_SET) 266 | { 267 | //Not joined, try again later 268 | Serial.println("Did not join network, skip sending frame"); 269 | return; 270 | } 271 | 272 | uint32_t i = 0; 273 | m_lora_app_data.port = LORAWAN_APP_PORT; 274 | m_lora_app_data.buffer[i++] = 'H'; 275 | m_lora_app_data.buffer[i++] = 'e'; 276 | m_lora_app_data.buffer[i++] = 'l'; 277 | m_lora_app_data.buffer[i++] = 'l'; 278 | m_lora_app_data.buffer[i++] = 'o'; 279 | m_lora_app_data.buffer[i++] = ' '; 280 | m_lora_app_data.buffer[i++] = 'w'; 281 | m_lora_app_data.buffer[i++] = 'o'; 282 | m_lora_app_data.buffer[i++] = 'r'; 283 | m_lora_app_data.buffer[i++] = 'l'; 284 | m_lora_app_data.buffer[i++] = 'd'; 285 | m_lora_app_data.buffer[i++] = '!'; 286 | m_lora_app_data.buffsize = i; 287 | 288 | lmh_error_status error = lmh_send(&m_lora_app_data, LMH_UNCONFIRMED_MSG); 289 | if (error == LMH_SUCCESS) 290 | { 291 | } 292 | Serial.printf("lmh_send result %d\n", error); 293 | } 294 | 295 | /**@brief Function for handling a LoRa tx timer timeout event. 296 | */ 297 | static void tx_lora_periodic_handler(void) 298 | { 299 | TimerSetValue(&appTimer, LORAWAN_APP_TX_DUTYCYCLE); 300 | TimerStart(&appTimer); 301 | Serial.println("Sending frame"); 302 | send_lora_frame(); 303 | } 304 | 305 | /**@brief Function for the Timer initialization. 306 | 307 | @details Initializes the timer module. This creates and starts application timers. 308 | */ 309 | static uint32_t timers_init(void) 310 | { 311 | appTimer.timerNum = 3; 312 | TimerInit(&appTimer, tx_lora_periodic_handler); 313 | 314 | // ret_code_t err_code; 315 | 316 | // APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE); 317 | 318 | // // Initialize timer module. 319 | // err_code = app_timer_init(); 320 | // VERIFY_SUCCESS(err_code); 321 | 322 | // // Initialize timers 323 | // err_code = app_timer_create(&lora_tx_timer_id, APP_TIMER_MODE_REPEATED, tx_lora_periodic_handler); 324 | // VERIFY_SUCCESS(err_code); 325 | 326 | return 0; 327 | } 328 | --------------------------------------------------------------------------------