From ebd0dcf7c23ab4edb8eae478512e2adf07ff2bcb Mon Sep 17 00:00:00 2001 From: Gijs Noorlander Date: Mon, 17 Feb 2020 23:28:24 +0100 Subject: [PATCH] [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. --- .../ESP8266-RN2483-async.ino | 263 ++++++++++++++++++ lib/RN2483-Arduino-Library/src/rn2xx3.cpp | 9 + lib/RN2483-Arduino-Library/src/rn2xx3.h | 4 + .../src/rn2xx3_handler.cpp | 4 + .../src/rn2xx3_handler.h | 16 +- 5 files changed, 290 insertions(+), 6 deletions(-) create mode 100644 lib/RN2483-Arduino-Library/examples/ESP8266-RN2483-async/ESP8266-RN2483-async.ino diff --git a/lib/RN2483-Arduino-Library/examples/ESP8266-RN2483-async/ESP8266-RN2483-async.ino b/lib/RN2483-Arduino-Library/examples/ESP8266-RN2483-async/ESP8266-RN2483-async.ino new file mode 100644 index 000000000..27cbd99f1 --- /dev/null +++ b/lib/RN2483-Arduino-Library/examples/ESP8266-RN2483-async/ESP8266-RN2483-async.ino @@ -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 +#include + +#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); +} diff --git a/lib/RN2483-Arduino-Library/src/rn2xx3.cpp b/lib/RN2483-Arduino-Library/src/rn2xx3.cpp index 5aeace42e..240b90a37 100644 --- a/lib/RN2483-Arduino-Library/src/rn2xx3.cpp +++ b/lib/RN2483-Arduino-Library/src/rn2xx3.cpp @@ -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(); } diff --git a/lib/RN2483-Arduino-Library/src/rn2xx3.h b/lib/RN2483-Arduino-Library/src/rn2xx3.h index 0964d74b0..a39291ef1 100644 --- a/lib/RN2483-Arduino-Library/src/rn2xx3.h +++ b/lib/RN2483-Arduino-Library/src/rn2xx3.h @@ -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, diff --git a/lib/RN2483-Arduino-Library/src/rn2xx3_handler.cpp b/lib/RN2483-Arduino-Library/src/rn2xx3_handler.cpp index 8ea6f5061..dee0f504e 100644 --- a/lib/RN2483-Arduino-Library/src/rn2xx3_handler.cpp +++ b/lib/RN2483-Arduino-Library/src/rn2xx3_handler.cpp @@ -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; } diff --git a/lib/RN2483-Arduino-Library/src/rn2xx3_handler.h b/lib/RN2483-Arduino-Library/src/rn2xx3_handler.h index 1eba6607c..d80841ccc 100644 --- a/lib/RN2483-Arduino-Library/src/rn2xx3_handler.h +++ b/lib/RN2483-Arduino-Library/src/rn2xx3_handler.h @@ -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;