Header comments & ABP
This commit is contained in:
parent
ff5ade2aa1
commit
c0ebd5a92e
6 changed files with 88 additions and 499 deletions
|
@ -1,178 +1,68 @@
|
||||||
/*
|
/*
|
||||||
RadioLib LoRaWAN End Device ABP Example
|
RadioLib LoRaWAN ABP Example
|
||||||
|
|
||||||
This example sets up a LoRaWAN node using ABP (activation
|
ABP = Activation by Personalisation, an alternative
|
||||||
by personalization). Before you start, you will have to
|
to OTAA (Over the Air Activation). OTAA is preferable.
|
||||||
|
|
||||||
|
This example joins a LoRaWAN network and will send
|
||||||
|
uplink packets. Before you start, you will have to
|
||||||
register your device at https://www.thethingsnetwork.org/
|
register your device at https://www.thethingsnetwork.org/
|
||||||
After your device is registered, you can run this example.
|
After your device is registered, you can run this example.
|
||||||
The device will start uploading data directly,
|
The device will join the network and start uploading data.
|
||||||
without having to join the network.
|
|
||||||
|
|
||||||
NOTE: LoRaWAN v1.1 requires storing parameters persistently!
|
LoRaWAN v1.1 requires the use of EEPROM (persistent storage).
|
||||||
RadioLib does this by using EEPROM (persistent storage),
|
Running this examples REQUIRES you to check "Resets frame counters"
|
||||||
by default starting at address 0 and using 448 bytes.
|
on your LoRaWAN dashboard. Refer to the network's documentation
|
||||||
If you already use EEPROM in your application,
|
on how to do this.
|
||||||
you will have to either avoid this range, or change it
|
|
||||||
by setting a different start address by changing the value of
|
|
||||||
RADIOLIB_HAL_PERSISTENT_STORAGE_BASE macro, either
|
|
||||||
during build or in src/BuildOpt.h.
|
|
||||||
|
|
||||||
For default module settings, see the wiki page
|
For default module settings, see the wiki page
|
||||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||||
|
|
||||||
For full API reference, see the GitHub Pages
|
For full API reference, see the GitHub Pages
|
||||||
https://jgromes.github.io/RadioLib/
|
https://jgromes.github.io/RadioLib/
|
||||||
|
|
||||||
|
For LoRaWAN details, see the wiki page
|
||||||
|
https://github.com/jgromes/RadioLib/wiki/LoRaWAN
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// include the library
|
#include "configABP.h"
|
||||||
#include <RadioLib.h>
|
|
||||||
|
|
||||||
// SX1262 has the following pin order:
|
|
||||||
// Module(NSS/CS, DIO1, RESET, BUSY)
|
|
||||||
// SX1262 radio = new Module(8, 14, 12, 13);
|
|
||||||
|
|
||||||
// SX1278 has the following pin order:
|
|
||||||
// Module(NSS/CS, DIO0, RESET, DIO1)
|
|
||||||
SX1278 radio = new Module(10, 2, 9, 3);
|
|
||||||
|
|
||||||
// create the node instance on the EU-868 band
|
|
||||||
// using the radio module and the encryption key
|
|
||||||
// make sure you are using the correct band
|
|
||||||
// based on your geographical location!
|
|
||||||
LoRaWANNode node(&radio, &EU868);
|
|
||||||
|
|
||||||
// for fixed bands with subband selection
|
|
||||||
// such as US915 and AU915, you must specify
|
|
||||||
// the subband that matches the Frequency Plan
|
|
||||||
// that you selected on your LoRaWAN console
|
|
||||||
/*
|
|
||||||
LoRaWANNode node(&radio, &US915, 2);
|
|
||||||
*/
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(115200);
|
||||||
|
while (!Serial);
|
||||||
|
delay(5000); // Give time to switch to the serial monitor
|
||||||
|
Serial.println(F("\nSetup ... "));
|
||||||
|
|
||||||
// initialize radio (SX1262 / SX1278 / ... ) with default settings
|
Serial.println(F("Initalise the radio"));
|
||||||
Serial.print(F("[Radio] Initializing ... "));
|
|
||||||
int state = radio.begin();
|
int state = radio.begin();
|
||||||
if(state == RADIOLIB_ERR_NONE) {
|
debug(state != RADIOLIB_ERR_NONE, F("Initalise radio failed"), state, true);
|
||||||
Serial.println(F("success!"));
|
|
||||||
} else {
|
|
||||||
Serial.print(F("failed, code "));
|
|
||||||
Serial.println(state);
|
|
||||||
while(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// device address - this number can be anything
|
Serial.println(F("Initalise LoRaWAN Network credentials"));
|
||||||
// when adding new end device in TTN, you can generate this number,
|
state = node.beginABP(devAddr, NwkSEncKey, AppSKey, NwkSKey, SNwkSIntKey, true);
|
||||||
// or you can set any value you want, provided it is unique
|
debug(state < RADIOLIB_ERR_NONE, F("Session setup failed"), state, true);
|
||||||
uint32_t devAddr = 0x12345678;
|
|
||||||
|
|
||||||
// select some encryption keys which will be used to secure the communication
|
|
||||||
// there are two of them - network key and application key
|
|
||||||
// because LoRaWAN uses AES-128, the key MUST be 16 bytes (or characters) long
|
|
||||||
|
|
||||||
// network key is the ASCII string "topSecretKey1234"
|
|
||||||
uint8_t nwkSKey[] = { 0x74, 0x6F, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65,
|
|
||||||
0x74, 0x4B, 0x65, 0x79, 0x31, 0x32, 0x33, 0x34 };
|
|
||||||
|
|
||||||
// application key is the ASCII string "aDifferentKeyABC"
|
|
||||||
uint8_t appSKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
|
|
||||||
0x6E, 0x74, 0x4B, 0x65, 0x79, 0x41, 0x42, 0x43 };
|
|
||||||
|
|
||||||
// network key 2 is the ASCII string "topSecretKey5678"
|
|
||||||
uint8_t fNwkSIntKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
|
|
||||||
0x6E, 0x74, 0x4B, 0x65, 0x35, 0x36, 0x37, 0x38 };
|
|
||||||
|
|
||||||
// network key 3 is the ASCII string "aDifferentKeyDEF"
|
|
||||||
uint8_t sNwkSIntKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
|
|
||||||
0x6E, 0x74, 0x4B, 0x65, 0x79, 0x44, 0x45, 0x46 };
|
|
||||||
|
|
||||||
// prior to LoRaWAN 1.1.0, only a single "nwkKey" is used
|
|
||||||
// when connecting to LoRaWAN 1.0 network, "appKey" will be disregarded
|
|
||||||
// and can be set to NULL
|
|
||||||
|
|
||||||
|
|
||||||
// if using EU868 on ABP in TTN, you need to set the SF for RX2 window manually
|
|
||||||
/*
|
|
||||||
node.rx2.drMax = 3;
|
|
||||||
*/
|
|
||||||
|
|
||||||
// on EEPROM-enabled boards, after the device has been activated,
|
|
||||||
// the session can be restored without rejoining after device power cycle
|
|
||||||
// this is intrinsically done when calling `beginABP()` with the same keys
|
|
||||||
// in that case, the function will not need to transmit a JoinRequest
|
|
||||||
|
|
||||||
// to start a LoRaWAN v1.0 session,
|
|
||||||
// the user can remove the fNwkSIntKey and sNwkSIntKey
|
|
||||||
/*
|
|
||||||
state = node.beginABP(devAddr, nwkSKey, appSKey);
|
|
||||||
*/
|
|
||||||
|
|
||||||
// start the device by directly providing the encryption keys and device address
|
|
||||||
Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... "));
|
|
||||||
state = node.beginABP(devAddr, nwkSKey, appSKey, fNwkSIntKey, sNwkSIntKey);
|
|
||||||
if(state >= RADIOLIB_ERR_NONE) {
|
|
||||||
Serial.println(F("success!"));
|
|
||||||
} else {
|
|
||||||
Serial.print(F("failed, code "));
|
|
||||||
Serial.println(state);
|
|
||||||
while(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Serial.println(F("Ready!\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// counter to keep track of transmitted packets
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
// send uplink to port 10
|
Serial.println(F("Sending uplink"));
|
||||||
Serial.print(F("[LoRaWAN] Sending uplink packet ... "));
|
|
||||||
String strUp = "Hello!" + String(count++);
|
|
||||||
String strDown;
|
|
||||||
int state = node.sendReceive(strUp, 10, strDown);
|
|
||||||
if(state == RADIOLIB_ERR_NONE) {
|
|
||||||
Serial.println(F("received a downlink!"));
|
|
||||||
|
|
||||||
// print data of the packet (if there are any)
|
// Read some inputs
|
||||||
Serial.print(F("[LoRaWAN] Data:\t\t"));
|
uint8_t Digital1 = digitalRead(2);
|
||||||
if(strDown.length() > 0) {
|
uint16_t Analog1 = analogRead(A0);
|
||||||
Serial.println(strDown);
|
|
||||||
} else {
|
|
||||||
Serial.println(F("<MAC commands only>"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// print RSSI (Received Signal Strength Indicator)
|
// Build payload byte array
|
||||||
Serial.print(F("[LoRaWAN] RSSI:\t\t"));
|
uint8_t uplinkPayload[3];
|
||||||
Serial.print(radio.getRSSI());
|
uplinkPayload[0] = Digital1;
|
||||||
Serial.println(F(" dBm"));
|
uplinkPayload[1] = highByte(Analog1); // See notes for high/lowByte functions
|
||||||
|
uplinkPayload[2] = lowByte(Analog1);
|
||||||
|
|
||||||
// print SNR (Signal-to-Noise Ratio)
|
// Perform an uplink
|
||||||
Serial.print(F("[LoRaWAN] SNR:\t\t"));
|
int state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload));
|
||||||
Serial.print(radio.getSNR());
|
debug((state != RADIOLIB_ERR_RX_TIMEOUT) && (state != RADIOLIB_ERR_NONE), F("Error in sendReceive"), state, false);
|
||||||
Serial.println(F(" dB"));
|
|
||||||
|
|
||||||
// print frequency error
|
// Wait until next uplink - observing legal & TTN FUP constraints
|
||||||
Serial.print(F("[LoRaWAN] Frequency error:\t"));
|
delay(uplinkIntervalSeconds * 1000UL);
|
||||||
Serial.print(radio.getFrequencyError());
|
|
||||||
Serial.println(F(" Hz"));
|
|
||||||
|
|
||||||
} else if(state == RADIOLIB_ERR_RX_TIMEOUT) {
|
|
||||||
Serial.println(F("no downlink!"));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Serial.print(F("failed, code "));
|
|
||||||
Serial.println(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// on EEPROM enabled boards, you should save the current session
|
|
||||||
// by calling "saveSession" which allows retrieving the session after reboot or deepsleep
|
|
||||||
node.saveSession();
|
|
||||||
|
|
||||||
// wait before sending another packet
|
|
||||||
uint32_t minimumDelay = 60000; // try to send once every minute
|
|
||||||
uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per law!)
|
|
||||||
uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows
|
|
||||||
|
|
||||||
delay(delayMs);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,26 +6,29 @@
|
||||||
// How often to send an uplink - consider legal & FUP constraints - see notes
|
// How often to send an uplink - consider legal & FUP constraints - see notes
|
||||||
const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds
|
const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds
|
||||||
|
|
||||||
// JoinEUI - previous versions of LoRaWAN called this AppEUI
|
// Device address - either a development address or one assigned
|
||||||
// for development purposes you can use all zeros - see wiki for details
|
// to the LoRaWAN Service Provider - TTN will generate one for you
|
||||||
#define RADIOLIB_LORAWAN_JOIN_EUI 0x0000000000000000
|
#ifndef RADIOLIB_LORAWAN_DEV_ADDR // Replace with your DevAddr
|
||||||
|
#define RADIOLIB_LORAWAN_DEV_ADDR 0x------
|
||||||
|
#endif
|
||||||
|
|
||||||
// The Device EUI & two keys can be generated on the TTN console
|
#ifndef RADIOLIB_LORAWAN_NWKS_KEY // Replace with your NwkS Key
|
||||||
#ifndef RADIOLIB_LORAWAN_DEV_EUI // Replace with your Device EUI
|
#define RADIOLIB_LORAWAN_NWKS_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
||||||
#define RADIOLIB_LORAWAN_DEV_EUI 0x---------------
|
|
||||||
#endif
|
#endif
|
||||||
#ifndef RADIOLIB_LORAWAN_APP_KEY // Replace with your App Key
|
#ifndef RADIOLIB_LORAWAN_SNWKSINT_KEY // Replace with your SNwkSInt Key
|
||||||
#define RADIOLIB_LORAWAN_APP_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
#define RADIOLIB_LORAWAN_SNWKSINT_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
||||||
#endif
|
#endif
|
||||||
#ifndef RADIOLIB_LORAWAN_NWK_KEY // Put your Nwk Key here
|
#ifndef RADIOLIB_LORAWAN_NWKSENC_KEY // Replace with your NwkSEnc Key
|
||||||
#define RADIOLIB_LORAWAN_NWK_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
#define RADIOLIB_LORAWAN_NWKSENC_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
||||||
|
#endif
|
||||||
|
#ifndef RADIOLIB_LORAWAN_APPS_KEY // Replace with your AppS Key
|
||||||
|
#define RADIOLIB_LORAWAN_APPS_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// For the curious, the #ifndef blocks allow for automated testing &/or you can
|
// For the curious, the #ifndef blocks allow for automated testing &/or you can
|
||||||
// put your EUI & keys in to your platformio.ini - see wiki for more tips
|
// put your EUI & keys in to your platformio.ini - see wiki for more tips
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Regional choices: EU868, US915, AU915, AS923, IN865, KR920, CN780, CN500
|
// Regional choices: EU868, US915, AU915, AS923, IN865, KR920, CN780, CN500
|
||||||
const LoRaWANBand_t Region = EU868;
|
const LoRaWANBand_t Region = EU868;
|
||||||
const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0
|
const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0
|
||||||
|
@ -97,11 +100,13 @@ const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Copy over the EUI's & keys in to the something that will not compile if incorrectly formatted
|
// Copy over the keys in to the something that will not compile if incorrectly formatted
|
||||||
uint64_t joinEUI = RADIOLIB_LORAWAN_JOIN_EUI;
|
uint32_t devAddr = RADIOLIB_LORAWAN_DEV_ADDR;
|
||||||
uint64_t devEUI = RADIOLIB_LORAWAN_DEV_EUI;
|
uint8_t NwkSKey[] = { RADIOLIB_LORAWAN_NWKS_KEY };
|
||||||
uint8_t appKey[] = { RADIOLIB_LORAWAN_APP_KEY };
|
uint8_t SNwkSIntKey[] = { RADIOLIB_LORAWAN_SNWKSINT_KEY }; // Previously sNwkSIntKey
|
||||||
uint8_t nwkKey[] = { RADIOLIB_LORAWAN_NWK_KEY };
|
uint8_t NwkSEncKey[] = { RADIOLIB_LORAWAN_NWKSENC_KEY }; // Previously fNwkSIntKey
|
||||||
|
uint8_t AppSKey[] = { RADIOLIB_LORAWAN_APPS_KEY };
|
||||||
|
|
||||||
|
|
||||||
// Create the LoRaWAN node
|
// Create the LoRaWAN node
|
||||||
LoRaWANNode node(&radio, &Region, subBand);
|
LoRaWANNode node(&radio, &Region, subBand);
|
||||||
|
|
|
@ -1,196 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
This demonstrates how to save the join information in to permanent memory
|
|
||||||
so that if the power fails, batteries run out or are changed, the rejoin
|
|
||||||
is more efficient & happens sooner due to the way that LoRaWAN secures
|
|
||||||
the join process - see the wiki for more details.
|
|
||||||
|
|
||||||
This is typically useful for devices that need more power than a battery
|
|
||||||
driven sensor - something like a air quality monitor or GPS based device that
|
|
||||||
is likely to use up it's power source resulting in loss of the session.
|
|
||||||
|
|
||||||
The relevant code is flagged with a ##### comment
|
|
||||||
|
|
||||||
Saving the entire session is possible but not demonstrated here - it has
|
|
||||||
implications for flash wearing and complications with which parts of the
|
|
||||||
session may have changed after an uplink. So it is assumed that the device
|
|
||||||
is going in to deep-sleep, as below, between normal uplinks.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(ESP32)
|
|
||||||
#pragma error ("This is not the example your device is looking for - ESP32 only")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// ##### Load the ESP32 preferences facilites
|
|
||||||
#include <Preferences.h>
|
|
||||||
Preferences store;
|
|
||||||
|
|
||||||
// LoRaWAN config, credentials & pinmap
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <RadioLib.h>
|
|
||||||
|
|
||||||
// Utilities & vars to support ESP32 deep-sleep. The RTC_DATA_ATTR attribute
|
|
||||||
// puts these in to the RTC memory which is preserved during deep-sleep
|
|
||||||
RTC_DATA_ATTR uint16_t bootCount = 1;
|
|
||||||
RTC_DATA_ATTR uint16_t bootCountSinceUnsuccessfulJoin = 0;
|
|
||||||
RTC_DATA_ATTR uint8_t LWsession[RADIOLIB_LORAWAN_SESSION_BUF_SIZE];
|
|
||||||
|
|
||||||
// Abbreviated version from the Arduino-ESP32 package, see
|
|
||||||
// https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/deepsleep.html
|
|
||||||
// for the complete set of options
|
|
||||||
void print_wakeup_reason() {
|
|
||||||
esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
|
|
||||||
if (wakeup_reason == ESP_SLEEP_WAKEUP_TIMER) {
|
|
||||||
Serial.println(F("Wake from sleep"));
|
|
||||||
} else {
|
|
||||||
Serial.print(F("Wake not caused by deep sleep: "));
|
|
||||||
Serial.println(wakeup_reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.print(F("Boot count: "));
|
|
||||||
Serial.println(bootCount++);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put device in to lowest power deep-sleep mode
|
|
||||||
void gotoSleep(uint32_t seconds) {
|
|
||||||
esp_sleep_enable_timer_wakeup(seconds * 1000UL * 1000UL); // Function uses uS
|
|
||||||
Serial.println(F("Sleeping\n"));
|
|
||||||
Serial.flush();
|
|
||||||
|
|
||||||
esp_deep_sleep_start();
|
|
||||||
|
|
||||||
// If this appears in the serial debug, we didn't go to sleep!
|
|
||||||
// So take defensive action so we don't continually uplink
|
|
||||||
Serial.println(F("\n\n### Sleep failed, delay of 5 minutes & then restart ###\n"));
|
|
||||||
delay(5UL * 60UL * 1000UL);
|
|
||||||
ESP.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Setup & execute all device functions ...
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
while (!Serial); // Wait for serial to be initalised
|
|
||||||
delay(2000); // Give time to switch to the serial monitor
|
|
||||||
Serial.println(F("\nSetup"));
|
|
||||||
print_wakeup_reason();
|
|
||||||
|
|
||||||
int16_t state = 0; // return value for calls to RadioLib
|
|
||||||
|
|
||||||
// Setup the radio based on the pinmap (connections) in config.h
|
|
||||||
Serial.println(F("Initalise the radio"));
|
|
||||||
state = radio.begin();
|
|
||||||
debug(state != RADIOLIB_ERR_NONE, F("Initalise radio failed"), state, true);
|
|
||||||
|
|
||||||
Serial.println(F("Recalling LoRaWAN nonces & session"));
|
|
||||||
// ##### Setup the flash storage
|
|
||||||
store.begin("radiolib");
|
|
||||||
// ##### If we have previously saved nonces, restore them
|
|
||||||
if (store.isKey("nonces")) {
|
|
||||||
uint8_t buffer[RADIOLIB_LORAWAN_NONCES_BUF_SIZE];// Create somewhere to store nonces
|
|
||||||
store.getBytes("nonces", buffer, RADIOLIB_LORAWAN_NONCES_BUF_SIZE);// Get them to the store
|
|
||||||
state = node.setBufferNonces(buffer); // Send them to LoRaWAN
|
|
||||||
debug(state != RADIOLIB_ERR_NONE, F("Restoring nonces buffer failed"), state, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recall session from RTC deep-sleep preserved variable
|
|
||||||
state = node.setBufferSession(LWsession); // Send them to LoRaWAN stack
|
|
||||||
// If we have booted at least once we should have a session to restore, so report any failure
|
|
||||||
// Otherwise no point saying there's been a failure when it was bound to fail with an empty
|
|
||||||
// LWsession var. At this point, bootCount has already been incremented, hence the > 2
|
|
||||||
debug((state != RADIOLIB_ERR_NONE) && (bootCount > 2), F("Restoring session buffer failed"), state, false);
|
|
||||||
|
|
||||||
// Process the restored session or failing that, create a new one &
|
|
||||||
// return flag to indicate a fresh join is required
|
|
||||||
Serial.println(F("Setup LoRaWAN session"));
|
|
||||||
state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, false);
|
|
||||||
// See comment above, no need to report a failure that is bound to occur on first boot
|
|
||||||
debug((state != RADIOLIB_ERR_NONE) && (bootCount > 2), F("Restore session failed"), state, false);
|
|
||||||
|
|
||||||
// Loop until successful join
|
|
||||||
while (state != RADIOLIB_ERR_NONE) {
|
|
||||||
Serial.println(F("Join ('login') to the LoRaWAN Network"));
|
|
||||||
state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, true);
|
|
||||||
|
|
||||||
if (state < RADIOLIB_ERR_NONE) {
|
|
||||||
Serial.print(F("Join failed: "));
|
|
||||||
Serial.println(state);
|
|
||||||
|
|
||||||
// How long to wait before join attenpts. This is an interim solution pending
|
|
||||||
// implementation of TS001 LoRaWAN Specification section #7 - this doc applies to v1.0.4 & v1.1
|
|
||||||
// It sleeps for longer & longer durations to give time for any gateway issues to resolve
|
|
||||||
// or whatever is interfering with the device <-> gateway airwaves.
|
|
||||||
uint32_t sleepForSeconds = min((bootCountSinceUnsuccessfulJoin++ + 1UL) * 60UL, 3UL * 60UL);
|
|
||||||
Serial.print(F("Boots since unsuccessful join: "));
|
|
||||||
Serial.println(bootCountSinceUnsuccessfulJoin);
|
|
||||||
Serial.print(F("Retrying join in "));
|
|
||||||
Serial.print(sleepForSeconds);
|
|
||||||
Serial.println(F(" seconds"));
|
|
||||||
|
|
||||||
gotoSleep(sleepForSeconds);
|
|
||||||
|
|
||||||
} else { // Join was successful
|
|
||||||
Serial.println(F("Joined"));
|
|
||||||
|
|
||||||
// ##### Save the join counters (nonces) to permanent store
|
|
||||||
Serial.println(F("Saving nonces to flash"));
|
|
||||||
uint8_t buffer[RADIOLIB_LORAWAN_NONCES_BUF_SIZE]; // Create somewhere to store nonces
|
|
||||||
uint8_t *persist = node.getBufferNonces(); // Get pointer to nonces
|
|
||||||
memcpy(buffer, persist, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); // Copy in to buffer
|
|
||||||
store.putBytes("nonces", buffer, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); // Send them to the store
|
|
||||||
|
|
||||||
// We'll save the session after the uplink
|
|
||||||
|
|
||||||
// Reset the failed join count
|
|
||||||
bootCountSinceUnsuccessfulJoin = 0;
|
|
||||||
|
|
||||||
delay(1000); // Hold off off hitting the airwaves again too soon - an issue in the US
|
|
||||||
|
|
||||||
} // if beginOTAA state
|
|
||||||
} // while join
|
|
||||||
|
|
||||||
// ##### Close the store
|
|
||||||
store.end();
|
|
||||||
|
|
||||||
|
|
||||||
// ----- And now for the main event -----
|
|
||||||
Serial.println(F("Sending uplink"));
|
|
||||||
|
|
||||||
// Read some inputs
|
|
||||||
uint8_t Digital2 = digitalRead(2);
|
|
||||||
uint16_t Analog1 = analogRead(A1);
|
|
||||||
|
|
||||||
// Build payload byte array
|
|
||||||
uint8_t uplinkPayload[3];
|
|
||||||
uplinkPayload[0] = Digital2;
|
|
||||||
uplinkPayload[1] = highByte(Analog1); // See notes for high/lowByte functions
|
|
||||||
uplinkPayload[2] = lowByte(Analog1);
|
|
||||||
|
|
||||||
// Perform an uplink
|
|
||||||
state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload));
|
|
||||||
debug((state != RADIOLIB_ERR_RX_TIMEOUT) && (state != RADIOLIB_ERR_NONE), F("Error in sendReceive"), state, false);
|
|
||||||
|
|
||||||
Serial.print(F("FcntUp: "));
|
|
||||||
Serial.println(node.getFcntUp());
|
|
||||||
|
|
||||||
// Now save session to RTC memory
|
|
||||||
uint8_t *persist = node.getBufferSession();
|
|
||||||
memcpy(LWsession, persist, RADIOLIB_LORAWAN_SESSION_BUF_SIZE);
|
|
||||||
|
|
||||||
// Wait until next uplink - observing legal & TTN FUP constraints
|
|
||||||
gotoSleep(uplinkIntervalSeconds);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// The ESP32 wakes from deep-sleep and starts from the very beginning
|
|
||||||
// which is a very good place to start, as any singing nun knows.
|
|
||||||
// It then goes back to sleep, so loop() is never called and which is
|
|
||||||
// why it is empty.
|
|
||||||
|
|
||||||
void loop() {}
|
|
||||||
|
|
|
@ -1,130 +0,0 @@
|
||||||
#ifndef _CONFIG_H
|
|
||||||
#define _CONFIG_H
|
|
||||||
|
|
||||||
#include <RadioLib.h>
|
|
||||||
|
|
||||||
// How often to send an uplink - consider legal & FUP constraints - see notes
|
|
||||||
const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds
|
|
||||||
|
|
||||||
// JoinEUI - previous versions of LoRaWAN called this AppEUI
|
|
||||||
// for development purposes you can use all zeros - see wiki for details
|
|
||||||
#define RADIOLIB_LORAWAN_JOIN_EUI 0x0000000000000000
|
|
||||||
|
|
||||||
// The Device EUI & two keys can be generated on the TTN console
|
|
||||||
#ifndef RADIOLIB_LORAWAN_DEV_EUI // Replace with your Device EUI
|
|
||||||
#define RADIOLIB_LORAWAN_DEV_EUI 0x---------------
|
|
||||||
#endif
|
|
||||||
#ifndef RADIOLIB_LORAWAN_APP_KEY // Replace with your App Key
|
|
||||||
#define RADIOLIB_LORAWAN_APP_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
|
||||||
#endif
|
|
||||||
#ifndef RADIOLIB_LORAWAN_NWK_KEY // Put your Nwk Key here
|
|
||||||
#define RADIOLIB_LORAWAN_NWK_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// For the curious, the #ifndef blocks allow for automated testing &/or you can
|
|
||||||
// put your EUI & keys in to your platformio.ini - see wiki for more tips
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Regional choices: EU868, US915, AU915, AS923, IN865, KR920, CN780, CN500
|
|
||||||
const LoRaWANBand_t Region = EU868;
|
|
||||||
const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0
|
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Below is to support the sketch - only make changes if the notes say so ...
|
|
||||||
|
|
||||||
// Auto select MCU <-> radio connections
|
|
||||||
// If you get an error message when compiling, it may be that the
|
|
||||||
// pinmap could not be determined - see the notes for more info
|
|
||||||
|
|
||||||
// Adafruit
|
|
||||||
#if defined(ARDUINO_SAMD_FEATHER_M0)
|
|
||||||
#pragma message ("Adafruit Feather M0 with RFM95")
|
|
||||||
#pragma message ("Link required on board")
|
|
||||||
SX1276 radio = new Module(8, 3, 4, 6);
|
|
||||||
|
|
||||||
|
|
||||||
// LilyGo
|
|
||||||
#elif defined(ARDUINO_TTGO_LORA32_V1)
|
|
||||||
#pragma message ("TTGO LoRa32 v1 - no Display")
|
|
||||||
SX1276 radio = new Module(18, 26, 14, 33);
|
|
||||||
|
|
||||||
#elif defined(ARDUINO_TTGO_LORA32_V2)
|
|
||||||
#pragma error ("ARDUINO_TTGO_LORA32_V2 awaiting pin map")
|
|
||||||
|
|
||||||
#elif defined(ARDUINO_TTGO_LoRa32_v21new) // T3_V1.6.1
|
|
||||||
#pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display")
|
|
||||||
SX1276 radio = new Module(18, 26, 14, 33);
|
|
||||||
|
|
||||||
#elif defined(ARDUINO_TBEAM_USE_RADIO_SX1262)
|
|
||||||
#pragma error ("ARDUINO_TBEAM_USE_RADIO_SX1262 awaiting pin map")
|
|
||||||
|
|
||||||
#elif defined(ARDUINO_TBEAM_USE_RADIO_SX1276)
|
|
||||||
#pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display")
|
|
||||||
SX1276 radio = new Module(18, 26, 23, 33);
|
|
||||||
|
|
||||||
|
|
||||||
// Heltec
|
|
||||||
#elif defined(ARDUINO_HELTEC_WIFI_LORA_32)
|
|
||||||
#pragma error ("ARDUINO_HELTEC_WIFI_LORA_32 awaiting pin map")
|
|
||||||
|
|
||||||
#elif defined(ARDUINO_heltec_wifi_kit_32_V2)
|
|
||||||
#pragma message ("ARDUINO_heltec_wifi_kit_32_V2 awaiting pin map")
|
|
||||||
SX1276 radio = new Module(18, 26, 14, 35);
|
|
||||||
|
|
||||||
#elif defined(ARDUINO_heltec_wifi_kit_32_V3)
|
|
||||||
#pragma message ("Using Heltec WiFi LoRa32 v3 - Display + USB-C")
|
|
||||||
SX1262 radio = new Module(8, 14, 12, 13);
|
|
||||||
|
|
||||||
#elif defined(ARDUINO_CUBECELL_BOARD)
|
|
||||||
#pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display")
|
|
||||||
SX1262 radio = new Module(RADIOLIB_BUILTIN_MODULE);
|
|
||||||
|
|
||||||
#elif defined(ARDUINO_CUBECELL_BOARD_V2)
|
|
||||||
#pragma error ("ARDUINO_CUBECELL_BOARD_V2 awaiting pin map")
|
|
||||||
|
|
||||||
|
|
||||||
#else
|
|
||||||
#pragma message ("Unknown board - no automagic pinmap available")
|
|
||||||
|
|
||||||
// SX1262 pin order: Module(NSS/CS, DIO1, RESET, BUSY);
|
|
||||||
// SX1262 radio = new Module(8, 14, 12, 13);
|
|
||||||
|
|
||||||
// SX1278 pin order: Module(NSS/CS, DIO0, RESET, DIO1);
|
|
||||||
// SX1278 radio = new Module(10, 2, 9, 3);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// Copy over the EUI's & keys in to the something that will not compile if incorrectly formatted
|
|
||||||
uint64_t joinEUI = RADIOLIB_LORAWAN_JOIN_EUI;
|
|
||||||
uint64_t devEUI = RADIOLIB_LORAWAN_DEV_EUI;
|
|
||||||
uint8_t appKey[] = { RADIOLIB_LORAWAN_APP_KEY };
|
|
||||||
uint8_t nwkKey[] = { RADIOLIB_LORAWAN_NWK_KEY };
|
|
||||||
|
|
||||||
// Create the LoRaWAN node
|
|
||||||
LoRaWANNode node(&radio, &Region, subBand);
|
|
||||||
|
|
||||||
|
|
||||||
// Helper function to display any issues
|
|
||||||
void debug(bool isFail, const __FlashStringHelper* message, int state, bool Freeze) {
|
|
||||||
if (isFail) {
|
|
||||||
Serial.print(message);
|
|
||||||
Serial.print("(");
|
|
||||||
Serial.print(state);
|
|
||||||
Serial.println(")");
|
|
||||||
while (Freeze);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to display a byte array
|
|
||||||
void arrayDump(uint8_t *buffer, uint16_t len) {
|
|
||||||
for (uint16_t c; c < len; c++) {
|
|
||||||
Serial.printf("%02X", buffer[c]);
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -11,8 +11,6 @@
|
||||||
shown here for reference.
|
shown here for reference.
|
||||||
|
|
||||||
LoRaWAN v1.1 requires the use of EEPROM (persistent storage).
|
LoRaWAN v1.1 requires the use of EEPROM (persistent storage).
|
||||||
Please refer to the 'persistent' example once you are familiar
|
|
||||||
with LoRaWAN.
|
|
||||||
Running this examples REQUIRES you to check "Resets DevNonces"
|
Running this examples REQUIRES you to check "Resets DevNonces"
|
||||||
on your LoRaWAN dashboard. Refer to the network's
|
on your LoRaWAN dashboard. Refer to the network's
|
||||||
documentation on how to do this.
|
documentation on how to do this.
|
||||||
|
@ -26,7 +24,6 @@
|
||||||
For LoRaWAN details, see the wiki page
|
For LoRaWAN details, see the wiki page
|
||||||
https://github.com/jgromes/RadioLib/wiki/LoRaWAN
|
https://github.com/jgromes/RadioLib/wiki/LoRaWAN
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
@ -38,7 +35,7 @@
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
while (!Serial); // Wait for serial to be initalised
|
while (!Serial); // Wait for serial to be initalised
|
||||||
delay(2000); // Give time to switch to the serial monitor
|
delay(5000); // Give time to switch to the serial monitor
|
||||||
Serial.println(F("\nSetup"));
|
Serial.println(F("\nSetup"));
|
||||||
|
|
||||||
int16_t state = 0; // return value for calls to RadioLib
|
int16_t state = 0; // return value for calls to RadioLib
|
||||||
|
|
|
@ -1,10 +1,33 @@
|
||||||
#include <Arduino.h>
|
/*
|
||||||
|
RadioLib LoRaWAN Starter Example
|
||||||
|
|
||||||
|
This example joins a LoRaWAN network and will send
|
||||||
|
uplink packets. Before you start, you will have to
|
||||||
|
register your device at https://www.thethingsnetwork.org/
|
||||||
|
After your device is registered, you can run this example.
|
||||||
|
The device will join the network and start uploading data.
|
||||||
|
|
||||||
|
Running this examples REQUIRES you to check "Resets DevNonces"
|
||||||
|
on your LoRaWAN dashboard. Refer to the network's
|
||||||
|
documentation on how to do this.
|
||||||
|
|
||||||
|
For default module settings, see the wiki page
|
||||||
|
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||||
|
|
||||||
|
For full API reference, see the GitHub Pages
|
||||||
|
https://jgromes.github.io/RadioLib/
|
||||||
|
|
||||||
|
For LoRaWAN details, see the wiki page
|
||||||
|
https://github.com/jgromes/RadioLib/wiki/LoRaWAN
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
while (!Serial);
|
while (!Serial);
|
||||||
|
delay(5000); // Give time to switch to the serial monitor
|
||||||
Serial.println(F("\nSetup ... "));
|
Serial.println(F("\nSetup ... "));
|
||||||
|
|
||||||
Serial.println(F("Initalise the radio"));
|
Serial.println(F("Initalise the radio"));
|
||||||
|
|
Loading…
Add table
Reference in a new issue