[RN2483] Add some example code for new RN2483 library

Since I have rewritten the entire library, I may also add some short example code to illustrate the new async features.
This commit is contained in:
Gijs Noorlander
2020-02-17 23:28:24 +01:00
parent da5c15258e
commit ebd0dcf7c2
5 changed files with 290 additions and 6 deletions
@@ -0,0 +1,263 @@
/*
* Author: G Noorlander (TD-er)
* Date: 2020-02-17
*
* Perform initialization + transfer of some data in async mode.
* This mode does allow to perform other tasks while waiting for a reply.
* In Async mode we must call the async_loop() function frequently which does
* process the state changes of the LoRa module.
*
* CHECK THE RULES BEFORE USING THIS PROGRAM!
*
* CHANGE ADDRESS!
* Change the device address, network (session) key, and app (session) key to the values
* that are registered via the TTN dashboard.
* The appropriate line is "myLora.initABP(XXX);" or "myLora.initOTAA(XXX);"
* When using ABP, it is advised to enable "relax frame count".
*
* Connect the RN2xx3 as follows:
* RN2xx3 -- ESP8266
* Uart TX -- GPIO4
* Uart RX -- GPIO5
* Reset -- GPIO15
* Vcc -- 3.3V
* Gnd -- Gnd
*
*/
#include <rn2xx3.h>
#include <SoftwareSerial.h>
#define RESET 15
SoftwareSerial mySerial(4, 5); // RX, TX !! labels on relay board is swapped !!
// create an instance of the rn2xx3 library,
// giving the software UART as stream to use,
// and using LoRa WAN
rn2xx3 myLora(mySerial);
unsigned long _timer = 0;
unsigned long _timeout = 0;
unsigned long _start_command = 0;
bool _command_sent = false;
int _message_count = 0;
void toggle_async_mode()
{
bool new_async_mode_enabled = !myLora.getAsyncMode();
myLora.setAsyncMode(new_async_mode_enabled);
Serial.print(F("Async mode: "));
Serial.println(new_async_mode_enabled ? F("enabled") : F("disabled"));
}
unsigned long time_passed_since(unsigned long start)
{
return millis() - start;
}
// Set some timeout in msec from now.
void set_timeout(unsigned long timeout)
{
_timeout = timeout;
_timer = millis();
}
// Check whether _timeout msec have passed since we called set_timeout
bool time_out_reached()
{
return time_passed_since(_timer) >= _timeout;
}
void log_time_passed(unsigned long start)
{
Serial.print(F("Time passed: "));
Serial.print(String(time_passed_since(start)));
Serial.println(F(" msec"));
}
// the setup routine runs once when you press reset:
void setup() {
// LED pin is GPIO2 which is the ESP8266's built in LED
pinMode(2, OUTPUT);
led_on();
// Open serial communications and wait for port to open:
Serial.begin(57600);
mySerial.begin(57600);
delay(1000); // wait for the arduino ide's serial console to open
Serial.println("Startup");
initialize_radio();
// transmit a startup message
myLora.tx("TTN Mapper on ESP8266 node");
led_off();
set_timeout(2000);
}
void initialize_radio()
{
mySerial.flush();
// reset RN2xx3
pinMode(RESET, OUTPUT);
digitalWrite(RESET, LOW);
delay(100);
digitalWrite(RESET, HIGH);
delay(100); // wait for the RN2xx3's startup message
Serial.println(F("Boot message:"));
Serial.println(mySerial.readBytesUntil('\n'));
mySerial.flush();
// check communication with radio
String hweui = myLora.hweui();
while (hweui.length() != 16)
{
Serial.println("Communication with RN2xx3 unsuccessful. Power cycle the board.");
Serial.println(hweui);
delay(10000);
hweui = myLora.hweui();
}
// print out the HWEUI so that we can register it via ttnctl
Serial.println("When using OTAA, register this DevEUI: ");
Serial.println(hweui);
Serial.println("RN2xx3 firmware version:");
Serial.println(myLora.sysver());
// configure your keys and join the network
Serial.println("Trying to join TTN");
bool join_result = false;
// ABP: initABP(String addr, String AppSKey, String NwkSKey);
join_result = myLora.initABP("02017201", "8D7FFEF938589D95AAD928C2E2E7E48F", "AE17E567AECC8787F749A62F5541D522");
// OTAA: initOTAA(String AppEUI, String AppKey);
// join_result = myLora.initOTAA("70B3D57ED00001A6", "A23C96EE13804963F8C2BD6285448198");
while (!join_result)
{
Serial.println("Unable to join. Are your keys correct, and do you have TTN coverage?");
delay(60000); // delay a minute before retry
join_result = myLora.init();
}
Serial.println("Successfully joined TTN");
}
bool send_message(const String& message) {
led_on();
Serial.print("TXing: ");
Serial.println(message);
unsigned long start = millis();
RN2xx3_datatypes::TX_return_type result = myLora.tx(message);
log_time_passed(start);
bool success = false;
switch (result) {
case RN2xx3_datatypes::TX_return_type::TX_FAIL:
// A TX command may fail for various reasons.
// Possible reasons:
// - not_joined
// - previous command has not yet finished (async mode)
// - Message is too long
// - Module does not reply within timeout period
// - other reasons
Serial.print(F("TX failed: "));
Serial.println(myLora.getLastError());
break;
case RN2xx3_datatypes::TX_return_type::TX_SUCCESS:
// Async mode: Command is accepted
// Default mode: No error received from module after RX2 window has passed.
Serial.println(F("TX Success"));
success = true;
break;
case RN2xx3_datatypes::TX_return_type::TX_WITH_RX:
// Async mode: Received RX is not yet known, will have to check later.
// Default mode: TX Success and something received in RX2 window.
Serial.print(F("TX Success, RX received: "));
Serial.println(myLora.getRx());
success = true;
break;
// No default: here, so the compiler will warn us
// if the enum has new cases which we do not yet handle.
}
led_off();
return success;
}
// To make it clear this is just one of the many tasks we can do,
// this is split in a separate function.
void loop_handle_LoRa_command() {
if (!time_out_reached()) {
// It is not our time yet to do stuff.
return;
}
if (!_command_sent) {
// Have not sent a TX command, try sending one.
String message = F("Hello World! (");
message += _message_count;
message += ')';
if (send_message(message)) {
++_message_count;
_start_command = millis();
_command_sent = true;
} else {
// Failed to send a message, wait for 1 second.
set_timeout(1000);
}
} else {
// A command has been sent, check if it has completed.
if (!myLora.command_finished()) {
// We still wait for the command to end....
Serial.print('.');
set_timeout(50); // Only print a dot every 50 msec.
} else {
Serial.println(F("Command finished."));
// Log how long it took
log_time_passed(_start_command);
_command_sent = false;
if (_message_count % 2 == 0) {
// Every 2 messages, toggle Async mode.
toggle_async_mode();
}
// To not overload the network, wait for 10 seconds before we send a new message.
set_timeout(10000);
}
}
}
// the loop routine runs over and over again forever:
void loop() {
if (myLora.getAsyncMode()) {
myLora.async_loop();
}
loop_handle_LoRa_command();
}
void led_on()
{
digitalWrite(2, 1);
}
void led_off()
{
digitalWrite(2, 0);
}
@@ -27,6 +27,10 @@ void rn2xx3::setAsyncMode(bool enabled) {
_rn2xx3_handler.setAsyncMode(enabled);
}
bool rn2xx3::getAsyncMode() const {
return _rn2xx3_handler.getAsyncMode();
}
bool rn2xx3::autobaud()
{
// FIXME TD-er: Must fix this, as it is not working well.
@@ -166,6 +170,11 @@ rn2xx3_handler::RN_state rn2xx3::wait_command_accepted(unsigned long timeout)
return _rn2xx3_handler.wait_command_accepted(timeout);
}
bool rn2xx3::command_finished() const
{
return _rn2xx3_handler.command_finished();
}
String rn2xx3::getRx() {
return _rn2xx3_handler.get_rx_message();
}
+4
View File
@@ -40,6 +40,8 @@ public:
*/
void setAsyncMode(bool enabled);
bool getAsyncMode() const;
/*
* Transmit the correct sequence to the rn2xx3 to trigger its autobauding feature.
* After this operation the rn2xx3 should communicate at the same baud rate than us.
@@ -208,6 +210,8 @@ public:
rn2xx3_handler::RN_state wait_command_accepted(unsigned long timeout = 10000);
bool command_finished() const;
/*
* Change the datarate at which the RN2xx3 transmits.
* A value of between 0 and 5 can be specified,
@@ -456,6 +456,10 @@ void rn2xx3_handler::setAsyncMode(bool enabled) {
_asyncMode = enabled;
}
bool rn2xx3_handler::getAsyncMode() const {
return _asyncMode;
}
bool rn2xx3_handler::useOTAA() const {
return _otaa;
}
@@ -52,13 +52,17 @@ public:
// It will return when the state waiting for reply_received_rx2 has been reached, or the command has finished (due to error)
RN_state wait_command_accepted(unsigned long timeout = 10000);
// Check whether a command has finished.
bool command_finished() const;
/*
* Initialise the RN2xx3 and join the LoRa network (if applicable).
* This function can only be called after calling initABP() or initOTAA().
* The sole purpose of this function is to re-initialise the radio if it
* is in an unknown state.
*/
bool init();
bool init();
/*
* Initialise the RN2xx3 and join a network using personalization.
@@ -70,9 +74,9 @@ public:
* NwkSKey: Network Session Key as a HEX string.
* Example "AE17E567AECC8787F749A62F5541D522"
*/
bool initABP(const String& addr,
const String& AppSKey,
const String& NwkSKey);
bool initABP(const String& addr,
const String& AppSKey,
const String& NwkSKey);
// TODO: initABP(uint8_t * addr, uint8_t * AppSKey, uint8_t * NwkSKey)
@@ -128,6 +132,8 @@ public:
void setAsyncMode(bool enabled);
bool getAsyncMode() const;
bool useOTAA() const;
void setLastUsedJoinMode(bool isOTAA);
@@ -151,8 +157,6 @@ public:
private:
bool command_finished() const;
// Return the data to send
const String& get_send_data() const;