├── library.properties ├── examples ├── MLR003-simulation │ ├── README.md │ ├── config_application.h │ └── MLR003-simulation.ino ├── LoRaWAN_Class_A │ ├── config_application.h │ └── LoRaWAN_Class_A.ino ├── LoRaWAN_Class_C │ ├── config_application.h │ └── LoRaWAN_Class_C.ino └── LoRaWAN_State-Machine │ ├── config_application.h │ └── LoRaWAN_State-Machine.ino ├── CHANGELOG.md ├── src ├── lorae5.h └── lorae5.cpp └── README.md /library.properties: -------------------------------------------------------------------------------- 1 | name=LoRaWAN-Seeed-Grove-Wio-E5 2 | version=2.0.1 3 | author=Sylvain Montagny,sylvain.montagny@univ-smb.fr 4 | maintainer=Savoie Mont Blanc University 5 | sentence=Sending LoRaWAN frame with a Grove Wio E5 module. 6 | paragraph=This library proposes a simpler access to the LoRa-E5 module (LoRaE5). It handles uplinks, downlinks, ABP, OTAA, confirmed, unconfirmed, and port selection. EU868 and US915. 7 | category=Communication 8 | url=https://github.com/SylvainMontagny/LoRaE5 9 | architectures=Leonardo, Zero -------------------------------------------------------------------------------- /examples/MLR003-simulation/README.md: -------------------------------------------------------------------------------- 1 | # MLR003 simulator 2 | This program simulates a MLR003 micropelt thermostatic valve. 3 | 4 | ## Payload decoder 5 | - 1st byte : Setpoint. Default value 20°C. Set by downlink. 6 | - 2de byte : Ambient temperature. From 15°C to 25°C up and down. 7 | 8 | You can use this uplink payload decoder in your favorite Network Server: 9 | 10 | ```plaintext 11 | function decodeUplink(input) { 12 | return { 13 | data: { 14 | setpoint: input.bytes[0], 15 | temperature: input.bytes[1] 16 | }, 17 | warnings: [], 18 | errors: [] 19 | }; 20 | } 21 | ``` 22 | 23 | ## Changing the setpoint 24 | The Setpoint can be overwritten using a 1 byte downlink. 25 | -------------------------------------------------------------------------------- /examples/LoRaWAN_Class_A/config_application.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* Please see README page on https://github.com/SylvainMontagny/LoRaE5 */ 3 | /***********************************************************************/ 4 | 5 | #define REGION EU868 6 | #define ACTIVATION_MODE ABP 7 | #define CLASS CLASS_A 8 | #define SPREADING_FACTOR 7 9 | #define ADAPTIVE_DR false 10 | #define CONFIRMED false 11 | #define PORT_UP 15 12 | 13 | #define SEND_BY_PUSH_BUTTON false 14 | #define FRAME_DELAY 20000 15 | 16 | String devEUI = "0000000000000000"; 17 | 18 | // Configuration for ABP Activation Mode 19 | String devAddr = "00000000"; 20 | String nwkSKey = "00000000000000000000000000000000"; 21 | String appSKey = "00000000000000000000000000000000"; 22 | 23 | // Configuration for OTAA Activation Mode 24 | String appKey = "00000000000000000000000000000000"; 25 | String appEUI = "0000000000000000"; 26 | -------------------------------------------------------------------------------- /examples/LoRaWAN_Class_C/config_application.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* Please see README page on https://github.com/SylvainMontagny/LoRaE5 */ 3 | /***********************************************************************/ 4 | 5 | #define REGION EU868 6 | #define ACTIVATION_MODE ABP 7 | #define CLASS CLASS_C 8 | #define SPREADING_FACTOR 7 9 | #define ADAPTIVE_DR false 10 | #define CONFIRMED false 11 | #define PORT_UP 15 12 | 13 | #define SEND_BY_PUSH_BUTTON false 14 | #define FRAME_DELAY 20000 15 | 16 | String devEUI = "0000000000000000"; 17 | 18 | // Configuration for ABP Activation Mode 19 | String devAddr = "00000000"; 20 | String nwkSKey = "00000000000000000000000000000000"; 21 | String appSKey = "00000000000000000000000000000000"; 22 | 23 | // Configuration for OTAA Activation Mode 24 | String appKey = "00000000000000000000000000000000"; 25 | String appEUI = "0000000000000000"; 26 | -------------------------------------------------------------------------------- /examples/MLR003-simulation/config_application.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* Please see README page on https://github.com/SylvainMontagny/LoRaE5 */ 3 | /***********************************************************************/ 4 | 5 | #define REGION EU868 6 | #define ACTIVATION_MODE ABP 7 | #define CLASS CLASS_A 8 | #define SPREADING_FACTOR 7 9 | #define ADAPTIVE_DR false 10 | #define CONFIRMED false 11 | #define PORT_UP 15 12 | 13 | #define SEND_BY_PUSH_BUTTON false 14 | #define FRAME_DELAY 20000 15 | 16 | String devEUI = "0000000000000000"; 17 | 18 | // Configuration for ABP Activation Mode 19 | String devAddr = "00000000"; 20 | String nwkSKey = "00000000000000000000000000000000"; 21 | String appSKey = "00000000000000000000000000000000"; 22 | 23 | // Configuration for OTAA Activation Mode 24 | String appKey = "00000000000000000000000000000000"; 25 | String appEUI = "0000000000000000"; 26 | -------------------------------------------------------------------------------- /examples/LoRaWAN_State-Machine/config_application.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* Please see README page on https://github.com/SylvainMontagny/LoRaE5 */ 3 | /***********************************************************************/ 4 | 5 | #define REGION EU868 6 | #define ACTIVATION_MODE ABP 7 | #define CLASS CLASS_A 8 | #define SPREADING_FACTOR 7 9 | #define ADAPTIVE_DR false 10 | #define CONFIRMED false 11 | #define PORT_UP 15 12 | 13 | #define SEND_BY_PUSH_BUTTON false 14 | #define FRAME_DELAY 20000 15 | 16 | String devEUI = "0000000000000000"; 17 | 18 | // Configuration for ABP Activation Mode 19 | String devAddr = "00000000"; 20 | String nwkSKey = "00000000000000000000000000000000"; 21 | String appSKey = "00000000000000000000000000000000"; 22 | 23 | // Configuration for OTAA Activation Mode 24 | String appKey = "00000000000000000000000000000000"; 25 | String appEUI = "0000000000000000"; 26 | -------------------------------------------------------------------------------- /examples/LoRaWAN_Class_A/LoRaWAN_Class_A.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "lorae5.h" 3 | #include "config_application.h" 4 | 5 | uint8_t sizePayloadUp = 4; 6 | uint8_t sizePayloadDown = 0; 7 | 8 | uint8_t payloadUp[20] = {0x00, 0x01, 0x02, 0x03}; 9 | uint8_t payloadDown[20] ={0}; 10 | 11 | LORAE5 lorae5(devEUI, appEUI, appKey, devAddr, nwkSKey, appSKey); 12 | 13 | /***********************************************************************/ 14 | /* Please see README page on https://github.com/SylvainMontagny/LoRaE5 */ 15 | /***********************************************************************/ 16 | 17 | void setup() { 18 | lorae5.setup(REGION, ACTIVATION_MODE, CLASS, SPREADING_FACTOR, ADAPTIVE_DR, CONFIRMED, PORT_UP, SEND_BY_PUSH_BUTTON, FRAME_DELAY); 19 | lorae5.printInfo(); 20 | 21 | if(ACTIVATION_MODE == OTAA){ 22 | USB_Serial.println("Join Procedure in progress..."); 23 | while(lorae5.join() == false); 24 | delay(2000); 25 | } 26 | } 27 | 28 | void loop() { 29 | // Send data 30 | lorae5.sendData(payloadUp, sizePayloadUp); 31 | 32 | // Check downlink on RX1-RX2 and call processDownlink() 33 | if (lorae5.awaitForDownlinkClass_A(payloadDown, &sizePayloadDown) == RET_DOWNLINK){ 34 | processDownlink(); 35 | }; 36 | 37 | // Wait in Low Power mode 38 | lorae5.sleep(); 39 | } 40 | 41 | void processDownlink() { 42 | // You have received "sizePayloadDown" bytes stored in the table "payloadDown" 43 | } -------------------------------------------------------------------------------- /examples/LoRaWAN_Class_C/LoRaWAN_Class_C.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "lorae5.h" 3 | #include "config_application.h" 4 | 5 | uint8_t sizePayloadUp = 4; 6 | uint8_t sizePayloadDown = 0; 7 | 8 | uint8_t payloadUp[20] = {0x00, 0x01, 0x02, 0x03}; 9 | uint8_t payloadDown[20] ={0}; 10 | 11 | LORAE5 lorae5(devEUI, appEUI, appKey, devAddr, nwkSKey, appSKey); 12 | 13 | /***********************************************************************/ 14 | /* Please see README page on https://github.com/SylvainMontagny/LoRaE5 */ 15 | /***********************************************************************/ 16 | 17 | void setup() { 18 | lorae5.setup(REGION, ACTIVATION_MODE, CLASS, SPREADING_FACTOR, ADAPTIVE_DR, CONFIRMED, PORT_UP, SEND_BY_PUSH_BUTTON, FRAME_DELAY); 19 | lorae5.printInfo(); 20 | 21 | if(ACTIVATION_MODE == OTAA){ 22 | USB_Serial.println("Join Procedure in progress..."); 23 | while(lorae5.join() == false); 24 | delay(2000); 25 | } 26 | } 27 | 28 | void loop() { 29 | // Send data 30 | lorae5.sendData(payloadUp, sizePayloadUp); 31 | 32 | // Check downlink on RX1-RX2 and call processDownlink() 33 | if (lorae5.awaitForDownlinkClass_A(payloadDown, &sizePayloadDown) == RET_DOWNLINK){ 34 | processDownlink(); 35 | }; 36 | 37 | // Check downlink on RXC and call processDownlink() 38 | if (lorae5.awaitForDownlinkClass_C(payloadDown, &sizePayloadDown) == RET_DOWNLINK){ 39 | processDownlink(); 40 | }; 41 | } 42 | 43 | void processDownlink() { 44 | // You have received "sizePayloadDown" bytes stored in the table "payloadDown" 45 | } -------------------------------------------------------------------------------- /examples/MLR003-simulation/MLR003-simulation.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "lorae5.h" 3 | #include "config_application.h" 4 | 5 | uint8_t sizePayloadUp = 2; 6 | uint8_t sizePayloadDown = 0; 7 | 8 | // payloadUp[0] = setpoint 9 | // payloadUp[1] = temperature 10 | uint8_t payloadUp[2] = {20, 20}; 11 | uint8_t payloadDown[50] ={0}; 12 | bool up = true; 13 | 14 | LORAE5 lorae5(devEUI, appEUI, appKey, devAddr, nwkSKey, appSKey); 15 | 16 | /***********************************************************************/ 17 | /* Please see README page on https://github.com/SylvainMontagny/LoRaE5 */ 18 | /***********************************************************************/ 19 | 20 | void setup() { 21 | lorae5.setup(REGION, ACTIVATION_MODE, CLASS, SPREADING_FACTOR, ADAPTIVE_DR, CONFIRMED, PORT_UP, SEND_BY_PUSH_BUTTON, FRAME_DELAY); 22 | lorae5.printInfo(); 23 | 24 | if(ACTIVATION_MODE == OTAA){ 25 | USB_Serial.println("Join Procedure in progress..."); 26 | while(lorae5.join() == false); 27 | delay(2000); 28 | } 29 | } 30 | 31 | void loop(){ 32 | // Send data 33 | lorae5.sendData(payloadUp, sizePayloadUp); 34 | 35 | // Check downlink and call processDownlink() 36 | if ( lorae5.awaitForDownlinkClass_A(payloadDown, &sizePayloadDown) == RET_DOWNLINK ){ 37 | payloadUp[0]=payloadDown[0]; 38 | processDownlink(); 39 | } 40 | 41 | (up==true)? payloadUp[1]+=1 : payloadUp[1]-=1; 42 | if ((payloadUp[1] == 15) or (payloadUp[1] == 25)) up = !up; 43 | 44 | // Wait for the next transmission 45 | lorae5.sleep(); 46 | } 47 | 48 | void processDownlink() { 49 | // You have received "sizePayloadDown" bytes stored in the table "payloadDown" 50 | } -------------------------------------------------------------------------------- /examples/LoRaWAN_State-Machine/LoRaWAN_State-Machine.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "lorae5.h" 3 | #include "config_application.h" 4 | 5 | uint8_t sizePayloadUp = 4; 6 | uint8_t sizePayloadDown = 0; 7 | 8 | uint8_t ret; 9 | 10 | uint8_t payloadUp[20] = {0x00, 0x01, 0x02, 0x03}; 11 | uint8_t payloadDown[20] ={0}; 12 | 13 | LORAE5 lorae5(devEUI, appEUI, appKey, devAddr, nwkSKey, appSKey); 14 | 15 | enum class State { SEND_DATA, AWAIT_FOR_DOWNLINK_CLASS_A, AWAIT_FOR_DOWNLINK_CLASS_C, SLEEP, PROCESS_DOWNLINK}; 16 | 17 | State currentState = State::SEND_DATA; 18 | 19 | /***********************************************************************/ 20 | /* Please see README page on https://github.com/SylvainMontagny/LoRaE5 */ 21 | /***********************************************************************/ 22 | 23 | void setup() { 24 | lorae5.setup(REGION, ACTIVATION_MODE, CLASS, SPREADING_FACTOR, ADAPTIVE_DR, CONFIRMED, PORT_UP, SEND_BY_PUSH_BUTTON, FRAME_DELAY); 25 | lorae5.printInfo(); 26 | 27 | if(ACTIVATION_MODE == OTAA){ 28 | USB_Serial.println("Join Procedure in progress..."); 29 | while(lorae5.join() == false); 30 | delay(2000); 31 | } 32 | } 33 | 34 | void loop() { 35 | switch (currentState) { 36 | case State::SEND_DATA: 37 | lorae5.sendData(payloadUp, sizePayloadUp); 38 | currentState = State::AWAIT_FOR_DOWNLINK_CLASS_A; 39 | break; 40 | 41 | case State::AWAIT_FOR_DOWNLINK_CLASS_A: { 42 | 43 | ret = lorae5.awaitForDownlinkClass_A(payloadDown, &sizePayloadDown); 44 | 45 | switch (ret) { 46 | case RET_DOWNLINK: 47 | currentState = State::PROCESS_DOWNLINK; 48 | break; 49 | 50 | case RET_NO_DOWNLINK: 51 | currentState = State::PROCESS_DOWNLINK; 52 | break; 53 | 54 | case RET_NO_ACK: 55 | currentState = State::SEND_DATA; 56 | break; 57 | } 58 | break; 59 | } 60 | 61 | case State::AWAIT_FOR_DOWNLINK_CLASS_C: { 62 | 63 | ret = lorae5.awaitForDownlinkClass_C(payloadDown, &sizePayloadDown); 64 | 65 | switch (ret) { 66 | case RET_DOWNLINK: 67 | currentState = State::PROCESS_DOWNLINK; 68 | break; 69 | 70 | case RET_T: 71 | currentState = State::SEND_DATA; 72 | break; 73 | 74 | case RET_TIMEOUT: 75 | currentState = State::SEND_DATA; 76 | break; 77 | } 78 | break; 79 | } 80 | 81 | case State::PROCESS_DOWNLINK: 82 | processDownlink(); 83 | currentState = State::SLEEP; 84 | break; 85 | 86 | case State::SLEEP: 87 | lorae5.sleep(); 88 | currentState = State::SEND_DATA; 89 | break; 90 | 91 | } 92 | } 93 | 94 | void processDownlink() { 95 | // You have received "sizePayloadDown" bytes stored in the table "payloadDown" 96 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | #### File: LoRaE5 3 | #### Author: Sylvain Montagny & Dorian Desserey 4 | 5 | 6 | ## Knows limitation 7 | - Tested on "EU868" & "US915". 8 | - Tested compatibility only with "Leonardo" and "Zero" Arduino boards. 9 | - "LoRaWAN modem is busy" message may occurs for Class C/ABP. 10 | 11 | 12 | ## Version: V2.0.0 | 2024-04-22 13 | 14 | ### Added 15 | - Implement a awaitForDownlinkClass_A() & awaitForDownlinkClass_C() method. 16 | - Implement a getPortDown() method. 17 | - Implement a getPayloadDown() method. 18 | - Implement a getClassDownlink() method. 19 | - Implement a sleep() method. 20 | - Implement a setFrequencyBand() method. 21 | - Define a new parameter "REGION". 22 | - "LoRaWAN_Class_A.ino", "LoRaWAN_Class_C" & "LoRaWAN_State-Machine.ino". 23 | - Doxygen standard written for all functions. 24 | - Ultra-low power consumption of the LoRaE5 module when sleep() function is called. 25 | 26 | ### Modified 27 | - Rename "displayPayloadUp()" by "sendPayloadUp()". 28 | - In sendData() functions with 2 parameters, rename "done = 1" by "done = true". 29 | - Move "Send_By_Push_Button" variable from each function parameters to setup() function. 30 | - Arrange functions in header (private or public). 31 | - getSetSF() & setSF() to match with US915 region. 32 | - Add "REGION" parameter in setup. 33 | 34 | ### Removed 35 | - SendData() function with 4 parameters. 36 | 37 | 38 | ## Version: V1.2.0 | 2024-03-22 39 | 40 | ### Added 41 | - Implement a CLASS_C mode. 42 | - Implement a setClass() method. 43 | - Implement a getClass() method. 44 | - Display the downlink C frame to the user. 45 | 46 | ### Modified 47 | - Update of printInfo(). 48 | 49 | 50 | ## Version: V1.1.1 | 2024-03-08 51 | 52 | ### Added 53 | - Implement a method keyboard Input "t" for Uplink Initiation. 54 | - Improved program stability by fixing "Length error 0" error. 55 | 56 | ### Modified 57 | - Update the access specifier of the displayPayloadUp function from public to private. 58 | - Update of all blank function (func() by func(void)). 59 | - Error correction of uplink payload value sent to server. 60 | 61 | ### Removed 62 | - Unused sendMsg() function. 63 | 64 | 65 | ## Version: V1.1.0 | 2024-03-07 66 | 67 | ### Added 68 | - Implement a new method named getPortDown(). 69 | - Implement an overloaded version of the sendData() method. 70 | 71 | ### Modified 72 | - Replace all occurrences of "port" with "portUp”. 73 | - Replace all occurrences of getPort() with getPortUp(). 74 | - Replace all occurrences of setPort() with setPortUp(). 75 | - Update both versions of the sendData() methods to use the displayPayloadUp() function for displaying the "Up" payload. 76 | 77 | 78 | ## Version: V1.0.0 | 2024-02-19 79 | 80 | ### First version features: 81 | 82 | #### Send and receive frames (Uplink/Downlink): 83 | - Uplink : Send data to the network server. 84 | - Downlink : Receive data from the network server. 85 | 86 | #### Choose your transmission mode (ABP/OTAA): 87 | - ABP : Activation by Personalization. 88 | - OTAA : Over-the-Air Activation. 89 | 90 | #### Supported class: 91 | - Class A, suitable for battery-powered devices. 92 | 93 | #### Differents types of frames (Confirmed/Unconfirmed): 94 | - Confirmed : The network server acknowledges messages. 95 | - Unconfirmed : The network server does not acknowledge messages. 96 | 97 | #### Adaptive Data Rate activation choice (ON/OFF): 98 | - Adjusts the data transmission rate. 99 | - Optimize energy consumption and reliability. 100 | 101 | #### Spreading Factor (SF): 102 | - Controls how wide you "spread" your signal. 103 | -------------------------------------------------------------------------------- /src/lorae5.h: -------------------------------------------------------------------------------- 1 | #include 2 | #define USB_Serial Serial 3 | #define LoRa_Serial Serial1 4 | 5 | #define EU868 0 6 | #define US915 1 7 | 8 | #define ABP 0 9 | #define OTAA 1 10 | 11 | #define CLASS_A 0 12 | #define CLASS_B 1 13 | #define CLASS_C 2 14 | 15 | #define DEBUG true 16 | #define NO_DEBUG false 17 | 18 | #define DEBUG_LEVEL 0 19 | /* list for DEBUG_LEVEL: 20 | - join : 1; 21 | - getSetSF : 2; 22 | - sendData : 3; 23 | - awaitForDownlink : 4; 24 | - setFrequencyBand : 5; 25 | */ 26 | 27 | #define CONF true 28 | #define UNCONF false 29 | 30 | #define _HEXA 0 31 | #define _STRING 1 32 | 33 | enum RETURN { RET_DOWNLINK, RET_NO_DOWNLINK, RET_NO_ACK, RET_T, RET_TIMEOUT }; 34 | 35 | class LORAE5{ 36 | 37 | public: 38 | // uint8_t downlink[50]; 39 | 40 | private: 41 | uint8_t region; 42 | bool mode; 43 | uint8_t devClass; 44 | uint8_t sf; 45 | bool adr; 46 | bool confirmed; 47 | uint8_t portUp; 48 | uint8_t portDown; 49 | 50 | String devEUI; 51 | String devAddr; 52 | String nwkSKey; 53 | String appSKey; 54 | String appKey; 55 | String appEUI; 56 | 57 | bool SEND_BY_PUSH_BUTTON; 58 | uint32_t FRAME_DELAY; 59 | 60 | void readResponse(uint32_t timeOut, bool debug); 61 | bool checkResponse(uint32_t timeOut, char *strCheck, bool debug); 62 | bool checkBoard(void); 63 | 64 | void setDevEUI(String deveui); 65 | void setAppKey(String appkey); 66 | void setAppEUI(String appeui); 67 | void setDevAddr(String devaddr); 68 | void setNwkSKey(String nwkskey); 69 | void setAppSKey(String appskey); 70 | void setMode(bool mode); 71 | void setClass(uint8_t devClass); 72 | void setRXDelay(); 73 | void setSF(uint8_t sf); 74 | void setADR(bool adr); 75 | void setPortUp(uint8_t portUp); 76 | void setFrequencyBand(void); 77 | 78 | void getMode(void); 79 | void getSetSF(void); 80 | void getPortDown(String response); 81 | void getPayloadDown(uint8_t* payloadDown, uint8_t* sizePayloadDown, String response); 82 | void getChannelDown(String response); 83 | uint8_t getADR(void); 84 | uint8_t getPortUp(void); 85 | uint8_t getClass(void); 86 | 87 | void sendPayloadUp(uint8_t* payloadUp, uint8_t sizePayloadUp); 88 | 89 | public: 90 | LORAE5(String devEUI, String appEUI, String appKey, String devAddr, String nwkSKey, String appSKey); 91 | void setup(uint8_t band, bool mode, uint8_t devClass, uint8_t sf, bool adr, bool messageType, uint8_t portUp, bool SEND_BY_PUSH_BUTTON, uint32_t FRAME_DELAY, uint8_t RX_pin, uint8_t TX_pin); 92 | void printInfo(); 93 | 94 | bool join(void); 95 | 96 | void sendData(uint8_t* payloadUp, uint8_t sizePayloadUp); 97 | uint8_t awaitForDownlinkClass_A(uint8_t* payloadDown, uint8_t* sizePayloadDown); 98 | uint8_t awaitForDownlinkClass_C(uint8_t* payloadDown, uint8_t* sizePayloadDown); 99 | uint8_t sleep(void); 100 | 101 | }; 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 1. Learn more about LoRaWAN 2 | 3 | Savoie Mont Blanc University provides many educationnal ressources about LoRaWAN: 4 | 5 | :notebook: One free ebook available here: [LoRaWAN for beginners books](https://www.univ-smb.fr/lorawan/en/free-book/) 6 | 7 | :tv: E-learning platform available here: [LoRaWAN for beginners videos](https://www.udemy.com/course/lora-lorawan-internet-of-things/?referralCode=21DED0F1021F4E261955) 8 | 9 | :bulb: 2 days training sessions available here: [LoRaWAN and IoT Training](https://www.univ-smb.fr/lorawan/avada_portfolio/formation-distanciel/) 10 | 11 | 12 | # 2. About this Arduino library 13 | 14 | This Arduino Library communicates with the [Grove Wio E5 LoRa-E5](https://wiki.seeedstudio.com/Grove_LoRa_E5_New_Version/). The configuration is made thanks to a unique file called config_application. Here are the possible configuration : 15 | 16 | | #define | Possible values | Behavior | 17 | |---------------------- |-------------------- |---------------------------------------------------------------| 18 | | REGION | EU868 or US915 | Region | 19 | | ACTIVATION_MODE | ABP or OTAA | Activation Method | 20 | | CLASS | CLASS_A or CLASS_C | Class | 21 | | SPREADING_FACTOR | Number [7;12] | 7 = SF7, 8 = SF8, ..., 12 = SF12 | 22 | | ADAPTIVE_DR | true or false | Enable Adaptive Data Rate (if true) | 23 | | CONFIRMED | true or false | Frame Confirmed (if true) OR Frame Unconfirmed (if false) | 24 | | PORT_UP | Number [0;255] | Application Port number | 25 | | SEND_BY_PUSH_BUTTON | true or false | Sending method (Time or Push Button) | 26 | | FRAME_DELAY | Time (ms) | Time between 2 frames (Minimum 7000) | 27 | 28 | :warning: **Notice** : 29 | - **EU868**: The Spreading Factor is between 7 & 12 30 | - **US915**: The Spreading Factor is between 7 & 10 31 | 32 | This library has only been tested with an **Arduino Leonardo** and an **Arduino Zero** boards. 33 | 34 | 35 | ## 2.1. Arduino IDE Installation 36 | 37 | - Install Arduino IDE from the [Arduino Website](https://www.arduino.cc/) 38 | - In Arduino IDE, go to **tools > Manage Librairies...** or **Ctrl + Shift + I** 39 | - Write in the section **Filter your search...** : "lora e5". 40 | - Choose your desired version and then click on **Install**. 41 | 42 | 43 | ## 2.2. Arduino Hardware 44 | 45 | This library works with any Arduino boards with two serial ports (or one USB + one Serial). It has been tested on an Arduino **Leonardo & Zero** boards with: 46 | - USB_Serial: Connection to computer: 115200 baud. 47 | - LoRa_Serial: Serial link for the communication between the Arduino MCU and the LoRa-E5 LoRaWAN module. 48 | 49 | Your LoRa-E5 module need to be connected to the RX-TX arduino header pin. 50 | 51 | 52 | ## 2.3. How to use this library 53 | 54 | - Set up the parameters in **config_application.h** file. If you use ABP, you need to configure devAdddr, nwkSKey and appSKey. If you use OTAA you need to configure devEUI, appEUI and appKey. 55 | - Set up your Gateway or use a public coverage. 56 | - Register your Device on a Network Server (TTN, Actility, LORIOT, ...) 57 | - Open the Serial Monitor in the arduino IDE to see the logs. :warning: 115200 baud. 58 | 59 | 60 | ## 2.4. How to add sensors 61 | 62 | - Any sensors can be added to the free I2C bus, analog pins and GPIOs. 63 | 64 | 65 | ## 2.5. Best practices 66 | 67 | - It is recommended to opt for OTAA over ABP for the Activation Mode. 68 | -------------------------------------------------------------------------------- /src/lorae5.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "lorae5.h" 3 | 4 | LORAE5::LORAE5(String devEUI, String appEUI, String appKey, String devAddr, String nwkSKey, String appSKey){ 5 | this->devEUI = devEUI; 6 | this->appEUI = appEUI; 7 | this->appKey = appKey; 8 | this->devAddr = devAddr; 9 | this->nwkSKey = nwkSKey; 10 | this->appSKey = appSKey; 11 | } 12 | 13 | /// @brief Allow to set all the setup for our transmission 14 | /// @param region is the region selected by the user 15 | /// @param mode is the mode selected by the user 16 | /// @param devClass is the class of the device selected by the user 17 | /// @param sf is the Spreading Factor selected by the user 18 | /// @param adr is to enable or disable the Adaptive Data Rate 19 | /// @param confirmed is to enable or disable the confirmed message 20 | /// @param portUp is the uplink port selected by the user 21 | /// @param SEND_BY_PUSH_BUTTON is the push button condition selected by the user 22 | /// @param FRAME_DELAY is the frame delay selected by the user 23 | void LORAE5::setup(uint8_t region, bool mode, uint8_t devClass, uint8_t sf, bool adr, bool confirmed, uint8_t portUp, bool SEND_BY_PUSH_BUTTON, uint32_t FRAME_DELAY,uint8_t RX_pin, uint8_t TX_pin){ 24 | unsigned long startTime = millis(); 25 | LoRa_Serial.begin(9600, SERIAL_8N1, RX_pin, TX_pin); 26 | USB_Serial.begin(115200); 27 | while (!USB_Serial && (millis() - startTime < 5000)); 28 | 29 | while(!checkBoard()); 30 | 31 | setDevEUI(devEUI); 32 | setAppEUI(appEUI); 33 | setAppKey(appKey); 34 | 35 | setDevAddr(devAddr); 36 | setNwkSKey(nwkSKey); 37 | setAppSKey(appSKey); 38 | 39 | this->region = region; 40 | 41 | setRXDelay(); 42 | setFrequencyBand(); 43 | setMode(mode); 44 | setClass(devClass); 45 | setSF(sf); 46 | setADR(adr); 47 | setPortUp(portUp); 48 | 49 | this->confirmed = confirmed; 50 | this->SEND_BY_PUSH_BUTTON = SEND_BY_PUSH_BUTTON; 51 | this->FRAME_DELAY = FRAME_DELAY; 52 | } 53 | 54 | /// @brief Allow to set the RX delay 55 | void LORAE5::setRXDelay(){ 56 | readResponse(1000,NO_DEBUG); 57 | LoRa_Serial.println("AT+DELAY=RX1,1000"); 58 | readResponse(400,NO_DEBUG); 59 | LoRa_Serial.println("AT+DELAY=RX2,2000"); 60 | readResponse(400,NO_DEBUG); 61 | } 62 | 63 | /// @brief Allow to set the mode of the device 64 | /// @param mode is the mode selected by the user 65 | void LORAE5::setMode(bool mode){ 66 | String strMode; 67 | 68 | this->mode = mode; 69 | if (mode == OTAA) strMode = "LWOTAA"; 70 | if (mode == ABP) strMode = "LWABP"; 71 | 72 | LoRa_Serial.println("AT+MODE=" + strMode); 73 | readResponse(400,NO_DEBUG); 74 | } 75 | 76 | /// @brief Allow to set the class of the device 77 | /// @param devClass is the the class of the device selected by the user 78 | void LORAE5::setClass(uint8_t devClass){ 79 | String strClass; 80 | 81 | this->devClass = devClass; 82 | if (devClass == CLASS_A) strClass = "A"; 83 | if (devClass == CLASS_C) strClass = "C"; 84 | LoRa_Serial.println("AT+CLASS=" + strClass); 85 | readResponse(400,NO_DEBUG); 86 | } 87 | 88 | /// @brief Allow to set the Spreading Factor (SF) of the device 89 | /// @param sf is the Spreading factor selected by the user 90 | void LORAE5::setSF(uint8_t sf){ 91 | this->sf = sf; 92 | if ((this->region) == EU868){ 93 | LoRa_Serial.println("AT+DR=DR" + String(-sf+12)); 94 | } 95 | if ((this->region) == US915){ 96 | LoRa_Serial.println("AT+DR=DR" + String(-sf+10)); 97 | } 98 | 99 | String response = LoRa_Serial.readStringUntil('\n'); 100 | readResponse(400,NO_DEBUG); 101 | } 102 | 103 | /// @brief Allow to process and set the Spreading Factor (SF) received from the downlink 104 | void LORAE5::getSetSF(){ 105 | String response; 106 | LoRa_Serial.setTimeout(500); 107 | LoRa_Serial.println("AT+DR"); 108 | response = LoRa_Serial.readStringUntil('\n'); 109 | #if DEBUG_LEVEL == 2 110 | USB_Serial.println(response); // DEBUG 111 | #endif 112 | if (this->adr == true){ 113 | response = LoRa_Serial.readStringUntil('\n'); 114 | #if DEBUG_LEVEL == 2 115 | USB_Serial.println(response); // DEBUG 116 | #endif 117 | } 118 | 119 | if ((this->region) == EU868){ 120 | if ( response.indexOf("DR0") != -1 ) this->sf = 12; 121 | else if ( response.indexOf("DR1") != -1 ) this->sf = 11; 122 | else if ( response.indexOf("DR2") != -1 ) this->sf = 10; 123 | else if ( response.indexOf("DR3") != -1 ) this->sf = 9; 124 | else if ( response.indexOf("DR4") != -1 ) this->sf = 8; 125 | else if ( response.indexOf("DR5") != -1 ) this->sf = 7; 126 | } 127 | 128 | if ((this->region) == US915){ 129 | if ( response.indexOf("DR0") != -1 ) this->sf = 10; 130 | else if ( response.indexOf("DR1") != -1 ) this->sf = 9; 131 | else if ( response.indexOf("DR2") != -1 ) this->sf = 8; 132 | else if ( response.indexOf("DR3") != -1 ) this->sf = 7; 133 | } 134 | 135 | readResponse(100,NO_DEBUG); 136 | } 137 | 138 | /// @brief Allow to set Adaptative Data Rate (ADR) 139 | /// @param adr is to enable or disable the Adaptive Data Rate 140 | void LORAE5::setADR(bool adr){ 141 | this->adr = adr; 142 | LoRa_Serial.println("AT+ADR=" + ((adr == true)? String("ON") : String("OFF"))); 143 | readResponse(400,NO_DEBUG); 144 | } 145 | 146 | /// @brief Allow to set Device EUI 147 | /// @param deveui is the Device EUI 148 | void LORAE5::setDevEUI(String deveui){ 149 | LoRa_Serial.println("AT+ID=DEVEUI," + devEUI); 150 | readResponse(400,NO_DEBUG); 151 | } 152 | 153 | /// @brief Allow to set Application EUI 154 | /// @param appeui is the Application EUI 155 | void LORAE5::setAppEUI(String appeui){ 156 | LoRa_Serial.println("AT+ID=APPEUI," + appeui); 157 | readResponse(400,NO_DEBUG); 158 | } 159 | 160 | /// @brief Allow to set Application Key 161 | /// @param appkey is the Application Key 162 | void LORAE5::setAppKey(String appkey){ 163 | LoRa_Serial.println("AT+KEY=APPKEY," + appkey); 164 | readResponse(400,NO_DEBUG); 165 | } 166 | 167 | /// @brief Allow to set Device Address 168 | /// @param devaddr is the Device Address 169 | void LORAE5::setDevAddr(String devaddr){ 170 | LoRa_Serial.println("AT+ID=DEVADDR," + devaddr); 171 | readResponse(400,NO_DEBUG); 172 | } 173 | 174 | /// @brief Allow to set Network Session Key 175 | /// @param nwkskey is the Network Session Key 176 | void LORAE5::setNwkSKey(String nwkskey){ 177 | LoRa_Serial.println("AT+KEY=NWKSKEY," + nwkskey); 178 | readResponse(400,NO_DEBUG); 179 | } 180 | 181 | /// @brief Allow to set Application Session Key 182 | /// @param appskey is the Application Session Key 183 | void LORAE5::setAppSKey(String appskey){ 184 | LoRa_Serial.println("AT+KEY=APPSKEY," + appskey); 185 | readResponse(400,NO_DEBUG); 186 | } 187 | 188 | /// @brief Allow to set the uplink port 189 | /// @param portUp is the uplink port selected by the user 190 | void LORAE5::setPortUp(uint8_t portUp){ 191 | this->portUp = portUp; 192 | LoRa_Serial.println("AT+PORT=" + String(portUp)); 193 | readResponse(400,NO_DEBUG); 194 | } 195 | 196 | /// @brief Allow to check if a board is connected 197 | /// @return Board status 198 | bool LORAE5::checkBoard(){ 199 | bool success; 200 | delay(500); 201 | LoRa_Serial.println("AT"); 202 | USB_Serial.println("\n\n\n\n"); 203 | if ( (success = checkResponse(400,"+AT: OK\r\n",NO_DEBUG)) == false){ 204 | USB_Serial.println("> LoRa-E5 board not detected ..."); 205 | } 206 | else{ 207 | USB_Serial.println("> LoRa-E5 board detected ...\n"); 208 | } 209 | return success; 210 | } 211 | 212 | /// @brief Allow to check if join procedure is successful 213 | /// @return Joined status 214 | bool LORAE5::join(){ 215 | bool joined = false, failed = false, done = false; 216 | String response, strDevAddr; 217 | uint32_t index; 218 | 219 | delay(2000); 220 | LoRa_Serial.setTimeout(10000); 221 | LoRa_Serial.println("AT+JOIN"); 222 | 223 | do{ 224 | response = LoRa_Serial.readStringUntil('\n'); 225 | #if DEBUG_LEVEL == 1 226 | USB_Serial.println(response); // DEBUG 227 | #endif 228 | 229 | // Analyse DevAddr response 230 | if ( (index = response.indexOf("DevAddr")) != -1 ){ 231 | index += strlen("DevAddr "); 232 | strDevAddr += response.charAt(index + 0); 233 | strDevAddr += response.charAt(index + 1); 234 | strDevAddr += ":"; 235 | strDevAddr += response.charAt(index + 3); 236 | strDevAddr += response.charAt(index + 4); 237 | strDevAddr += ":"; 238 | strDevAddr += response.charAt(index + 6); 239 | strDevAddr += response.charAt(index + 7); 240 | strDevAddr += ":"; 241 | strDevAddr += response.charAt(index + 9); 242 | strDevAddr += response.charAt(index + 10); 243 | USB_Serial.println("> SUCCESS JOINED OTAA !!!"); 244 | USB_Serial.println("> DevAddr = " + strDevAddr); 245 | joined = true; 246 | } 247 | // Analyse end of Join procedure 248 | else if ( (index = response.indexOf("Done")) != -1 ){ 249 | done = true; 250 | } 251 | // Analyse failed join 252 | else if ( (index = response.indexOf("Join failed")) != -1 ){ 253 | USB_Serial.println("> JOIN FAILED ..."); 254 | failed = true; 255 | } 256 | } while((!joined || !done) && !failed ); 257 | USB_Serial.println(""); 258 | return joined; 259 | } 260 | 261 | /// @brief Allow to send uplink data 262 | /// @param payloadUp is the data to send 263 | /// @param sizePayloadUp is the size of the data to send 264 | void LORAE5::sendPayloadUp(uint8_t* payloadUp, uint8_t sizePayloadUp) { 265 | USB_Serial.println("Sending " + String(this->confirmed ? "Confirmed " : "Unconfirmed ") + String("Data Up")); 266 | USB_Serial.print("- Payload UP "); 267 | 268 | String stringPayloadUp = ""; 269 | for (uint8_t i = 0; i < sizePayloadUp; i++) { 270 | USB_Serial.print(" 0x"); 271 | if (payloadUp[i] < 0x10) { 272 | USB_Serial.print('0'); 273 | stringPayloadUp += 0; 274 | } 275 | USB_Serial.print(payloadUp[i], HEX); 276 | USB_Serial.print(" "); 277 | stringPayloadUp += String(payloadUp[i], HEX); 278 | } 279 | 280 | USB_Serial.println(""); 281 | USB_Serial.println("- PortUp\t" + String(this->portUp)); 282 | USB_Serial.println("- SF\t\t" + String(this->sf)); 283 | USB_Serial.println(""); 284 | 285 | LoRa_Serial.println("AT+" + String(this->confirmed ? "C" : "") + String("MSGHEX") + String("=") + stringPayloadUp); 286 | } 287 | 288 | /// @brief Allow to manage the sending of uplink data 289 | /// @param payloadUp is the data to send 290 | /// @param sizePayloadUp is the size of the data to send 291 | void LORAE5::sendData(uint8_t* payloadUp, uint8_t sizePayloadUp){ 292 | bool transmissionDone = false; 293 | String response; 294 | uint32_t index; 295 | LoRa_Serial.setTimeout(500); 296 | 297 | getSetSF(); 298 | sendPayloadUp(payloadUp, sizePayloadUp); 299 | do{ 300 | response = LoRa_Serial.readStringUntil('\n'); 301 | #if DEBUG_LEVEL == 3 302 | USB_Serial.println(response); // DEBUG 303 | #endif 304 | 305 | if ( (index = response.indexOf("Start")) != -1 ){ 306 | USB_Serial.println("Transmission Done\nWaiting for Downlink..."); 307 | transmissionDone = true; 308 | } 309 | 310 | if ( (index = response.indexOf("Length error 0")) != -1 ){ 311 | LoRa_Serial.println("AT+MSG"); 312 | } 313 | 314 | } while (!transmissionDone); 315 | 316 | if(confirmed == true){ 317 | response = LoRa_Serial.readStringUntil('\n'); 318 | #if DEBUG_LEVEL == 3 319 | USB_Serial.println(response); // DEBUG 320 | #endif 321 | if ( (index = response.indexOf("Wait ACK")) != -1 ){ 322 | USB_Serial.println("Waiting for ACK..."); 323 | } 324 | } 325 | USB_Serial.println(""); 326 | } 327 | 328 | /// @brief Allow to debug or consume characters from the LoRaE5 module 329 | /// @param timeOut is the maximum allocated time for the function 330 | /// @param debug is to enable or disable the display 331 | void LORAE5::readResponse(uint32_t timeOut, bool debug){ 332 | uint32_t tstart = millis(); 333 | 334 | while (millis()-tstart < timeOut){ 335 | if(LoRa_Serial.available() > 0) { 336 | if (debug == DEBUG){ 337 | USB_Serial.print((char)LoRa_Serial.read()); 338 | } 339 | else{ 340 | LoRa_Serial.read(); 341 | } 342 | } 343 | } 344 | } 345 | 346 | /// @brief Allow to check if a string is present in the log 347 | /// @param timeOut is the maximum waiting time 348 | /// @param strCheck is the String we are looking for 349 | /// @param debug is to enable or disable the display 350 | /// @return if the requested string is present or not 351 | bool LORAE5::checkResponse(uint32_t timeOut, char *strCheck, bool debug){ 352 | uint32_t tstart = millis(); 353 | uint8_t i=0, checkSize = strlen(strCheck); 354 | char c; 355 | bool success = false; 356 | 357 | while (millis()-tstart < timeOut){ 358 | if(LoRa_Serial.available() > 0) { 359 | c = LoRa_Serial.read(); 360 | 361 | if (debug == DEBUG){ 362 | USB_Serial.print(c); 363 | } 364 | 365 | if( c == strCheck[i] ){ 366 | if (i == (checkSize-1)){ 367 | success = true; 368 | } 369 | else{ 370 | i++; 371 | } 372 | } 373 | else{ 374 | i=0; 375 | } 376 | } 377 | } 378 | return success; 379 | } 380 | 381 | /// @brief Displays the port up 382 | /// @param string is the information received from the device and stored in a buffer memory 383 | /// @return port up 384 | uint8_t LORAE5::getPortUp(){ 385 | return this->portUp; 386 | } 387 | 388 | /// @brief Displays the port down 389 | /// @param string is the information received from the device and stored in a buffer memory 390 | /// @return port down 391 | void LORAE5::getPortDown(String response){ 392 | 393 | String strPortDown; 394 | uint32_t index; 395 | uint32_t indexSemicolon = response.indexOf(';'); 396 | index = response.indexOf("PORT: "); 397 | index += strlen ("PORT: "); 398 | 399 | for (uint8_t i = 0 ; i < (indexSemicolon - index ) ; i++){ 400 | strPortDown +=response.charAt(index + i); 401 | } 402 | uint8_t portDown = strPortDown.toInt(); 403 | 404 | USB_Serial.println("- Port " + String(portDown)); 405 | this->portDown = portDown; 406 | } 407 | 408 | /// @brief Allow to process and display the payload down 409 | /// @param payloadDown is downlink message 410 | /// @param sizePayloadDown is size of the downlink message 411 | /// @param response is the information received from the device and stored in a buffer memory 412 | /// @return Payload down 413 | void LORAE5::getPayloadDown(uint8_t* payloadDown, uint8_t* sizePayloadDown, String response){ 414 | 415 | // Get Downlink Payload 416 | String strPayloadDown; 417 | uint32_t index; 418 | uint32_t indexQuote = response.lastIndexOf("\""); 419 | index = response.indexOf("RX: \""); 420 | index+= strlen("RX: \""); 421 | for (uint8_t i = 0 ; i < (indexQuote - index ) ; i++){ 422 | strPayloadDown +=response.charAt(index + i); 423 | } 424 | *sizePayloadDown = strPayloadDown.length() / 2 ; 425 | for (uint8_t i = 0 ; i < *sizePayloadDown ; i++){ 426 | String strByte =strPayloadDown.substring(i * 2, i * 2 + 2); 427 | payloadDown[i] = strtoul(strByte.c_str(), nullptr, 16); 428 | } 429 | USB_Serial.print("- Payload DOWN "); 430 | for (uint8_t i = 0 ; i < *sizePayloadDown ; i++){ 431 | USB_Serial.print(" 0x"); 432 | if (payloadDown[i] < 0x10){ 433 | USB_Serial.print('0'); 434 | } 435 | USB_Serial.print(payloadDown[i], HEX); 436 | USB_Serial.print(" "); 437 | } 438 | USB_Serial.println(""); 439 | } 440 | 441 | /// @brief Displays downlink channel 442 | /// @param response is the information received from the device and stored in a buffer memory 443 | void LORAE5::getChannelDown(String response){ 444 | 445 | String strChannelDown; 446 | uint32_t index; 447 | uint32_t indexSemicolon = response.indexOf(", RSSI "); 448 | index = response.indexOf(" RXWIN"); 449 | index += strlen (" RXWIN"); 450 | 451 | for (uint8_t i = 0 ; i < (indexSemicolon - index ) ; i++){ 452 | strChannelDown +=response.charAt(index + i); 453 | } 454 | uint8_t channelDown = strChannelDown.toInt(); 455 | 456 | if (channelDown == 0){ 457 | USB_Serial.println("- Slot RXC"); 458 | } else { 459 | USB_Serial.println("- Slot RX" + String(channelDown)); 460 | } 461 | USB_Serial.println(""); 462 | } 463 | 464 | /// @brief Updates the Device Class 465 | /// @return Device Class 466 | uint8_t LORAE5::getClass(){ 467 | return this->devClass; 468 | } 469 | 470 | /// @brief Updates the Data Rate 471 | /// @return Data rate 472 | uint8_t LORAE5::getADR(){ 473 | return this->adr; 474 | } 475 | 476 | /// @brief Summarizes device parameters 477 | void LORAE5::printInfo(){ 478 | #ifndef ARDUINO_AVR_LEONARDO 479 | USB_Serial.println("# For more information visit : https://github.com/SylvainMontagny/LoRaE5"); 480 | 481 | if(this->mode == OTAA) {USB_Serial.println("> OTAA");} 482 | else {USB_Serial.println("> ABP");} 483 | 484 | if(this->devClass == CLASS_A) {USB_Serial.println("> CLASS_A");} 485 | if(this->devClass == CLASS_C) {USB_Serial.println("> CLASS_C");} 486 | 487 | USB_Serial.println("> SF " + String(this->sf)); 488 | 489 | if(this->adr == true) {USB_Serial.println("> ADR ON");} 490 | else {USB_Serial.println("> ADR OFF");} 491 | 492 | USB_Serial.println(((this->confirmed == CONF)? String("> Confirmed") : String("> Unconfirmed"))); 493 | USB_Serial.println("> Port " + String(this->portUp)); 494 | USB_Serial.println(); 495 | USB_Serial.println( "* DevEUI 0x " + this->devEUI); 496 | 497 | if (this->mode == ABP) { 498 | USB_Serial.println("* DevAddr 0x " + this->devAddr); 499 | USB_Serial.println("* NwkSKey 0x " + this->nwkSKey); 500 | USB_Serial.println("* AppSKey 0x " + this->appSKey); 501 | } 502 | 503 | if (this->mode == OTAA){ 504 | USB_Serial.println("* AppKey 0x " + this->appKey); 505 | USB_Serial.println("* AppEUI-JoinEUI 0x " + this->appEUI); 506 | } 507 | USB_Serial.println(); 508 | #endif 509 | } 510 | 511 | /// @brief Waiting for Class_A downlink 512 | /// @param payloadDown is downlink message 513 | /// @param sizePayloadDown is size of the downlink message 514 | /// @return Downlink data information (RET_T/RET_TIMEOUT/RET_DOWNLINK/RET_NO_DOWNLINK) 515 | uint8_t LORAE5::awaitForDownlinkClass_A(uint8_t* payloadDown, uint8_t* sizePayloadDown){ 516 | uint32_t index; 517 | String response; 518 | bool isDoneReceived = false; 519 | bool isAckReceived = false; 520 | 521 | LoRa_Serial.setTimeout(500); 522 | 523 | do{ 524 | response += LoRa_Serial.readStringUntil('\n'); 525 | #if DEBUG_LEVEL == 4 526 | USB_Serial.println(response); // DEBUG 527 | #endif 528 | 529 | /****** Test Done *******/ 530 | if ( (index = response.indexOf("Done")) != -1 ){ 531 | isDoneReceived = true; 532 | } 533 | 534 | }while(isDoneReceived == false); 535 | 536 | //Confirmed part 537 | if ((confirmed == true)){ 538 | if (index = response.indexOf("ACK Received") != -1 ){ 539 | USB_Serial.println("ACK received"); 540 | isAckReceived = true; 541 | } 542 | else { 543 | USB_Serial.println("No ACK received\n"); 544 | return RET_NO_ACK; 545 | } 546 | } 547 | 548 | //Processing data received 549 | if ((index = response.indexOf("PORT: ")) != -1) { 550 | USB_Serial.println("Receiving Data Down:"); 551 | getPortDown(response); 552 | getPayloadDown(payloadDown, sizePayloadDown, response); 553 | getChannelDown(response); 554 | return RET_DOWNLINK; 555 | } 556 | 557 | else if ( ((index = response.indexOf("RXWIN")) != -1) && (isAckReceived == false) ) { 558 | USB_Serial.println("MAC command received :"); 559 | getChannelDown(response); 560 | return RET_NO_DOWNLINK; 561 | } 562 | 563 | else { 564 | if(isAckReceived == false){ 565 | USB_Serial.println("No Class_A Data Received."); 566 | } 567 | USB_Serial.println(); 568 | return RET_NO_DOWNLINK; 569 | } 570 | 571 | } 572 | 573 | /// @brief Waiting for Class_C downlink 574 | /// @param payloadDown is downlink message 575 | /// @param sizePayloadDown is size of the downlink message 576 | /// @return Downlink data information (RET_T/RET_TIMEOUT/RET_DOWNLINK) 577 | uint8_t LORAE5::awaitForDownlinkClass_C(uint8_t* payloadDown, uint8_t* sizePayloadDown){ 578 | uint32_t index; 579 | String response; 580 | bool isDoneReceived = false; 581 | uint32_t startTime = millis(); 582 | 583 | LoRa_Serial.setTimeout(500); 584 | 585 | #ifndef ARDUINO_AVR_LEONARDO 586 | if( this->SEND_BY_PUSH_BUTTON == true ){ 587 | USB_Serial.println("> Press 't' or receive a downlink.\n"); 588 | }else if( this->SEND_BY_PUSH_BUTTON == false ){ 589 | USB_Serial.println("> Press 't', receive a downlink or wait " + String(this->FRAME_DELAY) + String(" ms.\n")); 590 | } 591 | #endif 592 | 593 | do{ 594 | response += LoRa_Serial.readStringUntil('\n'); 595 | #if DEBUG_LEVEL == 4 596 | USB_Serial.println(response); // DEBUG 597 | #endif 598 | 599 | /****** Test Done *******/ 600 | if ( (index = response.indexOf("Done")) != -1 ){ 601 | isDoneReceived = true; 602 | } 603 | 604 | /****** Test 't' : transmission request *******/ 605 | if ( USB_Serial.available() > 0 ) { 606 | char userInput = USB_Serial.read(); 607 | if (userInput == 't' || userInput == 'T') { 608 | USB_Serial.println("# User input 't'.\n"); 609 | return RET_T; 610 | } 611 | } 612 | 613 | /****** Test Timeout *******/ 614 | if ((this->SEND_BY_PUSH_BUTTON == false) && ((millis() - startTime) >= (this->FRAME_DELAY))){ 615 | USB_Serial.println("No Class_C Data Received.\n"); 616 | USB_Serial.println("# Timeout reached.\n"); 617 | return RET_TIMEOUT; 618 | } 619 | 620 | }while(isDoneReceived == false); 621 | 622 | //Processing data received 623 | if ((index = response.indexOf("PORT: ")) != -1) { 624 | USB_Serial.println("Receiving Data Down:"); 625 | getPortDown(response); 626 | getPayloadDown(payloadDown, sizePayloadDown, response); 627 | getChannelDown(response); 628 | return RET_DOWNLINK; 629 | } 630 | } 631 | 632 | /// @brief Allow to wait and put the LoRaE5 module in Low Power mode ON/OFF 633 | /// @return Downlink data information (RET_T/RET_TIMEOUT) 634 | uint8_t LORAE5::sleep(){ 635 | uint32_t startTime = millis(); 636 | LoRa_Serial.println("AT+LOWPOWER"); 637 | String response = LoRa_Serial.readStringUntil('\n'); 638 | readResponse(5, NO_DEBUG); 639 | 640 | if (this->SEND_BY_PUSH_BUTTON == false) { 641 | while((millis() - startTime) < (this->FRAME_DELAY)){ 642 | if (USB_Serial.available() > 0) { 643 | char userInput = USB_Serial.read(); 644 | if (userInput == 't' || userInput == 'T') { 645 | LoRa_Serial.println("WAKE-UP"); 646 | response = LoRa_Serial.readStringUntil('\n'); 647 | readResponse(5,NO_DEBUG); 648 | USB_Serial.println("# User input 't'.\n"); 649 | return RET_T; 650 | } 651 | } 652 | } 653 | LoRa_Serial.println("WAKE-UP"); 654 | response = LoRa_Serial.readStringUntil('\n'); 655 | readResponse(5,NO_DEBUG); 656 | USB_Serial.println("# Timeout reached.\n"); 657 | return RET_TIMEOUT; 658 | } else { 659 | bool userTest = false; 660 | while (userTest == false){ 661 | if (USB_Serial.available() > 0) { 662 | char userInput = USB_Serial.read(); 663 | if (userInput == 't' || userInput == 'T') { 664 | LoRa_Serial.println("WAKE-UP"); 665 | response = LoRa_Serial.readStringUntil('\n'); 666 | readResponse(5,NO_DEBUG); 667 | userTest = true; 668 | USB_Serial.println("# User input 't'.\n"); 669 | return RET_T; 670 | } 671 | } 672 | } 673 | } 674 | } 675 | 676 | /// @brief Allow to change frequency band 677 | void LORAE5::setFrequencyBand(){ 678 | String response; 679 | 680 | if ((this->region) == EU868){ 681 | LoRa_Serial.println("AT+DR=EU868"); 682 | response = LoRa_Serial.readStringUntil('\n'); 683 | } 684 | 685 | if ((this->region) == US915){ 686 | LoRa_Serial.println("AT+DR=US915"); 687 | response = LoRa_Serial.readStringUntil('\n'); 688 | } 689 | 690 | #if DEBUG_LEVEL == 5 691 | LoRa_Serial.println("AT+CH"); 692 | response = LoRa_Serial.readStringUntil('\n'); 693 | USB_Serial.println(response); 694 | #endif 695 | } 696 | --------------------------------------------------------------------------------