SX126x-Arduino  1.3.0
Sensor-Gateway-Deepsleep\LoRa-Gateway\src\main.cpp
#include <Arduino.h>
#include <WiFi.h>
#include <SPI.h>
#include <Ticker.h>
#include <rom/rtc.h>
#include <driver/rtc_io.h>
#include <SX126x-Arduino.h>
#include <Adafruit_MQTT.h>
#include <Adafruit_MQTT_Client.h>
// Set to 1 to stop any output on Serial
#define BATT_SAVE_ON 0
// ESP32 Feather - Alora RFM1262 pin configuration
int PIN_LORA_RESET = 32;
int PIN_LORA_DIO_1 = 14;
int PIN_LORA_BUSY = 27;
int PIN_LORA_NSS = 33;
int PIN_LORA_SCLK = 5;
int PIN_LORA_MISO = 19;
int PIN_LORA_MOSI = 18;
// Define LoRa parameters
#define RF_FREQUENCY 915600000 // Hz
#define TX_OUTPUT_POWER 22 // dBm
#define LORA_BANDWIDTH 0 // [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12]
#define LORA_CODINGRATE 1 // [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx
#define LORA_SYMBOL_TIMEOUT 0 // Symbols
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 3000
#define TX_TIMEOUT_VALUE 3000
#define BUFFER_SIZE 512 // Define the payload size here
// Event declarations
void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr);
void OnRxTimeout(void);
void OnRxError(void);
/* The Data message that will be received from the sensor nodes */
struct dataMsg
{
uint8_t startMark[4] = {0xAA, 0x55, 0x00, 0x00};
uint32_t nodeId;
int8_t tempInt;
uint8_t tempFrac;
uint8_t humidInt;
uint8_t humidFrac;
uint8_t endMark[4] = {0x00, 0x00, 0x55, 0xAA};
} dataMsg;
// Adafruit IoT stuff
#define AIO_USERNAME "<PUT_YOUR_AIO_USERNAME_HERE>"
#define AIO_KEY " < PUT_YOUR_AIO_KEY_HERE > "
#define AIO_SERVER "io.adafruit.com"
#define AIO_SERVERPORT 1883 // use 8883 for SSL
// PUT YOUR OWN WIFI AP NAME AND PASSWORD HERE
#define ssid "MyWiFi"
#define pass "qwerty123"
WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
Adafruit_MQTT_Publish temp_feed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/t_node_0");
Adafruit_MQTT_Publish humid_feed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/h_node_0");
time_t adafruitTimeout;
void goToSleep(void);
void setup()
{
#if BATT_SAVE_ON == 0
// Start serial communication
Serial.begin(115200);
#endif
// Check the reasons the CPU's are started. We need this later to decide if a full initialization
// of the SX126x is required or if the chip just woke up triggered by an interrupt from the SX126x
RESET_REASON cpu0WakeupReason = rtc_get_reset_reason(0);
RESET_REASON cpu1WakeupReason = rtc_get_reset_reason(1);
// Show we are awake
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
hw_config hwConfig;
// Define the HW configuration between MCU and SX126x
hwConfig.CHIP_TYPE = SX1262_CHIP; // Example uses an eByte E22 module with an SX1262
hwConfig.PIN_LORA_RESET = PIN_LORA_RESET; // LORA RESET
hwConfig.PIN_LORA_NSS = PIN_LORA_NSS; // LORA SPI CS
hwConfig.PIN_LORA_SCLK = PIN_LORA_SCLK; // LORA SPI CLK
hwConfig.PIN_LORA_MISO = PIN_LORA_MISO; // LORA SPI MISO
hwConfig.PIN_LORA_DIO_1 = PIN_LORA_DIO_1; // LORA DIO_1
hwConfig.PIN_LORA_BUSY = PIN_LORA_BUSY; // LORA SPI BUSY
hwConfig.PIN_LORA_MOSI = PIN_LORA_MOSI; // LORA SPI MOSI
hwConfig.RADIO_TXEN = -1; // LORA ANTENNA TX ENABLE
hwConfig.RADIO_RXEN = -1; // LORA ANTENNA RX ENABLE
hwConfig.USE_DIO2_ANT_SWITCH = true; // Example uses an CircuitRocks Alora RFM1262 which uses DIO2 pins as antenna control
hwConfig.USE_DIO3_TCXO = true; // Example uses an CircuitRocks Alora RFM1262 which uses DIO3 to control oscillator voltage
hwConfig.USE_DIO3_ANT_SWITCH = false; // Only Insight ISP4520 module uses DIO3 as antenna control
// Slowing down the ESP32 to 1/4 of its speed saves more energy
setCpuFrequencyMhz(80);
// Initialize the LoRa chip
if ((cpu0WakeupReason == DEEPSLEEP_RESET) || (cpu1WakeupReason == DEEPSLEEP_RESET))
{
// Wake up reason was a DEEPSLEEP_RESET, which means we were woke up by the SX126x
#if BATT_SAVE_ON == 0
Serial.println("Starting lora_hardware_re_init");
#endif
}
else
{
// Other wake up reasons mean we need to do a complete initialization of the SX126x
#if BATT_SAVE_ON == 0
Serial.println("Starting lora_hardware_init");
#endif
lora_hardware_init(hwConfig);
}
// Initialize the Radio callbacks we need to receive data
RadioEvents.RxDone = OnRxDone;
RadioEvents.RxTimeout = OnRxTimeout;
RadioEvents.RxError = OnRxError;
// Check the wakeup reason
if ((cpu0WakeupReason == DEEPSLEEP_RESET) || (cpu1WakeupReason == DEEPSLEEP_RESET))
{
// Wake up reason was a DEEPSLEEP_RESET, just re-initialize the callbacks
#if BATT_SAVE_ON == 0
Serial.println("Trying to handle SX1262 event after deep sleep wakeup");
#endif
// Re-initialize the Radio
// Initialize WiFi connection
WiFi.begin(ssid, pass);
// Handle LoRa events to find out what the reason for the wake up was and handle the data
}
else
{
#if BATT_SAVE_ON == 0
Serial.println("Power on reset, reinitialize the Radio");
#endif
// Other wake up reasons mean we need to do a complete initialization of the SX126x
// Initialize the Radio
// Set Radio channel
Radio.SetChannel(RF_FREQUENCY);
// Set Radio TX configuration
Radio.SetTxConfig(MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, TX_TIMEOUT_VALUE);
// Set Radio RX configuration
Radio.SetRxConfig(MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true);
}
// goToSleep will start listening to LoRa data packages and put the ESP32 into deep sleep
goToSleep();
}
void loop()
{
}
void goToSleep(void)
{
// Start waiting for data package
// To get maximum power savings we use Radio.SetRxDutyCycle instead of Radio.Rx(0)
// This function keeps the SX1261/2 chip most of the time in sleep and only wakes up short times
// to catch incoming data packages
Radio.SetRxDutyCycle(2 * 1024 * 1000 * 15.625, 10 * 1024 * 1000 * 15.625);
// Go back to bed
#if BATT_SAVE_ON == 0
Serial.println("Start sleeping");
#endif
// Make sure the DIO1, RESET and NSS GPIOs are hold on required levels during deep sleep
rtc_gpio_set_direction((gpio_num_t)PIN_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)PIN_LORA_DIO_1);
rtc_gpio_set_direction((gpio_num_t)PIN_LORA_RESET, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_set_level((gpio_num_t)PIN_LORA_RESET, HIGH);
rtc_gpio_set_direction((gpio_num_t)PIN_LORA_NSS, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_set_level((gpio_num_t)PIN_LORA_NSS, HIGH);
// Setup deep sleep with wakeup by external source
esp_sleep_enable_ext0_wakeup((gpio_num_t)PIN_LORA_DIO_1, RISING);
// Finally set ESP32 into sleep
esp_deep_sleep_start();
}
void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
{
#if BATT_SAVE_ON == 0
Serial.println("Receive finished");
char debugOutput[1024];
for (int idx = 0; idx < size; idx++)
{
sprintf(&debugOutput[(idx * 2)], "%02X", payload[idx]);
}
Serial.printf("Data: %s\n", debugOutput);
#endif
// Save received message in a buffer
memcpy((void *)&dataMsg, payload, size);
// Check if it is a valid message
char startMark[4] = {0xAA, 0x55, 0x00, 0x00};
char endMark[4] = {0x00, 0x00, 0x55, 0xAA};
if ((memcmp((void *)dataMsg.startMark, startMark, 4) != 0) || (memcmp((void *)dataMsg.endMark, endMark, 4) != 0))
{
#if BATT_SAVE_ON == 0
Serial.println("Data is not for us");
goToSleep();
#endif
}
// Wait for WiFi connection
while (!WiFi.isConnected())
{
if ((millis() - adafruitTimeout) > 60000)
{
#if BATT_SAVE_ON == 0
Serial.println("Timeout connecting WiFi");
#endif
goToSleep();
}
}
int8_t ret;
// Connect to Adafruit IO
while ((ret = mqtt.connect()) != 0)
{
if ((millis() - adafruitTimeout) > 60000)
{
#if BATT_SAVE_ON == 0
Serial.println("Timeout connecting to Adafruit IO");
#endif
goToSleep();
}
}
// Topic for publishing
char devTopic[256];
// Data for publishing
char devValue[128];
// Publish Temperature from received message
sprintf(devTopic, "%s/feeds/%08X_t",
AIO_USERNAME,
dataMsg.nodeId);
sprintf(devValue, "%.2f", ((float)dataMsg.tempFrac / 100.0) + (float)dataMsg.tempInt);
#if BATT_SAVE_ON == 0
Serial.printf("Publishing topic %s with value %s\n", devTopic, devValue);
#endif
mqtt.publish(devTopic, devValue);
// Publish Humidity from received message
sprintf(devTopic, "%s/feeds/%08X_h",
AIO_USERNAME,
dataMsg.nodeId);
sprintf(devValue, "%.2f", ((float)dataMsg.humidFrac / 100.0) + (float)dataMsg.humidInt);
#if BATT_SAVE_ON == 0
Serial.printf("Publishing topic %s with value %s\n", devTopic, devValue);
#endif
mqtt.publish(devTopic, devValue);
// Cleanup
mqtt.disconnect();
WiFi.disconnect(true);
// LoRa receive finished, go back to bed
goToSleep();
}
void OnRxTimeout(void)
{
#if BATT_SAVE_ON == 0
Serial.println("Receive timeout");
#endif
// LoRa RX failed, go back to bed
goToSleep();
}
void OnRxError(void)
{
#if BATT_SAVE_ON == 0
Serial.println("Receive error");
#endif
// LoRa RX failed, go back to bed
goToSleep();
}
SX126xSetDioIrqParams
void SX126xSetDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask)
Sets the IRQ mask and DIO masks.
Definition: sx126x.cpp:393
IRQ_RX_TX_TIMEOUT
@ IRQ_RX_TX_TIMEOUT
Definition: sx126x.h:448
IRQ_RADIO_NONE
@ IRQ_RADIO_NONE
Definition: sx126x.h:438
hw_config::RADIO_TXEN
int RADIO_TXEN
Definition: board.h:69
RadioEvents_t::RxTimeout
void(* RxTimeout)(void)
Rx Timeout callback prototype.
Definition: radio.h:75
Radio_s::SetRxConfig
void(* SetRxConfig)(RadioModems_t modem, uint32_t bandwidth, uint32_t datarate, uint8_t coderate, uint32_t bandwidthAfc, uint16_t preambleLen, uint16_t symbTimeout, bool fixLen, uint8_t payloadLen, bool crcOn, bool freqHopOn, uint8_t hopPeriod, bool iqInverted, bool rxContinuous)
Sets the reception parameters.
Definition: radio.h:195
Radio_s::Init
void(* Init)(RadioEvents_t *events)
Initializes the radio.
Definition: radio.h:109
RadioEvents_t
Radio driver callback functions.
Definition: radio.h:52
RadioEvents
static RadioEvents_t RadioEvents
Definition: LoRaMac.cpp:325
IRQ_RX_DONE
@ IRQ_RX_DONE
Definition: sx126x.h:440
RadioEvents_t::RxError
void(* RxError)(void)
Rx Error callback prototype.
Definition: radio.h:79
RadioEvents_t::RxDone
void(* RxDone)(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
Rx Done callback prototype.
Definition: radio.h:71
hw_config::USE_DIO2_ANT_SWITCH
bool USE_DIO2_ANT_SWITCH
Definition: board.h:71
lora_hardware_init
uint32_t lora_hardware_init(hw_config hwConfig)
Initializes the target board peripherals.
Definition: board.cpp:47
Radio_s::SetTxConfig
void(* SetTxConfig)(RadioModems_t modem, int8_t power, uint32_t fdev, uint32_t bandwidth, uint32_t datarate, uint8_t coderate, uint16_t preambleLen, bool fixLen, bool crcOn, bool freqHopOn, uint8_t hopPeriod, bool iqInverted, uint32_t timeout)
Sets the transmission parameters.
Definition: radio.h:237
hw_config::PIN_LORA_MOSI
int PIN_LORA_MOSI
Definition: board.h:68
Radio
const struct Radio_s Radio
Radio driver.
Definition: radio.cpp:358
SX126x-Arduino.h
hw_config::CHIP_TYPE
int CHIP_TYPE
Definition: board.h:61
hw_config::PIN_LORA_NSS
int PIN_LORA_NSS
Definition: board.h:63
lora_hardware_re_init
uint32_t lora_hardware_re_init(hw_config hwConfig)
Initializes the target board peripherals after deep sleep wake up.
Definition: board.cpp:79
Radio_s::SetRxDutyCycle
void(* SetRxDutyCycle)(uint32_t rxTime, uint32_t sleepTime)
Sets the Rx duty cycle management parameters.
Definition: radio.h:383
SX1262_CHIP
#define SX1262_CHIP
Definition: board.h:55
Radio_s::Standby
void(* Standby)(void)
Sets the radio in standby mode.
Definition: radio.h:275
hw_config::USE_DIO3_ANT_SWITCH
bool USE_DIO3_ANT_SWITCH
Definition: board.h:73
Radio_s::IrqProcessAfterDeepSleep
void(* IrqProcessAfterDeepSleep)(void)
Definition: radio.h:362
hw_config::PIN_LORA_DIO_1
int PIN_LORA_DIO_1
Definition: board.h:66
hw_config::PIN_LORA_SCLK
int PIN_LORA_SCLK
Definition: board.h:64
hw_config
Definition: board.h:60
hw_config::USE_DIO3_TCXO
bool USE_DIO3_TCXO
Definition: board.h:72
hw_config::PIN_LORA_MISO
int PIN_LORA_MISO
Definition: board.h:65
MODEM_LORA
@ MODEM_LORA
Definition: radio.h:34
hw_config::PIN_LORA_RESET
int PIN_LORA_RESET
Definition: board.h:62
Radio_s::ReInit
void(* ReInit)(RadioEvents_t *events)
Re-Initializes the radio after CPU wakeup from deep sleep.
Definition: radio.h:115
Radio_s::SetChannel
void(* SetChannel)(uint32_t freq)
Sets the channel frequency.
Definition: radio.h:133
hw_config::RADIO_RXEN
int RADIO_RXEN
Definition: board.h:70
hw_config::PIN_LORA_BUSY
int PIN_LORA_BUSY
Definition: board.h:67