├── .gitignore ├── test ├── img │ ├── 01.png │ └── 02.png ├── payLoadFormat.js ├── README.md ├── test.py ├── testFullfrequency │ └── testFullfrequency.ino └── testOTAA │ └── testOTAA.ino ├── src ├── lorawan.h └── arduino-rfm │ ├── Config.h │ ├── AES-128.h │ ├── Conversions.h │ ├── Encrypt.h │ ├── LoRaMAC.h │ ├── Conversions.cpp │ ├── lorawan-arduino-rfm.h │ ├── RFM95.h │ ├── Struct.h │ ├── AES-128.cpp │ ├── Encrypt.cpp │ ├── lorawan-arduino-rfm.cpp │ ├── LoRaMAC.cpp │ └── RFM95.cpp ├── doc └── release-notes.txt ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── Feedback-request.md │ └── BUG_REPORT_EC.md └── workflows │ └── BuildLibrary.yml ├── library.properties ├── LICENSE.txt ├── examples ├── serialToLoRaWAN │ └── serialToLoRaWAN.ino ├── class_c_abp │ └── class_c_abp.ino ├── class_a_abp │ └── class_a_abp.ino ├── class_a_otaa │ └── class_a_otaa.ino ├── class_c_otaa │ └── class_c_otaa.ino └── class_a_otaa_esp_deepsleep │ └── class_a_otaa_esp_deepsleep.ino ├── README.md └── API.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | \.DS_Store 3 | \.vscode 4 | -------------------------------------------------------------------------------- /test/img/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElectronicCats/Beelan-LoRaWAN/HEAD/test/img/01.png -------------------------------------------------------------------------------- /test/img/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElectronicCats/Beelan-LoRaWAN/HEAD/test/img/02.png -------------------------------------------------------------------------------- /src/lorawan.h: -------------------------------------------------------------------------------- 1 | #ifndef _LORAWAN_H_ 2 | #define _LORAWAN_H_ 3 | 4 | #include "arduino-rfm/lorawan-arduino-rfm.h" 5 | 6 | extern LoRaWANClass lora; 7 | 8 | #endif -------------------------------------------------------------------------------- /doc/release-notes.txt: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | Arduino LoRa.id SDK v1.0.0 (2-May-2018) 3 | ------------------------------- 4 | 5 | - Support Class A and Class C 6 | 7 | - Tested using Arduino Uno and LoRa RFM modules 8 | 9 | - Only send LoRa RX to Serial 10 | 11 | ============================================================================== -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: See if your issue is solved before creating a new one. 4 | about: Try to see if ther is an answer for yoru questions before you create a new issue. 5 | url: https://github.com/ElectronicCats/Beelan-LoRaWAN/issues?q=is%3Aissue+is%3Aclosed 6 | - name: ElectronicCats 7 | about: Contact us through our website 8 | url: https://electroniccats.com/contact/ -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Beelan LoRaWAN 2 | version=2.5.1 3 | author=Beelan 4 | maintainer=Electronic Cats 5 | sentence=LoRaWAN Arduino Library for standalone LoRaWAN modules class A and C with a simple API. 6 | paragraph=Device library for LoRaWAN network US, EU and AS. Support SX1276/72 or RFM95 7 | category=Communication 8 | url=https://github.com/ElectronicCats/Beelan-LoRaWAN 9 | architectures=avr,esp8266,samd,esp32,samdlc,renesas,renesas_portenta,renesas_uno 10 | includes=lorawan.h 11 | -------------------------------------------------------------------------------- /test/payLoadFormat.js: -------------------------------------------------------------------------------- 1 | // function to payload in the things network 2 | function Decoder(bytes, port) { 3 | // Decode an uplink message from a buffer 4 | // (array) of bytes to an object of fields. 5 | var decoded = {}; 6 | 7 | // if (port === 1) decoded.led = bytes[0]; 8 | var chan = bytes[0]; 9 | var rate = bytes[1]; 10 | var txP = bytes[2]; 11 | var tr = bytes[3]; 12 | var cntint = (bytes[4] << 8) | bytes[5]; 13 | 14 | decoded.chan = chan; 15 | decoded.rate = rate; 16 | decoded.Power = txP; 17 | decoded.tried = tr; 18 | decoded.cnt = cntint; 19 | 20 | return decoded; 21 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feedback-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F408 Feddback or requests" 3 | about: Suggest an idea or improvement for this project 4 | title: 'ElectronicCats' 5 | labels: 'enhancement' 6 | --- 7 | 8 | **What idea or improvement for the library has occurred to you?** 9 | 10 | **If you found an error:** 11 | 12 | Please describe clearly and concisely the problem you are aware of. 13 | 14 | 15 | **Have you already found a solution?** 16 | 17 | If you found a way to fix the problem please let us know. 18 | It would be very helpful if you put the part of the code that needs to be corrected and how it needs to be corrected. 19 | 20 | ***Thanks a lot*** 21 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | ### Testing 2 | 3 | Hardware used: 4 | - USB Stick CatWan from Electronic cats 5 | - Gateway Larid Sentrius RG101 6 | 7 | Frequencies: 8 | - Used US_915 BAND 9 | - Using SUB BAND 1 [ 903.9 - 905.3 Mhz] for Uplink 10 | - Using [923.3 - 927.5 Mhz] to Downlink 11 | - Using datarates acording to US_915 12 | 13 | In this example is used testFullFrequency program in test folder 14 | 15 | ### Results 16 | 17 | The images show the usb send and receive in left, at the right we see what the server receive. 18 | 19 | ![TST01](img/01.png?raw=true) 20 | 21 | ![TST02](img/02.png?raw=true) 22 | 23 | The uplink works in every frequency. 24 | 25 | The dowlink works in almost cases, this is because how is handling the interrupt in data reception -------------------------------------------------------------------------------- /test/test.py: -------------------------------------------------------------------------------- 1 | import time 2 | import ttn 3 | import datetime 4 | 5 | app_id = "you_app_id" 6 | access_key = "you_access_key" 7 | 8 | print('Time\t Dev Id\t\tCNT\tFrec\tData Rate\tPW CH DR TR') 9 | 10 | def uplink_callback(msg, client): 11 | now = datetime.datetime.now() 12 | print (now.strftime("%H:%M:%S"), end = ' ') 13 | print(msg.dev_id, end = '\t') 14 | print(msg.counter, end = '\t') 15 | print(msg.metadata.frequency, end = '\t') 16 | print(msg.metadata.data_rate, end = '\t') 17 | print(msg.payload_fields.Power, end = ' ') 18 | print(msg.payload_fields.chan, end = ' ') 19 | print(msg.payload_fields.rate, end = ' ') 20 | print(msg.payload_fields.tried) 21 | 22 | 23 | handler = ttn.HandlerClient(app_id, access_key,"discovery.thethings.network:1900") 24 | 25 | # using mqtt client 26 | mqtt_client = handler.data() 27 | mqtt_client.set_uplink_callback(uplink_callback) 28 | mqtt_client.connect() 29 | 30 | 31 | while True: 32 | time.sleep(30) 33 | 34 | mqtt_client.close() 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Beelan. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/arduino-rfm/Config.h: -------------------------------------------------------------------------------- 1 | // To define your LoRaWAN frequency band here 2 | //#define US_915 3 | //#define AS_923 4 | //#define AS_923_2 5 | //#define EU_868 6 | //#define AU_915 7 | //#define IN_865 8 | 9 | #if !defined(AS_923) && !defined(AS_923_2) && !defined(EU_868) && !defined(US_915) && !defined(AU_915) &&!defined(IN_865) 10 | #error "Select the Region in Config.h" 11 | #endif 12 | 13 | // If you dont define _CLASS_C_, CLASS_A mode will be on 14 | //#define _CLASS_C_ 15 | 16 | //Uncomment for debug 17 | //#define DEBUG 18 | 19 | // Define max payload size used for this node 20 | #define MAX_UPLINK_PAYLOAD_SIZE 220 21 | #define MAX_DOWNLINK_PAYLOAD_SIZE 220 22 | 23 | #ifdef US_915 24 | //Select the subband you're working on 25 | // make sure your gateway is working with the selected subband 26 | //#define SUBND_0 // 902.3 - 903.7 Mhz 27 | #define SUBND_1 // 903.9 - 905.3 Mhz 28 | //#define SUBND_2 // 905.5 - 906.9 Mhz 29 | //#define SUBND_3 // 907.1 - 908.5 Mhz 30 | //#define SUBND_4 // 908.7 - 910.1 Mhz 31 | //#define SUBND_5 // 910.3 - 911.7 Mhz 32 | //#define SUBND_6 // 911.9 - 913.3 Mhz 33 | //#define SUBND_7 // 913.5 - 914.9 Mhz 34 | #endif 35 | 36 | #ifdef AU_915 37 | //Select the subband you're working on 38 | // make sure your gateway is working with the selected subband 39 | #define SUBND_0 // 915.2 - 916.6 Mhz 40 | //#define SUBND_1 // 916.8 - 918.2 Mhz 41 | //#define SUBND_2 // 918.4 - 919.8 Mhz 42 | //#define SUBND_3 // 920.0 - 921.4 Mhz 43 | //#define SUBND_4 // 921.6 - 923.0 Mhz 44 | //#define SUBND_5 // 923.2 - 924.6 Mhz 45 | //#define SUBND_6 // 924.8 - 926.2 Mhz 46 | //#define SUBND_7 // 926.4 - 927.8 Mhz 47 | #endif 48 | -------------------------------------------------------------------------------- /examples/serialToLoRaWAN/serialToLoRaWAN.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //ABP Credentials 4 | const char *devAddr = "00000000"; 5 | const char *nwkSKey = "00000000000000000000000000000000"; 6 | const char *appSKey = "00000000000000000000000000000000"; 7 | 8 | char myStr[240]; 9 | char outStr[255]; 10 | byte recvStatus = 0; 11 | 12 | const sRFM_pins RFM_pins = { 13 | .CS = 6, 14 | .RST = 7, 15 | .DIO0 = 8, 16 | .DIO1 = 9, 17 | .DIO2 = -1, 18 | .DIO5 = -1, 19 | }; 20 | 21 | 22 | void setup() { 23 | // Setup loraid access 24 | Serial.begin(115200); 25 | Serial.println("Start.."); 26 | if(!lora.init()){ 27 | Serial.println("RFM95 not detected"); 28 | delay(5000); 29 | return; 30 | } 31 | 32 | // Set LoRaWAN Class change CLASS_A or CLASS_C 33 | lora.setDeviceClass(CLASS_C); 34 | 35 | // Set Data Rate 36 | lora.setDataRate(SF7BW125); 37 | 38 | // set channel to random 39 | lora.setChannel(MULTI); 40 | 41 | // Put ABP Key and DevAddress here 42 | lora.setNwkSKey(nwkSKey); 43 | lora.setAppSKey(appSKey); 44 | lora.setDevAddr(devAddr); 45 | } 46 | 47 | void loop() { 48 | 49 | if(Serial.available()){ 50 | uint8_t i = 0; 51 | while(Serial.available()>0){ 52 | myStr[i++]=Serial.read(); 53 | if(i>240){ 54 | Serial.println("[ERROR] payload maximo alcanzado"); 55 | break; 56 | } 57 | } 58 | Serial.print("Uplink: "); 59 | Serial.println(myStr); 60 | lora.sendUplink(myStr, strlen(myStr), 0, 1); 61 | } 62 | 63 | recvStatus = lora.readData(outStr); 64 | if(recvStatus) { 65 | Serial.print("Downlink: "); 66 | Serial.println(outStr); 67 | } 68 | 69 | // Check Lora RX 70 | lora.update(); 71 | } 72 | -------------------------------------------------------------------------------- /examples/class_c_abp/class_c_abp.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //ABP Credentials 4 | const char *devAddr = "00000000"; 5 | const char *nwkSKey = "00000000000000000000000000000000"; 6 | const char *appSKey = "00000000000000000000000000000000"; 7 | 8 | const unsigned long interval = 20000; // 10 s interval to send message 9 | unsigned long previousMillis = 0; // will store last time message sent 10 | unsigned int counter = 0; // message counter 11 | 12 | char myStr[50]; 13 | char outStr[255]; 14 | byte recvStatus = 0; 15 | 16 | const sRFM_pins RFM_pins = { 17 | .CS = 6, 18 | .RST = 7, 19 | .DIO0 = 8, 20 | .DIO1 = 9, 21 | .DIO2 = -1, 22 | .DIO5 = -1, 23 | }; 24 | 25 | 26 | void setup() { 27 | // Setup loraid access 28 | Serial.begin(115200); 29 | delay(5000); 30 | Serial.println("Start.."); 31 | if(!lora.init()){ 32 | Serial.println("RFM95 not detected"); 33 | delay(5000); 34 | return; 35 | } 36 | 37 | // Set LoRaWAN Class change CLASS_A or CLASS_C 38 | lora.setDeviceClass(CLASS_C); 39 | 40 | // Set Data Rate 41 | lora.setDataRate(SF7BW125); 42 | 43 | // set channel to random 44 | lora.setChannel(MULTI); 45 | 46 | // Put ABP Key and DevAddress here 47 | lora.setNwkSKey(nwkSKey); 48 | lora.setAppSKey(appSKey); 49 | lora.setDevAddr(devAddr); 50 | } 51 | 52 | void loop() { 53 | 54 | // Check interval overflow 55 | if(millis() - previousMillis > interval) { 56 | previousMillis = millis(); 57 | 58 | sprintf(myStr, "Counter-%d", counter); 59 | 60 | Serial.print("Sending: "); 61 | Serial.println(myStr); 62 | 63 | lora.sendUplink(myStr, strlen(myStr), 0, 1); 64 | counter++; 65 | } 66 | 67 | recvStatus = lora.readData(outStr); 68 | if(recvStatus) { 69 | Serial.print("====>> "); 70 | Serial.println(outStr); 71 | } 72 | 73 | // Check Lora RX 74 | lora.update(); 75 | } -------------------------------------------------------------------------------- /src/arduino-rfm/AES-128.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | * Copyright 2017 Ideetron B.V. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | ******************************************************************************************/ 17 | /**************************************************************************************** 18 | * File: AES-128.h 19 | * Author: Gerben den Hartog 20 | * Compagny: Ideetron B.V. 21 | * Website: http://www.ideetron.nl/LoRa 22 | * E-mail: info@ideetron.nl 23 | ****************************************************************************************/ 24 | /**************************************************************************************** 25 | * Created on: 13-01-2017 26 | * Supported Hardware: ID150119-02 Nexus board with RFM95 27 | ****************************************************************************************/ 28 | 29 | #ifndef AES128_H 30 | #define AES128_H 31 | 32 | /* 33 | ******************************************************************************************** 34 | * FUNCTION 35 | ******************************************************************************************** 36 | */ 37 | 38 | void AES_Encrypt(unsigned char *Data, unsigned char *Key); 39 | 40 | #endif 41 | 42 | -------------------------------------------------------------------------------- /src/arduino-rfm/Conversions.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | * Copyright 2017 Ideetron B.V. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | ******************************************************************************************/ 17 | /**************************************************************************************** 18 | * File: Conversions.h 19 | * Author: Gerben den Hartog 20 | * Compagny: Ideetron B.V. 21 | * Website: http://www.ideetron.nl/LoRa 22 | * E-mail: info@ideetron.nl 23 | ****************************************************************************************/ 24 | /**************************************************************************************** 25 | * Created on: 12-01-2017 26 | * Supported Hardware: ID150119-02 Nexus board with RFM95 27 | ****************************************************************************************/ 28 | 29 | #ifndef CONVERSIONS_H 30 | #define CONVERSIONS_H 31 | 32 | /* 33 | ***************************************************************************************** 34 | * FUNCTION PROTOTYPES 35 | ***************************************************************************************** 36 | */ 37 | 38 | void Hex2ASCII(unsigned char ASCII, unsigned char *Upper_Nibble, unsigned char *Lower_Nibble); 39 | unsigned char ASCII2Hex(unsigned char Upper_Nibble, unsigned char Lower_Nibble); 40 | 41 | #endif 42 | 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_REPORT_EC.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F640 Bug report " 3 | about: Report a bug or unexpected behavior while using the Beelan-LoRaWAN library 4 | title: 'ElectronicCats' 5 | labels: bug 6 | --- 7 | 8 | **Please, before reporting any issue** 9 | - Make sure your board it's in good condition. 10 | - Make sure your Module it's in good condition and that it is compatible. 11 | - Verify that it really is a library problem and not a hardware problem. 12 | - **Avoid** to submit a GitHub issue for project troubleshooting. 13 | 14 | Any feedback/suggestions should be discussed on the [feedback section](https://github.com/ElectronicCats/Beelan-LoRaWAN/issues): 15 | * Just click on New Issue and select the correct category. 16 | 17 | When reporting any issue, please try to provide all relevant information to help on its resolution. 18 | 19 | 20 | **Describe the bug** 21 | A clear and concise description of what the bug is. 22 | The more detailed this is, the easier we can come up with a solution. 23 | 24 | 25 | **To Reproduce** 26 | Complete source code which can be used to reproduce the issue. Please try to be as generic as possible. 27 | No extra code, extra hardware, etc. 28 | If you think it is absolutely necessary to add this, mention in detail the connections in case it is necessary hardware and also indicate the part of code that was added and how it was added. 29 | 30 | 31 | **Expected behavior** 32 | A clear and concise description of what you expected to happen. 33 | 34 | **Screenshots** 35 | If applicable, add screenshots to help explain your problem. 36 | 37 | **To help us to understand your situation, we would like to ask you for the following additional information::** 38 | - Which board are you using? 39 | - What Operating System and version are you using (e.g. Windows 11, macOS 12.0, Linux)? 40 | - In case you are using the Arduino IDE, What version of the Arduino IDE? 41 | - Did it work before?, If it worked before, what were the parameters that you modify? 42 | 43 | 44 | **Additional context** 45 | Add any other context about the problem here that you think will help us to solve your problem. -------------------------------------------------------------------------------- /examples/class_a_abp/class_a_abp.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Example of ABP device 3 | * Authors: 4 | * Ivan Moreno 5 | * Eduardo Contreras 6 | * June 2019 7 | * 8 | * This code is beerware; if you see me (or any other collaborator 9 | * member) at the local, and you've found our code helpful, 10 | * please buy us a round! 11 | * Distributed as-is; no warranty is given. 12 | */ 13 | #include 14 | 15 | //ABP Credentials 16 | const char *devAddr = "00000000"; 17 | const char *nwkSKey = "00000000000000000000000000000000"; 18 | const char *appSKey = "00000000000000000000000000000000"; 19 | 20 | const unsigned long interval = 10000; // 10 s interval to send message 21 | unsigned long previousMillis = 0; // will store last time message sent 22 | unsigned int counter = 0; // message counter 23 | 24 | char myStr[50]; 25 | char outStr[255]; 26 | byte recvStatus = 0; 27 | 28 | const sRFM_pins RFM_pins = { 29 | .CS = 20, 30 | .RST = 9, 31 | .DIO0 = 0, 32 | .DIO1 = 1, 33 | .DIO2 = 2, 34 | .DIO5 = 15, 35 | }; 36 | 37 | void setup() { 38 | // Setup loraid access 39 | Serial.begin(9600); 40 | while(!Serial); 41 | if(!lora.init()){ 42 | Serial.println("RFM95 not detected"); 43 | delay(5000); 44 | return; 45 | } 46 | 47 | // Set LoRaWAN Class change CLASS_A or CLASS_C 48 | lora.setDeviceClass(CLASS_A); 49 | 50 | // Set Data Rate 51 | lora.setDataRate(SF8BW125); 52 | 53 | // set channel to random 54 | lora.setChannel(MULTI); 55 | 56 | // Put ABP Key and DevAddress here 57 | lora.setNwkSKey(nwkSKey); 58 | lora.setAppSKey(appSKey); 59 | lora.setDevAddr(devAddr); 60 | } 61 | 62 | void loop() { 63 | // Check interval overflow 64 | if(millis() - previousMillis > interval) { 65 | previousMillis = millis(); 66 | 67 | sprintf(myStr, "Counter-%d", counter); 68 | 69 | Serial.print("Sending: "); 70 | Serial.println(myStr); 71 | 72 | lora.sendUplink(myStr, strlen(myStr), 0, 1); 73 | counter++; 74 | } 75 | 76 | recvStatus = lora.readData(outStr); 77 | if(recvStatus) { 78 | Serial.println(outStr); 79 | } 80 | 81 | // Check Lora RX 82 | lora.update(); 83 | } -------------------------------------------------------------------------------- /examples/class_a_otaa/class_a_otaa.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Example of OTAA device 3 | * Authors: 4 | * Ivan Moreno 5 | * Eduardo Contreras 6 | * June 2019 7 | * 8 | * This code is beerware; if you see me (or any other collaborator 9 | * member) at the local, and you've found our code helpful, 10 | * please buy us a round! 11 | * Distributed as-is; no warranty is given. 12 | */ 13 | #include 14 | 15 | // OTAA credentials 16 | const char *devEui = "0000000000000000"; 17 | const char *appEui = "0000000000000000"; 18 | const char *appKey = "00000000000000000000000000000000"; 19 | 20 | const unsigned long interval = 10000; // 10 s interval to send message 21 | unsigned long previousMillis = 0; // will store last time message sent 22 | unsigned int counter = 0; // message counter 23 | 24 | char myStr[50]; 25 | char outStr[255]; 26 | byte recvStatus = 0; 27 | 28 | const sRFM_pins RFM_pins = { 29 | .CS = 20, 30 | .RST = 9, 31 | .DIO0 = 0, 32 | .DIO1 = 1, 33 | .DIO2 = 2, 34 | .DIO5 = 15, 35 | }; 36 | 37 | void setup() { 38 | // Setup loraid access 39 | Serial.begin(9600); 40 | while(!Serial); 41 | if(!lora.init()){ 42 | Serial.println("RFM95 not detected"); 43 | delay(5000); 44 | return; 45 | } 46 | 47 | // Set LoRaWAN Class change CLASS_A or CLASS_C 48 | lora.setDeviceClass(CLASS_A); 49 | 50 | // Set Data Rate 51 | lora.setDataRate(SF9BW125); 52 | 53 | // set channel to random 54 | lora.setChannel(MULTI); 55 | 56 | // Put OTAA Key and DevAddress here 57 | lora.setDevEUI(devEui); 58 | lora.setAppEUI(appEui); 59 | lora.setAppKey(appKey); 60 | 61 | // Join procedure 62 | bool isJoined; 63 | do { 64 | Serial.println("Joining..."); 65 | isJoined = lora.join(); 66 | 67 | //wait for 10s to try again 68 | delay(10000); 69 | }while(!isJoined); 70 | Serial.println("Joined to network"); 71 | } 72 | 73 | void loop() { 74 | // Check interval overflow 75 | if(millis() - previousMillis > interval) { 76 | previousMillis = millis(); 77 | 78 | sprintf(myStr, "Counter-%d", counter); 79 | 80 | Serial.print("Sending: "); 81 | Serial.println(myStr); 82 | 83 | lora.sendUplink(myStr, strlen(myStr), 0, 1); 84 | counter++; 85 | } 86 | 87 | recvStatus = lora.readData(outStr); 88 | if(recvStatus) { 89 | Serial.println(outStr); 90 | } 91 | 92 | // Check Lora RX 93 | lora.update(); 94 | } -------------------------------------------------------------------------------- /src/arduino-rfm/Encrypt.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | * Copyright 2017 Ideetron B.V. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | ******************************************************************************************/ 17 | /**************************************************************************************** 18 | * File: Encrypt.h 19 | * Author: Gerben den Hartog 20 | * Compagny: Ideetron B.V. 21 | * Website: http://www.ideetron.nl/LoRa 22 | * E-mail: info@ideetron.nl 23 | ****************************************************************************************/ 24 | /**************************************************************************************** 25 | * Created on: 09-02-2017 26 | * Supported Hardware: ID150119-02 Nexus board with RFM95 27 | ****************************************************************************************/ 28 | 29 | #ifndef ENCRYPT_H 30 | #define ENCRYPT_H 31 | 32 | /* 33 | ******************************************************************************************** 34 | * INCLUDES 35 | ******************************************************************************************** 36 | */ 37 | 38 | #include "Struct.h" 39 | 40 | /* 41 | ***************************************************************************************** 42 | * FUNCTION PROTOTYPES 43 | ***************************************************************************************** 44 | */ 45 | 46 | void Construct_Data_MIC(sBuffer *Buffer, sLoRa_Session *Session_Data, sLoRa_Message *Message); 47 | void Calculate_MIC(sBuffer *Buffer, unsigned char *Key, sLoRa_Message *Message); 48 | void Encrypt_Payload(sBuffer *Buffer, unsigned char *Key, sLoRa_Message *Message); 49 | void Generate_Keys(unsigned char *Key, unsigned char *K1, unsigned char *K2); 50 | 51 | #endif 52 | 53 | -------------------------------------------------------------------------------- /examples/class_c_otaa/class_c_otaa.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Example of OTAA class C device 3 | */ 4 | #include 5 | 6 | // OTAA credentials 7 | const char *devEui = "0000000000000000"; 8 | const char *appEui = "0000000000000000"; 9 | const char *appKey = "00000000000000000000000000000000"; 10 | 11 | const unsigned long interval = 10000; // 10 s interval to send message 12 | unsigned long previousMillis = 0; // will store last time message sent 13 | unsigned int counter = 0; // message counter 14 | 15 | char myStr[50]; 16 | char outStr[255]; 17 | byte recvStatus = 0; 18 | 19 | //Pins used to handle the communication with the LoRa module 20 | const sRFM_pins RFM_pins = { 21 | .CS = 7, 22 | .RST = 3, 23 | .DIO0 = 18, 24 | .DIO1 = 19, 25 | .DIO2 = -1, 26 | .DIO5 = -1, 27 | }; 28 | 29 | void setup() { 30 | // Setup loraid access 31 | Serial.begin(115200); 32 | while(!Serial); 33 | if(!lora.init()){ 34 | Serial.println("RFM95 not detected"); 35 | delay(10000); 36 | return; 37 | } 38 | 39 | // Set LoRaWAN Class change CLASS_A or CLASS_C 40 | lora.setDeviceClass(CLASS_C); 41 | 42 | // Set Data Rate 43 | lora.setDataRate(SF8BW125); 44 | 45 | // set channel to random 46 | lora.setChannel(MULTI); 47 | 48 | // Put OTAA Key and DevAddress here 49 | lora.setDevEUI(devEui); 50 | lora.setAppEUI(appEui); 51 | lora.setAppKey(appKey); 52 | 53 | // Join procedure 54 | bool isJoined; 55 | do { 56 | Serial.println("Joining..."); 57 | isJoined = lora.join(); 58 | 59 | //wait for 10s to try again 60 | delay(10000); 61 | }while(!isJoined); 62 | Serial.println("Joined to network"); 63 | } 64 | 65 | void loop() { 66 | // Check interval overflow 67 | if (millis() - previousMillis > interval){ 68 | previousMillis = millis(); 69 | 70 | sprintf(myStr, "Counter-%d", counter); 71 | 72 | Serial.print("Sending: "); 73 | Serial.println(myStr); 74 | 75 | //To send the string, the message is confirmed, using Port1 76 | lora.sendUplink(myStr, strlen(myStr), 1, 1); 77 | counter++; 78 | } 79 | 80 | //If any downliks are received from the server, proceed to read and print it, as well as the reception power 81 | recvStatus = lora.readData(outStr); 82 | if(recvStatus) { 83 | Serial.println(lora.getRssi()); 84 | Serial.println(outStr); 85 | } 86 | 87 | // Check Lora RX 88 | lora.update(); 89 | 90 | //To receive the "Acknowlede" message from the server, since our Uplink was a confirmed one 91 | if(lora.readAck()) Serial.println("ack received"); 92 | } 93 | -------------------------------------------------------------------------------- /src/arduino-rfm/LoRaMAC.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | * Copyright 2017 Ideetron B.V. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | ******************************************************************************************/ 17 | /**************************************************************************************** 18 | * File: LoRaMAC.h 19 | * Author: Gerben den Hartog 20 | * Compagny: Ideetron B.V. 21 | * Website: http://www.ideetron.nl/LoRa 22 | * E-mail: info@ideetron.nl 23 | ****************************************************************************************/ 24 | /**************************************************************************************** 25 | * Created on: 06-01-2017 26 | * Supported Hardware: ID150119-02 Nexus board with RFM95 27 | ****************************************************************************************/ 28 | 29 | #ifndef LORAMAC_H 30 | #define LORAMAC_H 31 | 32 | /* 33 | ******************************************************************************************** 34 | * INCLUDES 35 | ******************************************************************************************** 36 | */ 37 | 38 | #include "Struct.h" 39 | 40 | /* 41 | ******************************************************************************************** 42 | * TYPE DEFINITION 43 | ******************************************************************************************** 44 | */ 45 | 46 | typedef enum {NO_RFM_COMMAND, NEW_RFM_COMMAND, RFM_COMMAND_DONE, JOIN} RFM_command_t; 47 | 48 | /* 49 | ***************************************************************************************** 50 | * FUNCTION PROTOTYPES 51 | ***************************************************************************************** 52 | */ 53 | 54 | void LORA_Cycle(sBuffer *Data_Tx, sBuffer *Data_Rx, RFM_command_t *RFM_Command, sLoRa_Session *Session_Data, sLoRa_OTAA *OTAA_Data, sLoRa_Message *Message_Rx, sSettings *LoRa_Settings); 55 | void LORA_Send_Data(sBuffer *Data_Tx, sLoRa_Session *Session_Data, sSettings *LoRa_Settings); 56 | void LORA_Receive_Data(sBuffer *Data_Rx, sLoRa_Session *Session_Data, sLoRa_OTAA *OTAA_Data, sLoRa_Message *Message, sSettings *LoRa_Settings); 57 | bool LORA_join_Accept(sBuffer *Data_Rx,sLoRa_Session *Session_Data, sLoRa_OTAA *OTAA_Data, sLoRa_Message *Message, sSettings *LoRa_Settings); 58 | void LoRa_Send_JoinReq(sLoRa_OTAA *OTAA_Data, sSettings *LoRa_Settings); 59 | #endif 60 | 61 | -------------------------------------------------------------------------------- /test/testFullfrequency/testFullfrequency.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Test in all frequencies of US_915 band 3 | * Using SUB BAND 1 [ 903.9 - 905.3 Mhz] see config.h 4 | * channel [0 - 7] 5 | * data rate [0 - 3] 6 | * Class A and C devices with ABP activation 7 | * 8 | * Gateway: Sentrius RG101 9 | * 10 | * Using CATWAN USB STICK hardware 11 | * 12 | * Author: Ivan Moreno 13 | * May 2019 14 | */ 15 | 16 | #include 17 | 18 | // ABP device 19 | const char *devAddr = "00000000"; 20 | const char *nwkSKey = "00000000000000000000000000000000"; 21 | const char *appSKey = "00000000000000000000000000000000"; 22 | 23 | 24 | unsigned long interval = 15000; // 15 s interval to send message 25 | unsigned long previousMillis = 0; // will store last time message sent 26 | unsigned long currentMillis; 27 | 28 | union _cntCI { 29 | unsigned char _cb[2]; 30 | unsigned int val; 31 | } counter; 32 | 33 | char payload[10]; 34 | char outStr[100]; 35 | byte recvStatus = 0; 36 | 37 | byte _dataRate; 38 | byte _channel; 39 | byte times; 40 | 41 | const sRFM_pins RFM_pins = { 42 | .CS = 20, 43 | .RST = 9, 44 | .DIO0 = 0, 45 | .DIO1 = 1, 46 | .DIO2 = 2, 47 | .DIO5 = 15, 48 | }; 49 | 50 | void setup() { 51 | // seti serial port 52 | Serial.begin(115200); 53 | delay(4000); 54 | 55 | if(!lora.init()){ 56 | Serial.println("RFM95 not detected"); 57 | return; 58 | } 59 | 60 | // Set LoRaWAN Class 61 | lora.setDeviceClass(CLASS_A); 62 | 63 | lora.setTxPower(15,PA_BOOST_PIN); 64 | 65 | counter.val = 0; 66 | _channel = 0; // 0 - 7 67 | _dataRate = 0; // 0 - 3 68 | times = 0; 69 | 70 | // Put ABP Key and DevAddress here 71 | lora.setNwkSKey(nwkSKey); 72 | lora.setAppSKey(appSKey); 73 | lora.setDevAddr(devAddr); 74 | } 75 | 76 | void loop() { 77 | currentMillis = millis(); 78 | // Check interval overflow 79 | if(currentMillis - previousMillis > interval) { 80 | if(times > 0){ 81 | times = 0; 82 | _dataRate++; 83 | if (_dataRate > 3){ 84 | _dataRate = 0; 85 | _channel++; 86 | if (_channel > 7) { 87 | _channel = 0; 88 | } 89 | } 90 | } 91 | // Set Channel 92 | lora.setChannel(_channel); 93 | 94 | // Set Data Rate 95 | lora.setDataRate(_dataRate); 96 | 97 | Serial.print("SND["); 98 | Serial.print(counter.val); 99 | Serial.print("], CH: "); 100 | Serial.print(_channel); 101 | Serial.print(", DR: "); 102 | Serial.println(_dataRate); 103 | 104 | previousMillis = currentMillis; 105 | 106 | payload[0] = _channel; 107 | payload[1] = _dataRate; 108 | payload[2] = 15; 109 | payload[3] = times + 1; 110 | payload[4] = counter._cb[1]; 111 | payload[5] = counter._cb[0]; 112 | 113 | lora.sendUplink(payload, 6, 0, 1); 114 | counter.val++; 115 | times++; 116 | } 117 | 118 | // Check Lora RX 119 | lora.update(); 120 | 121 | recvStatus = lora.readData(outStr); 122 | if(recvStatus) { 123 | Serial.print("Rcv: "); 124 | Serial.println(outStr); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /test/testOTAA/testOTAA.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Test in all frequencies of US_915 band 3 | * Using SUB BAND 1 [ 903.9 - 905.3 Mhz] see config.h 4 | * channel [0 - 7] 5 | * data rate [0 - 3] 6 | * Class A and C devices with OTAA activation 7 | * 8 | * Gateway: Sentrius RG101 9 | * 10 | * Using CATWAN USB STICK hardware 11 | * 12 | * Author: Ivan Moreno 13 | * June 2019 14 | */ 15 | 16 | #include 17 | 18 | // OTAA credentials 19 | const char *devEui = "0000000000000000"; 20 | const char *appEui = "0000000000000000"; 21 | const char *appKey = "00000000000000000000000000000000"; 22 | 23 | const unsigned long interval = 30000; // 15 s interval to send message 24 | unsigned long previousMillis = 0; // will store last time message sent 25 | unsigned long currentMillis; 26 | 27 | union _cntCI { 28 | unsigned char _cb[2]; 29 | unsigned int val; 30 | } counter; 31 | 32 | char payload[10]; 33 | char outStr[100]; 34 | byte recvStatus = 0; 35 | 36 | byte _dataRate; 37 | byte _channel; 38 | byte times; 39 | 40 | const sRFM_pins RFM_pins = { 41 | .CS = 20, 42 | .RST = 9, 43 | .DIO0 = 0, 44 | .DIO1 = 1, 45 | .DIO2 = 2, 46 | .DIO5 = 15, 47 | }; 48 | 49 | 50 | void setup() { 51 | Serial.begin(115200); 52 | delay(5000); 53 | 54 | if (!lora.init()){ 55 | Serial.println("RFM no init"); 56 | delay(5000); 57 | return; 58 | } 59 | lora.setDeviceClass(CLASS_A); 60 | lora.setTxPower(15,PA_BOOST_PIN); 61 | lora.setChannel(CH0); 62 | lora.setDataRate(SF9BW125); 63 | 64 | counter.val = 0; 65 | _channel = 0; // 0 - 7 66 | _dataRate = 0; // 0 - 3 67 | times = 0; 68 | 69 | lora.setDevEUI(devEui); 70 | lora.setAppEUI(appEui); 71 | lora.setAppKey(appKey); 72 | 73 | bool isJoined; 74 | do { 75 | Serial.println("Joining..."); 76 | isJoined = lora.join(); 77 | delay(10000); 78 | }while(!isJoined); 79 | Serial.println("Joined to network"); 80 | } 81 | 82 | void loop() { 83 | currentMillis = millis(); 84 | // Check interval overflow 85 | if(currentMillis - previousMillis > interval) { 86 | if(times > 0){ 87 | times = 0; 88 | _dataRate++; 89 | if (_dataRate > 3){ 90 | _dataRate = 0; 91 | _channel++; 92 | if (_channel > 7) { 93 | _channel = 0; 94 | } 95 | } 96 | } 97 | // Set Channel 98 | lora.setChannel(_channel); 99 | 100 | // Set Data Rate 101 | lora.setDataRate(_dataRate); 102 | 103 | Serial.print("SND["); 104 | Serial.print(counter.val); 105 | Serial.print("], CH: "); 106 | Serial.print(_channel); 107 | Serial.print(", DR: "); 108 | Serial.println(_dataRate); 109 | 110 | previousMillis = currentMillis; 111 | 112 | payload[0] = _channel; 113 | payload[1] = _dataRate; 114 | payload[2] = 15; 115 | payload[3] = times + 1; 116 | payload[4] = counter._cb[1]; 117 | payload[5] = counter._cb[0]; 118 | 119 | lora.sendUplink(payload, 6, 0, 1); 120 | counter.val++; 121 | times++; 122 | } 123 | 124 | // Check Lora RX 125 | lora.update(); 126 | 127 | recvStatus = lora.readData(outStr); 128 | if(recvStatus) { 129 | Serial.print("Rcv: "); 130 | Serial.println(outStr); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/arduino-rfm/Conversions.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | * Copyright 2017 Ideetron B.V. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | ******************************************************************************************/ 17 | /**************************************************************************************** 18 | * File: Conversions.cpp 19 | * Author: Gerben den Hartog 20 | * Compagny: Ideetron B.V. 21 | * Website: http://www.ideetron.nl/LoRa 22 | * E-mail: info@ideetron.nl 23 | ****************************************************************************************/ 24 | /**************************************************************************************** 25 | * Created on: 12-01-2017 26 | * Supported Hardware: ID150119-02 Nexus board with RFM95 27 | ****************************************************************************************/ 28 | 29 | /* 30 | ***************************************************************************************** 31 | * INCLUDE FILES 32 | ***************************************************************************************** 33 | */ 34 | 35 | #include "Conversions.h" 36 | 37 | void Hex2ASCII(unsigned char ASCII, unsigned char *Upper_Nibble, unsigned char *Lower_Nibble) 38 | { 39 | //Load upper nibble 40 | *Upper_Nibble = (ASCII >> 4) & 0x0F; 41 | 42 | //Load lower nibbel 43 | *Lower_Nibble = ASCII & 0x0F; 44 | 45 | //Convert upper nibble to ascii 46 | if(*Upper_Nibble > 0x09) 47 | { 48 | *Upper_Nibble = *Upper_Nibble + 0x37; 49 | } 50 | else 51 | { 52 | *Upper_Nibble = *Upper_Nibble + 0x30; 53 | } 54 | 55 | //Convert Lower nibble 56 | if(*Lower_Nibble > 0x09) 57 | { 58 | *Lower_Nibble = *Lower_Nibble + 0x37; 59 | } 60 | else 61 | { 62 | *Lower_Nibble = *Lower_Nibble + 0x30; 63 | } 64 | } 65 | 66 | unsigned char ASCII2Hex(unsigned char Upper_Nibble, unsigned char Lower_Nibble) 67 | { 68 | 69 | unsigned char ASCII; 70 | 71 | // High Nibble 72 | if(Upper_Nibble >= '0' && Upper_Nibble <= '9') 73 | { 74 | ASCII =(Upper_Nibble - '0') * 16; 75 | } 76 | 77 | if(Upper_Nibble >= 'A' && Upper_Nibble <= 'F') 78 | { 79 | ASCII = (Upper_Nibble - 'A' + 10) * 16; 80 | } 81 | 82 | if(Upper_Nibble >= 'a' && Upper_Nibble <= 'f') 83 | { 84 | ASCII = (Upper_Nibble - 'a' + 10) * 16; 85 | } 86 | 87 | // Low Nibble 88 | if(Lower_Nibble >= '0' && Lower_Nibble <= '9') 89 | { 90 | ASCII = ASCII + (Lower_Nibble - '0'); 91 | } 92 | 93 | if(Lower_Nibble >= 'A' && Lower_Nibble <= 'F') 94 | { 95 | ASCII = ASCII + (Lower_Nibble - 'A' + 10); 96 | } 97 | 98 | if(Lower_Nibble >= 'a' && Lower_Nibble <= 'f') 99 | { 100 | ASCII = ASCII + (Lower_Nibble - 'a' + 10); 101 | } 102 | 103 | return ASCII; 104 | 105 | } 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/arduino-rfm/lorawan-arduino-rfm.h: -------------------------------------------------------------------------------- 1 | /* 2 | lorawan-arduino-rfm.h 3 | Copyright © 2018 lora.id. All right reserved. 4 | 5 | Author: Andri Rahmadhani 6 | Date: 2018-04-25 7 | 8 | Encapsulate Ideetron LoRaWAN simple node demonstrator 9 | *This fimrware supports 10 | *Over The Air Activation 11 | *Activation By Personalization 12 | *Class switching between Class A and Class C motes 13 | *Channel hopping 14 | * 15 | *The following settings kan be done 16 | *Channel Receive and Transmit 17 | *Datarate Receive and Transmit 18 | *Tranmit power 19 | *Confirmed or unconfirmed messages 20 | *Device Address 21 | *Application Session Key 22 | *Network Session Key 23 | *Device EUI 24 | *Application EUI 25 | *Application key 26 | *Mote Class 27 | 28 | Use of this source code is governed by the MIT license that can be found in the LICENSE file. 29 | */ 30 | 31 | #ifndef _LORAWAN_ARDUINO_RFM_H_ 32 | #define _LORAWAN_ARDUINO_RFM_H_ 33 | 34 | #include 35 | #include 36 | #include "AES-128.h" 37 | #include "Encrypt.h" 38 | #include "RFM95.h" 39 | #include "LoRaMAC.h" 40 | #include "Struct.h" 41 | #include "Config.h" 42 | 43 | /* 44 | ******************************************************************************************** 45 | * TYPE DEFINITION 46 | ******************************************************************************************** 47 | */ 48 | 49 | #define LORAWAN_VERSION "1.0.0" 50 | /* 51 | ***************************************************************************************** 52 | * CLASS 53 | ***************************************************************************************** 54 | */ 55 | 56 | class LoRaWANClass 57 | { 58 | public: 59 | LoRaWANClass(); 60 | ~LoRaWANClass(); 61 | 62 | bool init(void); 63 | bool join(void); 64 | void sleep(void); 65 | void wakeUp(void); 66 | void setDeviceClass(devclass_t dev_class); 67 | // OTAA credentials 68 | void setDevEUI(const char *devEUI_in); 69 | void setAppEUI(const char *appEUI_in); 70 | void setAppKey(const char *appKey_in); 71 | // ABP credentials 72 | void setNwkSKey(const char *NwkKey_in); 73 | void setAppSKey(const char *ApskKey_in); 74 | void setDevAddr(const char *devAddr_in); 75 | void sendUplink(char *data, unsigned int len, unsigned char confirm, unsigned char mport); 76 | void setDataRate(unsigned char data_rate); 77 | void setChannel(unsigned char channel); 78 | unsigned char getChannel(); 79 | unsigned char getDataRate(); 80 | void setTxPower1(unsigned char power_idx); 81 | void setTxPower(int level, txPin_t pinTx); 82 | void setRx1Delay(unsigned int ms); 83 | void setRx2Delay(unsigned int ms); 84 | void setRx1Window(unsigned int ms); 85 | void setRx2Window(unsigned int ms); 86 | int getRssi(); 87 | int readData(char *outBuff); 88 | bool readAck(void); 89 | void update(void); 90 | 91 | // frame counter 92 | unsigned int getFrameCounter(); 93 | void setFrameCounter(unsigned int FrameCounter); 94 | 95 | // Declare public instead of private so they can be save on RTC ram in the main script before deep sleep for ESP 96 | unsigned char DevEUI[8]; 97 | unsigned char AppEUI[8]; 98 | unsigned char AppKey[16]; 99 | unsigned char DevNonce[2]; 100 | unsigned char AppNonce[3]; 101 | unsigned char NetID[3]; 102 | unsigned char Address_Tx[4]; 103 | unsigned char NwkSKey[16]; 104 | unsigned char AppSKey[16]; 105 | unsigned int Frame_Counter_Tx; 106 | sSettings LoRa_Settings; 107 | 108 | private: 109 | void randomChannel(); 110 | 111 | private: 112 | // Messages 113 | unsigned char Data_Tx[MAX_UPLINK_PAYLOAD_SIZE]; 114 | sBuffer Buffer_Tx; 115 | unsigned char Data_Rx[MAX_DOWNLINK_PAYLOAD_SIZE]; 116 | sBuffer Buffer_Rx; 117 | sLoRa_Message Message_Rx; 118 | 119 | // Declare ABP session 120 | sLoRa_Session Session_Data; 121 | 122 | // Declare OTAA data struct 123 | sLoRa_OTAA OTAA_Data; 124 | 125 | unsigned char drate_common; 126 | 127 | // Lora Setting Class 128 | devclass_t dev_class; 129 | 130 | // channel mode 131 | unsigned char currentChannel; 132 | 133 | // UART 134 | RFM_command_t RFM_Command_Status; 135 | rx_t Rx_Status; 136 | 137 | // ACK reception 138 | ack_t Ack_Status; 139 | }; 140 | 141 | #endif 142 | -------------------------------------------------------------------------------- /src/arduino-rfm/RFM95.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | * Copyright 2017 Ideetron B.V. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | ******************************************************************************************/ 17 | /**************************************************************************************** 18 | * File: RFM95.h 19 | * Author: Gerben den Hartog 20 | * Compagny: Ideetron B.V. 21 | * Website: http://www.ideetron.nl/LoRa 22 | * E-mail: info@ideetron.nl 23 | ****************************************************************************************/ 24 | /**************************************************************************************** 25 | * Created on: 06-01-2017 26 | * Supported Hardware: ID150119-02 Nexus board with RFM95 27 | ****************************************************************************************/ 28 | 29 | #ifndef RFM95_H 30 | #define RFM95_H 31 | 32 | /* 33 | ******************************************************************************************** 34 | * INCLUDES 35 | ******************************************************************************************** 36 | */ 37 | 38 | #include "Struct.h" 39 | 40 | /* 41 | ***************************************************************************************** 42 | * TYPE DEFINITIONS 43 | ***************************************************************************************** 44 | */ 45 | 46 | typedef enum {NO_MESSAGE,NEW_MESSAGE,CRC_OK,MIC_OK,ADDRESS_OK,MESSAGE_DONE,TIMEOUT,WRONG_MESSAGE} message_t; 47 | 48 | #define PA_OUTPUT_RFO_PIN 0 49 | #define PA_OUTPUT_PA_BOOST_PIN 1 50 | 51 | /* 52 | ***************************************************************************************** 53 | * REGISTER DEFINITIONS 54 | ***************************************************************************************** 55 | */ 56 | 57 | typedef enum { 58 | RFM_REG_FIFO = 0x00, 59 | RFM_REG_OP_MODE = 0x01, 60 | RFM_REG_FR_MSB = 0x06, 61 | RFM_REG_FR_MID = 0x07, 62 | RFM_REG_FR_LSB = 0x08, 63 | RFM_REG_OCP = 0x0b, 64 | RFM_REG_PA_CONFIG = 0x09, 65 | RFM_REG_LNA = 0x0C, 66 | RFM_REG_FIFO_ADDR_PTR = 0x0D, 67 | RFM_REG_IRQ_FLAGS = 0x12, 68 | RFM_REG_LAST_RSSI = 0x1A, 69 | RFM_REG_MODEM_CONFIG1 = 0x1D, 70 | RFM_REG_MODEM_CONFIG2 = 0x1E, 71 | RFM_REG_SYM_TIMEOUT_LSB = 0x1F, 72 | RFM_REG_PREAMBLE_MSB = 0x20, 73 | RFM_REG_PREAMBLE_LSB = 0x21, 74 | RFM_REG_PAYLOAD_LENGTH = 0x22, 75 | RFM_REG_MODEM_CONFIG3 = 0x26, 76 | RFM_REG_INVERT_IQ = 0x33, 77 | RFM_REG_INVERT_IQ2 = 0x3b, 78 | RFM_REG_SYNC_WORD = 0x39, 79 | RFM_REG_DIO_MAPPING1 = 0x40, 80 | RFM_REG_DIO_MAPPING2 = 0x41, 81 | RFM_REG_PA_DAC = 0x4d 82 | 83 | } rfm_register_t; 84 | 85 | typedef enum { 86 | RFM_MODE_SLEEP = 0b000, 87 | RFM_MODE_STANDBY = 0b001, 88 | RFM_MODE_FSTX = 0b010, 89 | RFM_MODE_TX = 0b011, 90 | RFM_MODE_FSRX = 0b100, 91 | RFM_MODE_RXCONT = 0b101, 92 | RFM_MODE_RXSINGLE = 0b110, 93 | RFM_MODE_CAD = 0b111, 94 | RFM_MODE_LORA = 0b10000000 95 | } frm_mode_t; 96 | 97 | typedef enum { 98 | IRQ_RX_TIMEOUT_MASK = 0b10000000, 99 | IRQ_RX_DONE_MASK = 0b01000000, 100 | IRQ_TX_DONE_MASK = 0b00001000 101 | } irq_mask; 102 | 103 | /* 104 | ***************************************************************************************** 105 | * FUNCTION PROTOTYPES 106 | ***************************************************************************************** 107 | */ 108 | 109 | bool RFM_Init(); 110 | void RFM_Send_Package(sBuffer *RFM_Tx_Package, sSettings *LoRa_Settings); 111 | message_t RFM_Single_Receive(sSettings *LoRa_Settings); 112 | void RFM_Continuous_Receive(sSettings *LoRa_Settings); 113 | message_t RFM_Get_Package(sBuffer *RFM_Rx_Package); 114 | void RFM_Write(unsigned char RFM_Address, unsigned char RFM_Data); 115 | void RFM_Switch_Mode(unsigned char Mode); 116 | void RFM_Set_Tx_Power(int level, int outputPin); 117 | bool RFM_isRxDone(); 118 | void RFM_Set_OCP(unsigned char mA); 119 | 120 | unsigned char RFM_Get_Rssi(); 121 | 122 | 123 | #endif 124 | 125 | -------------------------------------------------------------------------------- /src/arduino-rfm/Struct.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | * Copyright 2017 Ideetron B.V. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | ******************************************************************************************/ 17 | /**************************************************************************************** 18 | * File: Struct.h 19 | * Author: Gerben den Hartog 20 | * Compagny: Ideetron B.V. 21 | * Website: http://www.ideetron.nl/LoRa 22 | * E-mail: info@ideetron.nl 23 | ****************************************************************************************/ 24 | /**************************************************************************************** 25 | * Created on: 06-02-2017 26 | * Supported Hardware: ID150119-02 Nexus board with RFM95 27 | ****************************************************************************************/ 28 | 29 | #include "Config.h" 30 | 31 | #ifndef STRUCT_H 32 | #define STRUCT_H 33 | 34 | /* 35 | ******************************************************************************************** 36 | * STRUCT DEFINITIONS 37 | ******************************************************************************************** 38 | */ 39 | 40 | struct sRFM_pins{ 41 | int CS; 42 | int RST; 43 | int DIO0; 44 | int DIO1; 45 | int DIO2; 46 | int DIO5; 47 | }; 48 | // common def 49 | extern const sRFM_pins RFM_pins; 50 | 51 | //Struct used for Buffers 52 | typedef struct { 53 | unsigned char *Data; 54 | unsigned char Counter; 55 | } sBuffer; 56 | 57 | //Struct used to store session data of a LoRaWAN session 58 | typedef struct { 59 | unsigned char *NwkSKey; 60 | unsigned char *AppSKey; 61 | unsigned char *DevAddr; 62 | unsigned int *Frame_Counter; 63 | } sLoRa_Session; 64 | 65 | typedef struct { 66 | unsigned char *DevEUI; 67 | unsigned char *AppEUI; 68 | unsigned char *AppKey; 69 | unsigned char *DevNonce; 70 | unsigned char *AppNonce; 71 | unsigned char *NetID; 72 | } sLoRa_OTAA; 73 | 74 | //Struct to store information of a LoRaWAN message to transmit or received 75 | typedef struct{ 76 | unsigned char MAC_Header; 77 | unsigned char DevAddr[4]; 78 | unsigned char Frame_Control; 79 | unsigned int Frame_Counter; 80 | unsigned char Frame_Port; 81 | unsigned char Frame_Options[15]; 82 | unsigned char MIC[4]; 83 | unsigned char Direction; 84 | } sLoRa_Message; 85 | 86 | //Struct used for storing settings of the mote 87 | typedef struct { 88 | unsigned char Confirm; //0x00 Unconfirmed, 0x01 Confirmed 89 | unsigned char Mport; //Port 1-223 90 | unsigned char Mote_Class; //0x00 Class A, 0x01 Class C 91 | unsigned char Datarate_Tx; //See RFM file 92 | unsigned char Datarate_Rx; //See RFM file 93 | unsigned char Channel_Tx; //See RFM file 94 | unsigned char Channel_Rx; //See RFM filed 95 | unsigned char Channel_Hopping; //0x00 No hopping, 0x01 Hopping 96 | unsigned char Transmit_Power; //0x00 to 0x0F 97 | unsigned int Rx1_Delay; 98 | unsigned int Rx2_Delay; // Rx2_Delay >= Rx1_Delay + RX1_Window 99 | unsigned int RX1_Window; 100 | unsigned int RX2_Window; 101 | } sSettings; 102 | 103 | typedef enum { 104 | CH0 = 0, 105 | CH1 = 1, 106 | CH2 = 2, 107 | #ifndef IN_865 //IN_865 only supports 3 channels 108 | CH3 = 3, 109 | CH4 = 4, 110 | CH5 = 5, 111 | CH6 = 6, 112 | CH7 = 7, 113 | #endif 114 | #ifdef EU_868 115 | CHRX2 = 8, 116 | #elif defined(IN_865) 117 | CHRX2 = 3, 118 | #else 119 | CH8 = 8, 120 | #endif 121 | MULTI = 20 122 | } channel_t; 123 | 124 | 125 | typedef enum { 126 | RFO_PIN = 0, 127 | PA_BOOST_PIN = 1 128 | } txPin_t; 129 | 130 | typedef enum { 131 | #if defined(US_915) 132 | SF10BW125 = 0x00, 133 | SF9BW125 = 0x01, 134 | SF8BW125 = 0x02, 135 | SF7BW125 = 0x03, 136 | SF8BW500 = 0x04, 137 | SF12BW500 = 0x08, 138 | SF11BW500 = 0x09, 139 | SF10BW500 = 0x0A, 140 | SF9BW500 = 0x0B, 141 | SF7BW500 = 0x0D 142 | #elif defined(AU_915) 143 | SF10BW125 = 0x00, 144 | SF9BW125 = 0x01, 145 | SF8BW125 = 0x02, 146 | SF7BW125 = 0x03, 147 | SF8BW500 = 0x04, 148 | SF12BW500 = 0x08, 149 | SF11BW500 = 0x09, 150 | SF10BW500 = 0x0A, 151 | SF9BW500 = 0x0B, 152 | SF7BW500 = 0x0D 153 | #elif defined(EU_868) 154 | SF12BW125 = 0x00, 155 | SF11BW125 = 0x01, 156 | SF10BW125 = 0x02, 157 | SF9BW125 = 0x03, 158 | SF8BW125 = 0x04, 159 | SF7BW125 = 0x05, 160 | SF7BW250 = 0x06 161 | #elif defined(AS_923) || defined(AS_923_2) 162 | SF12BW125 = 0x00, 163 | SF11BW125 = 0x01, 164 | SF10BW125 = 0x02, 165 | SF9BW125 = 0x03, 166 | SF8BW125 = 0x04, 167 | SF7BW125 = 0x05, 168 | SF7BW250 = 0x06 169 | #elif defined(IN_865) 170 | SF12BW125 = 0x00, 171 | SF11BW125 = 0x01, 172 | SF10BW125 = 0x02, 173 | SF9BW125 = 0x03, 174 | SF8BW125 = 0x04, 175 | SF7BW125 = 0x05 176 | #endif 177 | } dataRates_t; 178 | 179 | typedef enum {CLASS_A, CLASS_C} devclass_t; 180 | 181 | typedef enum {NO_RX, NEW_RX} rx_t; 182 | 183 | typedef enum {NO_ACK, NEW_ACK} ack_t; 184 | 185 | #endif 186 | -------------------------------------------------------------------------------- /.github/workflows/BuildLibrary.yml: -------------------------------------------------------------------------------- 1 | # LibraryBuild.yml 2 | # Github workflow script to test compile all examples of an Arduino library repository. 3 | # 4 | # Copyright (C) 2020 Armin Joachimsmeyer 5 | # https://github.com/ArminJo/Github-Actions 6 | # 7 | 8 | # This is the name of the workflow, visible on GitHub UI. 9 | name: LibraryBuild 10 | on: [push, pull_request] # see: https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request 11 | 12 | jobs: 13 | build: 14 | name: ${{ matrix.arduino-boards-fqbn }} - test compiling examples 15 | 16 | runs-on: ubuntu-latest # I picked Ubuntu to use shell scripts. 17 | 18 | strategy: 19 | matrix: 20 | # The matrix will produce one job for each configuration parameter of type `arduino-boards-fqbn` 21 | # In the Arduino IDE, the fqbn is printed in the first line of the verbose output for compilation as parameter -fqbn=... for the "arduino-builder -dump-prefs" command 22 | # 23 | # Examples: arduino:avr:uno, arduino:avr:leonardo, arduino:avr:nano, arduino:avr:mega 24 | # arduino:sam:arduino_due_x, arduino:samd:arduino_zero_native" 25 | # ATTinyCore:avr:attinyx5:chip=85,clock=1internal, digistump:avr:digispark-tiny, digistump:avr:digispark-pro 26 | # STM32:stm32:GenF1:pnum=BLUEPILL_F103C8 27 | # esp8266:esp8266:huzzah:eesz=4M3M,xtal=80, esp32:esp32:featheresp32:FlashFreq=80 28 | # You may add a suffix behind the fqbn with "|" to specify one board for e.g. different compile options like arduino:avr:uno|trace 29 | ############################################################################################################# 30 | arduino-boards-fqbn: 31 | - arduino:avr:uno 32 | - arduino:avr:uno|All-US_915 33 | - arduino:avr:leonardo|All-US_915 34 | - arduino:samd:nano_33_iot|All-US_915 35 | - arduino:samd:nano_33_iot 36 | - arduino:mbed:nano33ble|All-US_915 37 | - esp8266:esp8266:huzzah:eesz=4M3M,xtal=80|All-US_915 38 | - rp2040:rp2040:generic|All-US_915 39 | #- esp32:esp32:esp32:FlashFreq=80 40 | #- esp32:esp32:featheresp32:FlashFreq=80|All-US_915 41 | 42 | # Specify parameters for each board. 43 | # Parameters can be: platform-url, examples-exclude and examples-build-properties 44 | # With examples-exclude you may exclude specific examples for a board. Use a space separated list. 45 | ############################################################################################################# 46 | include: 47 | - arduino-boards-fqbn: arduino:avr:uno 48 | sketch-names: class_c_abp.ino,class_c_otaa_ack.ino # Comma separated list of sketch names (no path required) or patterns to use in build 49 | build-properties: 50 | class_c_abp: 51 | -DDEBUG 52 | -DEU_868 53 | -D_CLASS_C_ 54 | class_c_otaa_ack: 55 | -DDEBUG 56 | -DEU_868 57 | -D_CLASS_C_ 58 | All: 59 | -DEU_868 60 | -DDEBUG 61 | - arduino-boards-fqbn: arduino:avr:uno|All-US_915 62 | build-properties: 63 | All: 64 | -DUS_915 65 | -DDEBUG 66 | - arduino-boards-fqbn: arduino:avr:leonardo|All-US_915 67 | build-properties: 68 | All: 69 | -DUS_915 70 | -DDEBUG 71 | - arduino-boards-fqbn: arduino:samd:nano_33_iot 72 | build-properties: 73 | class_c_abp: 74 | -DDEBUG 75 | -DEU_868 76 | -D_CLASS_C_ 77 | class_c_otaa_ack: 78 | -DDEBUG 79 | -DEU_868 80 | -D_CLASS_C_ 81 | All: 82 | -DEU_868 83 | - arduino-boards-fqbn: arduino:samd:nano_33_iot|All-US_915 84 | build-properties: 85 | All: 86 | -DUS_915 87 | -DDEBUG 88 | - arduino-boards-fqbn: arduino:mbed:nano33ble|All-US_915 89 | build-properties: 90 | All: 91 | -DUS_915 92 | -DDEBUG 93 | - arduino-boards-fqbn: esp8266:esp8266:huzzah:eesz=4M3M,xtal=80|All-US_915 94 | platform-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json 95 | build-properties: 96 | All: 97 | -DUS_915 98 | -DDEBUG 99 | - arduino-boards-fqbn: rp2040:rp2040:generic|All-US_915 100 | platform-url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 101 | build-properties: 102 | All: 103 | -DUS_915 104 | -DDEBUG 105 | #- arduino-boards-fqbn: esp32:esp32:esp32:FlashFreq=80 106 | # platform-url: https://github.com/espressif/arduino-esp32/releases/download/3.0.0-alpha2/package_esp32_dev_index.json 107 | # extra-arduino-cli-args: "--warnings default" 108 | # build-properties: 109 | # class_c_abp: 110 | # -DEU_868 111 | # -D_CLASS_C_ 112 | # class_c_otaa_ack: 113 | # -DEU_868 114 | # -D_CLASS_C_ 115 | #All: 116 | # -DEU_868 117 | #- arduino-boards-fqbn: esp32:esp32:featheresp32:FlashFreq=80|All-US_915 118 | # platform-url: https://github.com/espressif/arduino-esp32/releases/download/3.0.0-alpha2/package_esp32_dev_index.json 119 | # extra-arduino-cli-args: "--warnings default" 120 | # build-properties: 121 | # All: 122 | # -DUS_915 123 | 124 | # Do not cancel all jobs / architectures if one job fails 125 | fail-fast: false 126 | 127 | # This is the list of steps this job will run. 128 | steps: 129 | 130 | # First of all, we clone the repo using the `checkout` action. 131 | - name: Checkout 132 | uses: actions/checkout@master 133 | 134 | - name: Compile all examples 135 | uses: ArminJo/arduino-test-compile@master 136 | with: 137 | arduino-board-fqbn: ${{ matrix.arduino-boards-fqbn }} 138 | platform-url: ${{ matrix.platform-url }} 139 | build-properties: ${{ toJson(matrix.build-properties) }} 140 | extra-arduino-cli-args: ${{ matrix.extra-arduino-cli-args }} 141 | 142 | -------------------------------------------------------------------------------- /examples/class_a_otaa_esp_deepsleep/class_a_otaa_esp_deepsleep.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Example of OTAA device with ESP8266 or ESP32 using deep sleep wihtout losing LoRaWAN settings. 3 | * A minimum number of pin is also used in this example. For slighly faster processing, 4 | * DIO0 and DIO1 should be used. 5 | */ 6 | #include 7 | #include 8 | 9 | // OTAA credentials 10 | const char *devEui = "0000000000000000"; 11 | const char *appEui = "0000000000000000"; 12 | const char *appKey = "00000000000000000000000000000000"; 13 | 14 | unsigned int counter = 0; // message counter 15 | 16 | char myStr[50]; 17 | char outStr[255]; 18 | byte recvStatus = 0; 19 | 20 | const sRFM_pins RFM_pins = { 21 | .CS = 15, 22 | .RST = 2, //Could be connected to the reset ESP pin of ESP8266, if so put -1 23 | .DIO0 = 0, 24 | .DIO1 = 4, 25 | .DIO2 = -1, 26 | .DIO5 = -1, 27 | }; 28 | 29 | struct RTCRAM 30 | { 31 | unsigned char DevEUI[8]; 32 | unsigned char AppEUI[8]; 33 | unsigned char AppKey[16]; 34 | unsigned char DevNonce[2]; 35 | unsigned char AppNonce[3]; 36 | unsigned char NetID[3]; 37 | unsigned char Address_Tx[4]; 38 | unsigned char NwkSKey[16]; 39 | unsigned char AppSKey[16]; 40 | unsigned int Frame_Counter_Tx; 41 | sSettings LoRa_Settings; 42 | unsigned int counter; 43 | }; 44 | #if defined(ESP32) 45 | RTC_DATA_ATTR RTCRAM rtcRAM; 46 | #else 47 | RTCRAM rtcRAM; 48 | #endif 49 | 50 | void deepsleep(uint64_t us) 51 | { 52 | Serial.println("We go to deep sleep"); 53 | delay(10); 54 | lora.sleep(); 55 | #if defined(ESP8266) 56 | ESP.deepSleep(us, WAKE_RF_DISABLED); 57 | #elif defined(ESP32) 58 | esp_sleep_enable_timer_wakeup(us); 59 | esp_deep_sleep_start(); 60 | #else 61 | Serial.print("Deep sleep not supported by this example"); 62 | #endif 63 | } 64 | 65 | void saveVariableRTC() 66 | { 67 | memcpy(rtcRAM.DevEUI, lora.DevEUI, sizeof(rtcRAM.DevEUI)); 68 | memcpy(rtcRAM.AppEUI, lora.AppEUI, sizeof(rtcRAM.AppEUI)); 69 | memcpy(rtcRAM.AppKey, lora.AppKey, sizeof(rtcRAM.AppKey)); 70 | memcpy(rtcRAM.DevNonce, lora.DevNonce, sizeof(rtcRAM.DevNonce)); 71 | memcpy(rtcRAM.AppNonce, lora.AppNonce, sizeof(rtcRAM.AppNonce)); 72 | memcpy(rtcRAM.NetID, lora.NetID, sizeof(rtcRAM.NetID)); 73 | memcpy(rtcRAM.Address_Tx, lora.Address_Tx, sizeof(rtcRAM.Address_Tx)); 74 | memcpy(rtcRAM.NwkSKey, lora.NwkSKey, sizeof(rtcRAM.NwkSKey)); 75 | memcpy(rtcRAM.AppSKey, lora.AppSKey, sizeof(rtcRAM.AppSKey)); 76 | memcpy(&rtcRAM.Frame_Counter_Tx, &lora.Frame_Counter_Tx, sizeof(rtcRAM.Frame_Counter_Tx)); 77 | memcpy(&rtcRAM.LoRa_Settings, &lora.LoRa_Settings, sizeof(rtcRAM.LoRa_Settings)); 78 | memcpy(&rtcRAM.counter, &counter, sizeof(rtcRAM.counter)); 79 | #if defined(ESP8266) 80 | ESP.rtcUserMemoryWrite(32, (uint32_t *)&rtcRAM, sizeof(rtcRAM)); 81 | #endif 82 | } 83 | 84 | void loadVariableRTC() 85 | { 86 | #if defined(ESP8266) 87 | ESP.rtcUserMemoryRead(32, (uint32_t *)&rtcRAM, sizeof(rtcRAM)); 88 | #endif 89 | memcpy(lora.DevEUI, rtcRAM.DevEUI, sizeof(rtcRAM.DevEUI)); 90 | memcpy(lora.AppEUI, rtcRAM.AppEUI, sizeof(rtcRAM.AppEUI)); 91 | memcpy(lora.AppKey, rtcRAM.AppKey, sizeof(rtcRAM.AppKey)); 92 | memcpy(lora.DevNonce, rtcRAM.DevNonce, sizeof(rtcRAM.DevNonce)); 93 | memcpy(lora.AppNonce, rtcRAM.AppNonce, sizeof(rtcRAM.AppNonce)); 94 | memcpy(lora.NetID, rtcRAM.NetID, sizeof(rtcRAM.NetID)); 95 | memcpy(lora.Address_Tx, rtcRAM.Address_Tx, sizeof(rtcRAM.Address_Tx)); 96 | memcpy(lora.NwkSKey, rtcRAM.NwkSKey, sizeof(rtcRAM.NwkSKey)); 97 | memcpy(lora.AppSKey, rtcRAM.AppSKey, sizeof(rtcRAM.AppSKey)); 98 | memcpy(&lora.Frame_Counter_Tx, &rtcRAM.Frame_Counter_Tx, sizeof(rtcRAM.Frame_Counter_Tx)); 99 | memcpy(&lora.LoRa_Settings, &rtcRAM.LoRa_Settings, sizeof(rtcRAM.LoRa_Settings)); 100 | memcpy(&counter, &rtcRAM.counter, sizeof(rtcRAM.counter)); 101 | } 102 | 103 | #if defined(ESP8266) 104 | uint32_t magicBytes; // if previous data save on rtc, this equal 'magi' (183510009) 105 | void loadMagicByte() 106 | { 107 | ESP.rtcUserMemoryRead(127, &magicBytes, sizeof(magicBytes)); 108 | } 109 | 110 | void saveMagicByte() 111 | { 112 | magicBytes = 183510009; 113 | ESP.rtcUserMemoryWrite(127, &magicBytes, 4); 114 | } 115 | #endif 116 | 117 | void setup() 118 | { 119 | // Setup loraid access 120 | Serial.begin(9600); 121 | while (!Serial) 122 | ; 123 | if (!lora.init()) 124 | { 125 | Serial.println("RFM95 not detected"); 126 | delay(5000); 127 | deepsleep(10000000); 128 | } 129 | 130 | // Join procedure 131 | bool isJoined = true; 132 | #if defined(ESP8266) 133 | loadMagicByte(); 134 | if (magicBytes != 183510009) 135 | #else 136 | if (rtcRAM.counter == 0) 137 | #endif 138 | { 139 | Serial.println("Cold boot Join procedure needs to be performed"); 140 | isJoined = false; 141 | } 142 | 143 | if (!isJoined) 144 | { 145 | // Set LoRaWAN Class change CLASS_A or CLASS_C 146 | lora.setDeviceClass(CLASS_A); 147 | // Set Data Rate 148 | lora.setDataRate(SF9BW125); 149 | // set channel to random 150 | lora.setChannel(MULTI); 151 | 152 | // Put OTAA Key and DevAddress here 153 | lora.setDevEUI(devEui); 154 | lora.setAppEUI(appEui); 155 | lora.setAppKey(appKey); 156 | while (!isJoined) 157 | { 158 | Serial.println("Joining..."); 159 | isJoined = lora.join(); 160 | 161 | // wait for 10s to try again if not join 162 | if (!isJoined) 163 | delay(10000); 164 | } 165 | Serial.println("Joined to network!\nWe now save LoRaWAN information"); 166 | delay(100); // Necessary for ESP32 in order to properly save data on RTC ram 167 | saveVariableRTC(); 168 | #if defined(ESP8266) 169 | saveMagicByte(); 170 | #endif 171 | } 172 | else 173 | { 174 | Serial.println("Previously joined network, we applied the save parameters"); 175 | loadVariableRTC(); 176 | } 177 | 178 | // Send a upling 179 | sprintf(myStr, "Counter-%d", counter); 180 | Serial.print("Sending: "); 181 | Serial.println(myStr); 182 | lora.sendUplink(myStr, strlen(myStr), 0, 1); 183 | counter++; 184 | 185 | // cycle over send and receive data with LoRa update 186 | lora.update(); 187 | recvStatus = lora.readData(outStr); 188 | if (recvStatus) 189 | { 190 | Serial.println(outStr); 191 | } 192 | delay(100); // Necessary for ESP32 in order to properly save data on RTC ram 193 | // Save pertinent information on RTC ram 194 | saveVariableRTC(); 195 | 196 | // Deep sleep for 10 s 197 | deepsleep(10000000); 198 | } 199 | 200 | void loop() 201 | { 202 | // We never get here 203 | Serial.print("Oups, you use this example without an ESP device"); 204 | delay(10000); 205 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![LibraryBuild](https://github.com/BeelanMX/Beelan-LoRaWAN/workflows/LibraryBuild/badge.svg?branch=master) 2 | 3 | > ⚠️ **This repository has been archived and is no longer maintained.** 4 | > 5 | > This library is in read-only mode and will not accept further changes, issues, or pull requests. 6 | > It may still be useful as a reference or for legacy projects, but it is not recommended for new development. 7 | 8 | 9 | Arduino LoRaWAN library 10 | ==================== 11 | This repository contains the simple LoRaWAN library encapsulated to run in the generic platform, allowing using the SX1272, SX1276 transceivers and compatible modules (such as some HopeRF RFM9x modules). 12 | 13 | This repository is all based on the library originally created by [Ideetron B.V.](https://github.com/Ideetron) This library is slightly 14 | modified and this [repo]( https://git.antares.id/lorawan-loraid/arduino-loraid), with the principal purpose to have an LoRaWAN MAC layer for arduino with an easy API. 15 | 16 | 17 | 18 | 19 | 20 | Features 21 | -------- 22 | The Arduino LoRaWAN library supports LoRaWAN Class A and Class C implementations operating in EU-868, AS-923, US-915 and AU-915 bands. Notice that this library is fairly simple with the aim of demonstrating the LoRaWAN capabilities. 23 | 24 | ### What certainly works: 25 | - Sending packets uplink, taking into account duty cycling. 26 | - Custom frequencies and data rate settings. 27 | - Receiving downlink packets in the RX1 window (EU-868, AS-923,US-915,AU-915). 28 | - Over-the-air activation (OTAA / joining) (EU-868, AS-923). 29 | - Class C operation. 30 | - Receiving downlink packets in the RX1 window (US-915). 31 | - Receiving downlink packets in the RX2 window. 32 | - Over-the-air activation (OTAA / joining) (US-915). 33 | 34 | ### What has not been tested: 35 | - Receiving and processing MAC commands. 36 | 37 | If you try one of these untested features and it works, be sure to let 38 | us know (creating a Github issue is probably the best way for that, also so you can submit a pull request to improve this repository). 39 | 40 | Installing the library 41 | ---------- 42 | Please refer to the wiki's [first steps](https://github.com/ElectronicCats/Beelan-LoRaWAN/wiki/2.-First-steps) section for instructions on installing the library. 43 | 44 | Configuration 45 | ------------- 46 | A number of features can be configured or disabled by editing the 47 | `config.h` file in the library folder. Unfortunately the Arduino 48 | environment does not offer any way to do this (compile-time) 49 | configuration from the sketch, so be careful to recheck your 50 | configuration when you switch between sketches or update the library. 51 | 52 | At the very least, you should set the right type of board in config.h, most other values should be fine at their defaults. 53 | 54 | When using the US_915 you need to select which sub-band you will use, by default it is sub-band 6. 55 | 56 | We have to manually set the frequency plan (EU_868, US_915, AS_923, AS_923_2...) in the Config.h of the library. 57 | 58 | Supported hardware 59 | ------------------ 60 | This library is intended to be used with plain LoRa transceivers, 61 | connecting to them using SPI. In particular, the SX1272 and SX1276 62 | families are supported (which should include SX1273, SX1277, SX1278 and 63 | SX1279 which only differ in the available frequencies, bandwidths and 64 | spreading factors). It has been tested with both SX1272 and SX1276 65 | chips, using the Semtech SX1272 evaluation board and the HopeRF RFM92 66 | and RFM95 boards (which supposedly contain an SX1272 and SX1276 chip 67 | respectively). 68 | 69 | Some of the supported pre-built board currently available in the market are: 70 | 71 | - [Electronic Cats BastWAN](https://electroniccats.com/store/bastwan/) 72 | - [Electronic Cats CatWAN USB-Stick](https://electroniccats.com/store/catwan-usb-stick/) 73 | - [Electronic Cats CatWAN Relay Board](https://electroniccats.com/store/catwan-relay-board/) 74 | - [Electronic Cats CatWAN Shield](https://electroniccats.com/store/loracatshield/) 75 | - [Cytron Shield LoRa-RFM](https://www.cytron.io/p-shield-lora-rfm) 76 | - [Dragino](http://www.dragino.com/products/module/item/102-lora-shield.html) 77 | 78 | 79 | This library has been tested using: 80 | 81 | - Arduino Uno 82 | - WeMos D1 R2 (ESP8266 family board) 83 | - ESP32 84 | - Electronic Cats CatWAN USB-Stick 85 | - Electronic Cats Bast-WAN (Based on SAMR34) 86 | - NINA B302 is NRF52840 core 87 | - STMDuino 88 | - RENESAS compatible boards 89 | 90 | Connections 91 | ----------- 92 | To make this library work, your Arduino (or whatever Arduino-compatible 93 | board you are using) should be connected to the transceiver. The exact 94 | connections are a bit dependent on the transceiver board and Arduino 95 | used, so this section tries to explain what each connection is for and 96 | in what cases it is (not) required. 97 | 98 | 99 | Pin mapping 100 | ----------- 101 | As described above, most connections can use arbitrary I/O pins on the 102 | Arduino side. To tell the arduino LoRaWAN library about these, a pin mapping struct is used in the sketch file. 103 | 104 | For example, this could look like this: 105 | 106 | sRFM_pins RFM_pins = { 107 | .CS = SS, 108 | .RST = RFM_RST, 109 | .DIO0 = RFM_DIO0, 110 | .DIO1 = RFM_DIO1, 111 | .DIO2 = RFM_DIO2, 112 | .DIO5 = RFM_DIO5 113 | }; 114 | 115 | API 116 | -------- 117 | - See [API.md](API.md). 118 | 119 | Tests 120 | ------- 121 | - See [Test Folder](test/README.md) 122 | 123 | ## How to contribute 124 | Contributions are welcome! 125 | 126 | Please read the document [**Contribution Manual**](https://github.com/ElectronicCats/electroniccats-cla/blob/main/electroniccats-contribution-manual.md) which will show you how to contribute your changes to the project. 127 | 128 | ✨ Thanks to all our [contributors](https://github.com/ElectronicCats/Beelan-LoRaWAN/graphs/contributors)! ✨ 129 | 130 | See [**_Electronic Cats CLA_**](https://github.com/ElectronicCats/electroniccats-cla/blob/main/electroniccats-cla.md) for more information. 131 | 132 | See the [**community code of conduct**](https://github.com/ElectronicCats/electroniccats-cla/blob/main/electroniccats-community-code-of-conduct.md) for a vision of the community we want to build and what we expect from it. 133 | 134 | Maintainer 135 | ------- 136 | 137 | Electronic Cats invests time and resources providing this open source design, please support Electronic Cats and open-source hardware by purchasing products from Electronic Cats! 138 | Thanks Beelan 139 | 140 | 141 |

142 | 143 |

144 |
145 | 146 | License 147 | ------- 148 | Most source files in this repository are made available under the 149 | [MIT License](https://github.com/ElectronicCats/Beelan-LoRaWAN/blob/master/LICENSE.txt). The examples which use a more liberal 150 | license. Some of the AES code is available under the LGPL. Refer to each 151 | individual source file for more details. 152 | 153 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | 2 | ### LoRa module initialization 3 | Initialize LoRa/LoRaWAN module. Must be called once in the Arduino setup block. 4 | 5 | #### Syntax 6 | ```c 7 | void init(void); 8 | ``` 9 | 10 | #### Example 11 | ```c 12 | void setup() { 13 | // Setup loraid access 14 | lora.init(); 15 | ... 16 | } 17 | ``` 18 | 19 | ### LoRa module sleep 20 | Put LoRa/LoRaWAN module to sleep. 21 | 22 | #### Syntax 23 | ```c 24 | void sleep(void); 25 | ``` 26 | 27 | #### Example 28 | ```c 29 | void loop() { 30 | ... 31 | // Put module to sleep 32 | lora.sleep(); 33 | ... 34 | } 35 | ``` 36 | 37 | ### LoRa module sleep 38 | Wake LoRa/LoRaWAN module. 39 | 40 | #### Syntax 41 | ```c 42 | void wakeUp(void); 43 | ``` 44 | 45 | #### Example 46 | ```c 47 | void loop() { 48 | ... 49 | // Wake up module 50 | lora.wakeUp(); 51 | ... 52 | } 53 | ``` 54 | 55 | ### Setup Authentication Keys for ABP activation 56 | Setup authentication keys for your LoRaWAN device, including device address. 57 | 58 | #### Syntax 59 | ```c 60 | void setAppSKey(unsigned char *ApskKey_in); 61 | void setNwkSKey(unsigned char *NwkKey_in); 62 | ``` 63 | 64 | #### Example 65 | ```c 66 | void setup() { 67 | // Setup loraid access 68 | if(!lora.init()){ 69 | Serial.println("RFM95 not detected"); 70 | while(1); 71 | } 72 | ... 73 | 74 | // Put Antares Key and DevAddress here 75 | lora.setNwkSKey("b7300d9f68b649ed30530f9dd69f9afe"); 76 | lora.setAppSKey("9d52eef7fab63eda18794d0e503ddf20"); 77 | ... 78 | 79 | } 80 | ``` 81 | 82 | ### Setup Device address 83 | Setup device address for activating the device. 84 | 85 | #### Syntax 86 | ```c 87 | void setDevAddr(unsigned char *devAddr_in); 88 | ``` 89 | 90 | #### Example 91 | ```c 92 | void setup() { 93 | // Setup loraid access 94 | if(!lora.init()){ 95 | Serial.println("RFM95 not detected"); 96 | while(1); 97 | } 98 | ... 99 | 100 | // Put Antares Key and DevAddress here 101 | lora.setDevAddr("07000007"); 102 | ... 103 | 104 | } 105 | ``` 106 | 107 | ### Setup Authentication Keys for OTAA activation 108 | Setup authentication keys for your LoRaWAN device, including device address. 109 | 110 | #### Syntax 111 | ```c 112 | void setDevEUI(const char *devEUI_in); 113 | void setAppEUI(const char *appEUI_in); 114 | void setAppKey(const char *appKey_in); 115 | ``` 116 | 117 | #### Example 118 | ```c 119 | void setup() { 120 | // Setup loraid access 121 | if(!lora.init()){ 122 | Serial.println("RFM95 not detected"); 123 | while(1); 124 | } 125 | ... 126 | 127 | // Put OTAA credentials here 128 | lora.setDevEUI("b7300d9f68b649ed"); 129 | lora.setAppEUI("8b649ed30530f9dd"); 130 | lora.setAppKey("9d52eef7fab63eda18794d0e503ddf20"); 131 | ... 132 | 133 | } 134 | ``` 135 | ### Join Procedure 136 | Need to join in the network 137 | 138 | #### Syntax 139 | ```c 140 | bool join(); 141 | ``` 142 | 143 | #### Example 144 | ```c 145 | void setup() { 146 | // Setup loraid access 147 | if(!lora.init()){ 148 | Serial.println("RFM95 not detected"); 149 | while(1); 150 | } 151 | 152 | // Set LoRaWAN Class change CLASS_A or CLASS_C 153 | lora.setDeviceClass(CLASS_A); 154 | 155 | // Set Data Rate 156 | lora.setDataRate(SF9BW125); 157 | 158 | // set channel to random 159 | lora.setChannel(MULTI); 160 | 161 | // Put OTAA Key and DevAddress here 162 | lora.setDevEUI(devEui); 163 | lora.setAppEUI(appEui); 164 | lora.setAppKey(appKey); 165 | 166 | // Join procedure 167 | bool isJoined; 168 | do { 169 | Serial.println("Joining..."); 170 | isJoined = lora.join(); 171 | 172 | //wait for 10s to try again 173 | delay(10000); 174 | }while(!isJoined); 175 | Serial.println("Joined to network"); 176 | } 177 | ``` 178 | 179 | 180 | ### Set Device Class 181 | Set class of the device (Class A or Class C). Input as `CLASS_A` or `CLASS_C` enum. 182 | #### Syntax 183 | ```c 184 | void setDeviceClass(devclass_t dev_class); 185 | ``` 186 | 187 | #### Example 188 | ```c 189 | void setup() { 190 | // Setup loraid access 191 | if(!lora.init()){ 192 | Serial.println("RFM95 not detected"); 193 | while(1); 194 | } 195 | ... 196 | 197 | // Set LoRaWAN Class 198 | lora.setDeviceClass(CLASS_A); 199 | ... 200 | } 201 | 202 | ``` 203 | ### Set Channel 204 | You can set channel allowed in your region (AS_923, EU_868 or US915). 205 | - For US_915 the channels can be [0 - 7] 206 | - Use MULTI if you want random channel 207 | 208 | ### Syntax 209 | ```c 210 | void setChannel(unsigned char channel); 211 | ``` 212 | #### Example 213 | ```c 214 | void setup() { 215 | // Setup loraid access 216 | if(!lora.init()){ 217 | Serial.println("RFM95 not detected"); 218 | while(1); 219 | } 220 | ... 221 | 222 | // Set random Channel 223 | lora.setChannel(MULTI); 224 | } 225 | 226 | ``` 227 | 228 | ### Set Data Rate 229 | You can set data rate allowed in your region (AS_923, EU_868 or US915). 230 | 231 | ## For AU915 232 | | data_rate | Name | Config | Direction 233 | |--------------|-------|-----------------|---------- 234 | | 0 | DR0 | SF12 BW 125 KHz | Uplink 235 | | 1 | DR1 | SF11 BW 125 KHz | Uplink 236 | | 2 | DR2 | SF10 BW 125 KHz | Uplink 237 | | 3 | DR3 | SF9 BW 125 KHz | Uplink 238 | | 4 | DR4 | SF8 BW 500 KHz | Uplink 239 | | 5 | DR5 | SF7 BW 500 KHz | Uplink 240 | | 6 | DR6 | SF8 BW 500 KHz | Uplink 241 | | 7 | RFU | N/A | N/A 242 | | 8 | DR8 | SF12 BW 500 KHz | Downlink 243 | | 9 | DR9 | SF11 BW 500 KHz | Downlink 244 | | 10 | DR10 | SF10 BW 500 KHz | Downlink 245 | | 11 | DR11 | SF9 BW 500 KHz | Downlink 246 | | 12 | DR12 | SF8 BW 500 KHz | Downlink 247 | | 13 | DR13 | SF7 BW 500 KHz | Downlink 248 | | 14 | RFU | N/A | N/A 249 | | 15 | Defined in LoRaWAN | N/A | 250 | 251 | For AU915 is important to remark that DR0-DR5 are only for UPLINKS 252 | and DR8-DR13 are only for DOWNLINKS 253 | 254 | 255 | ## For AS923 or EU868 256 | | data_rate | Name | Config |Direction 257 | |-----------|------|-----------------|---------------- 258 | | 0 | DR0 | SF12 BW 125 KHz | Uplink/Downlink 259 | | 1 | DR1 | SF11 BW 125 KHz | Uplink/Downlink 260 | | 2 | DR2 | SF10 BW 125 KHz | Uplink/Downlink 261 | | 3 | DR3 | SF9 BW 125 KHz | Uplink/Downlink 262 | | 4 | DR4 | SF8 BW 125 KHz | Uplink/Downlink 263 | | 5 | DR5 | SF7 BW 125 KHz | Uplink/Downlink 264 | | 6 | DR6 | SF7 BW 250 KHz | Uplink/Downlink 265 | 266 | ## For US915 267 | | data_rate | Name | Config | Direction 268 | |--------------|-------|-----------------|---------- 269 | | 0 | DR0 | SF10 BW 125 KHz | Uplink 270 | | 1 | DR1 | SF9 BW 125 KHz | Uplink 271 | | 2 | DR2 | SF8 BW 125 KHz | Uplink 272 | | 3 | DR3 | SF7 BW 125 KHz | Uplink 273 | | 4 | DR4 | SF8 BW 500 KHz | Uplink 274 | | 5:7 | RFU | N/A | N/A 275 | | 8 | DR8 | SF12 BW 500 KHz | Downlink 276 | | 9 | DR9 | SF11 BW 500 KHz | Downlink 277 | | 10 | DR10 | SF10 BW 500 KHz | Downlink 278 | | 11 | DR11 | SF9 BW 500 KHz | Downlink 279 | | 12 | DR12 | SF8 BW 500 KHz | Downlink 280 | | 13 | DR13 | SF7 BW 500 KHz | Downlink 281 | 282 | For US915 is important to remark that DR0-DR4 are only for UPLINKS 283 | and DR8-DR10 are only for DOWNLINKS 284 | 285 | *RFU: Reserved for future use 286 | 287 | ### Syntax 288 | ```c 289 | void setDataRate(unsigned char data_rate); 290 | ``` 291 | 292 | #### Example 293 | ```c 294 | void setup() { 295 | // Setup loraid access 296 | if(!lora.init()){ 297 | Serial.println("RFM95 not detected"); 298 | while(1); 299 | } 300 | ... 301 | 302 | // Set Data Rate to SF10 BW 125 KHz 303 | lora.setDataRate(2); 304 | } 305 | 306 | ``` 307 | 308 | ### Send data to LoRaWAN 309 | You need to specify the length of data you want to send and also the message type (unconfirmed or confirmed message). Set `confirm = 0` to send unconfirmed message and `confirm = 1`' to send confirmed message. 310 | 311 | For mport, you are free to utilize values between 1-223, it is entirely up to you to choose the values your node and infrastructure software sets or checks for. 312 | 313 | #### Syntax 314 | ```c 315 | void sendUplink(char *data, unsigned int len, unsigned char confirm, unsigned char mport); 316 | ``` 317 | 318 | #### Example 319 | ```c 320 | void loop() { 321 | // put your main code here, to run repeatedly: 322 | char myStr[] = "Ini data LoRaku"; 323 | 324 | lora.sendUplink(myStr, strlen(myStr), 0, 1); 325 | ... 326 | 327 | } 328 | ``` 329 | 330 | 331 | ### Update and run LoRa FSM 332 | Update and run the LoRa Finite State Machine (FSM). This line should be put inside the Arduino `loop` block. 333 | 334 | #### Syntax 335 | ```c 336 | void update(void); 337 | ``` 338 | 339 | #### Example 340 | ```c 341 | void loop() { 342 | ... 343 | 344 | // Check Lora RX 345 | lora.update(); 346 | } 347 | 348 | ``` 349 | 350 | In this version we'll try to use interrupts in order to eliminate this FSM. 351 | 352 | ### Check and retrieve incoming data 353 | Check for the latest incoming data from server either in binary or string format. You need to provide char buffer to read the data. 354 | #### Syntax 355 | ```c 356 | void readData(void); 357 | ``` 358 | 359 | #### Example 360 | ```c 361 | 362 | char buffer_rx[255]; 363 | 364 | void setup() { 365 | ... 366 | } 367 | 368 | void loop() { 369 | int recvStatus; 370 | ... 371 | 372 | // LoRa FSM 373 | lora.update(); 374 | 375 | // Check data 376 | recvStatus = lora.readData(buffer_rx); 377 | if(recvStatus) { 378 | Serial.println(buffer_rx); 379 | } 380 | } 381 | ``` 382 | -------------------------------------------------------------------------------- /src/arduino-rfm/AES-128.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | * Copyright 2017 Ideetron B.V. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | ******************************************************************************************/ 17 | /**************************************************************************************** 18 | * File: AES-128.cpp 19 | * Author: Gerben den Hartog 20 | * Compagny: Ideetron B.V. 21 | * Website: http://www.ideetron.nl/LoRa 22 | * E-mail: info@ideetron.nl 23 | ****************************************************************************************/ 24 | /**************************************************************************************** 25 | * Created on: 13-01-2017 26 | * Supported Hardware: ID150119-02 Nexus board with RFM95 27 | ****************************************************************************************/ 28 | 29 | #include 30 | #include "AES-128.h" 31 | 32 | 33 | static const unsigned char S_Table[16][16] = { 34 | {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, 35 | {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, 36 | {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, 37 | {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, 38 | {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, 39 | {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, 40 | {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, 41 | {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, 42 | {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, 43 | {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, 44 | {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, 45 | {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, 46 | {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, 47 | {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, 48 | {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, 49 | {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} 50 | }; 51 | 52 | 53 | /* 54 | ***************************************************************************************** 55 | * Title : AES_Add_Round_Key 56 | * Description : 57 | ***************************************************************************************** 58 | */ 59 | static void AES_Add_Round_Key(unsigned char *Round_Key, unsigned char (*State)[4]) 60 | { 61 | unsigned char Row, Collum; 62 | 63 | for(Collum = 0; Collum < 4; Collum++) 64 | { 65 | for(Row = 0; Row < 4; Row++) 66 | { 67 | State[Row][Collum] ^= Round_Key[Row + (Collum << 2)]; 68 | } 69 | } 70 | } //AES_Add_Round_Key 71 | 72 | /* 73 | ***************************************************************************************** 74 | * Title : AES_Sub_Byte 75 | * Description : 76 | ***************************************************************************************** 77 | */ 78 | static unsigned char AES_Sub_Byte(unsigned char Byte) 79 | { 80 | // unsigned char S_Row,S_Collum; 81 | // unsigned char S_Byte; 82 | // 83 | // S_Row = ((Byte >> 4) & 0x0F); 84 | // S_Collum = ((Byte >> 0) & 0x0F); 85 | // S_Byte = S_Table [S_Row][S_Collum]; 86 | 87 | return S_Table [ ((Byte >> 4) & 0x0F) ] [ ((Byte >> 0) & 0x0F) ]; 88 | } // AES_Sub_Byte 89 | 90 | /* 91 | ***************************************************************************************** 92 | * Title : AES_Shift_Rows 93 | * Description : 94 | ***************************************************************************************** 95 | */ 96 | static void AES_Shift_Rows(unsigned char (*State)[4]) 97 | { 98 | unsigned char Buffer; 99 | 100 | //Store firt byte in buffer 101 | Buffer = State[1][0]; 102 | //Shift all bytes 103 | State[1][0] = State[1][1]; 104 | State[1][1] = State[1][2]; 105 | State[1][2] = State[1][3]; 106 | State[1][3] = Buffer; 107 | 108 | Buffer = State[2][0]; 109 | State[2][0] = State[2][2]; 110 | State[2][2] = Buffer; 111 | Buffer = State[2][1]; 112 | State[2][1] = State[2][3]; 113 | State[2][3] = Buffer; 114 | 115 | Buffer = State[3][3]; 116 | State[3][3] = State[3][2]; 117 | State[3][2] = State[3][1]; 118 | State[3][1] = State[3][0]; 119 | State[3][0] = Buffer; 120 | } // AES_Shift_Rows 121 | 122 | 123 | /* 124 | ***************************************************************************************** 125 | * Title : AES_Mix_Collums 126 | * Description : 127 | ***************************************************************************************** 128 | */ 129 | static void AES_Mix_Collums(unsigned char (*State)[4]) 130 | { 131 | unsigned char Row,Collum; 132 | unsigned char a[4], b[4]; 133 | 134 | 135 | for(Collum = 0; Collum < 4; Collum++) 136 | { 137 | for(Row = 0; Row < 4; Row++) 138 | { 139 | a[Row] = State[Row][Collum]; 140 | b[Row] = (State[Row][Collum] << 1); 141 | 142 | if((State[Row][Collum] & 0x80) == 0x80) 143 | { 144 | b[Row] ^= 0x1B; 145 | } 146 | } 147 | 148 | State[0][Collum] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; 149 | State[1][Collum] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; 150 | State[2][Collum] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; 151 | State[3][Collum] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; 152 | } 153 | } // AES_Mix_Collums 154 | 155 | /* 156 | ***************************************************************************************** 157 | * Title : AES_Calculate_Round_Key 158 | * Description : 159 | ***************************************************************************************** 160 | */ 161 | static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key) 162 | { 163 | unsigned char i, j, b, Rcon; 164 | unsigned char Temp[4]; 165 | 166 | 167 | //Calculate Rcon 168 | Rcon = 0x01; 169 | while(Round != 1) 170 | { 171 | b = Rcon & 0x80; 172 | Rcon = Rcon << 1; 173 | 174 | if(b == 0x80) 175 | { 176 | Rcon ^= 0x1b; 177 | } 178 | Round--; 179 | } 180 | 181 | // Calculate first Temp 182 | // Copy laste byte from previous key and substitute the byte, but shift the array contents around by 1. 183 | Temp[0] = AES_Sub_Byte( Round_Key[12 + 1] ); 184 | Temp[1] = AES_Sub_Byte( Round_Key[12 + 2] ); 185 | Temp[2] = AES_Sub_Byte( Round_Key[12 + 3] ); 186 | Temp[3] = AES_Sub_Byte( Round_Key[12 + 0] ); 187 | 188 | // XOR with Rcon 189 | Temp[0] ^= Rcon; 190 | 191 | // Calculate new key 192 | for(i = 0; i < 4; i++) 193 | { 194 | for(j = 0; j < 4; j++) 195 | { 196 | Round_Key[j + (i << 2)] ^= Temp[j]; 197 | Temp[j] = Round_Key[j + (i << 2)]; 198 | } 199 | } 200 | } // AES_Calculate_Round_Key 201 | 202 | /* 203 | ***************************************************************************************** 204 | * Title : AES_Encrypt 205 | * Description : 206 | ***************************************************************************************** 207 | */ 208 | void AES_Encrypt(unsigned char *Data, unsigned char *Key) 209 | { 210 | unsigned char Row, Column, Round = 0; 211 | unsigned char Round_Key[16]; 212 | unsigned char State[4][4]; 213 | 214 | // Copy input to State arry 215 | for( Column = 0; Column < 4; Column++ ) 216 | { 217 | for( Row = 0; Row < 4; Row++ ) 218 | { 219 | State[Row][Column] = Data[Row + (Column << 2)]; 220 | } 221 | } 222 | 223 | // Copy key to round key 224 | memcpy( &Round_Key[0], &Key[0], 16 ); 225 | 226 | // Add round key 227 | AES_Add_Round_Key( Round_Key, State ); 228 | 229 | // Perform 9 full rounds with mixed collums 230 | for( Round = 1 ; Round < 10 ; Round++ ) 231 | { 232 | // Perform Byte substitution with S table 233 | for( Column = 0 ; Column < 4 ; Column++ ) 234 | { 235 | for( Row = 0 ; Row < 4 ; Row++ ) 236 | { 237 | State[Row][Column] = AES_Sub_Byte( State[Row][Column] ); 238 | } 239 | } 240 | 241 | // Perform Row Shift 242 | AES_Shift_Rows(State); 243 | 244 | // Mix Collums 245 | AES_Mix_Collums(State); 246 | 247 | // Calculate new round key 248 | AES_Calculate_Round_Key(Round, Round_Key); 249 | 250 | // Add the round key to the Round_key 251 | AES_Add_Round_Key(Round_Key, State); 252 | } 253 | 254 | // Perform Byte substitution with S table whitout mix collums 255 | for( Column = 0 ; Column < 4 ; Column++ ) 256 | { 257 | for( Row = 0; Row < 4; Row++ ) 258 | { 259 | State[Row][Column] = AES_Sub_Byte(State[Row][Column]); 260 | } 261 | } 262 | 263 | // Shift rows 264 | AES_Shift_Rows(State); 265 | 266 | // Calculate new round key 267 | AES_Calculate_Round_Key( Round, Round_Key ); 268 | 269 | // Add round key 270 | AES_Add_Round_Key( Round_Key, State ); 271 | 272 | // Copy the State into the data array 273 | for( Column = 0; Column < 4; Column++ ) 274 | { 275 | for( Row = 0; Row < 4; Row++ ) 276 | { 277 | Data[Row + (Column << 2)] = State[Row][Column]; 278 | } 279 | } 280 | } // AES_Encrypt 281 | 282 | 283 | 284 | 285 | -------------------------------------------------------------------------------- /src/arduino-rfm/Encrypt.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | * Copyright 2017 Ideetron B.V. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | ******************************************************************************************/ 17 | /**************************************************************************************** 18 | * File: Encrypt.cpp 19 | * Author: Gerben den Hartog 20 | * Compagny: Ideetron B.V. 21 | * Website: http://www.ideetron.nl/LoRa 22 | * E-mail: info@ideetron.nl 23 | ****************************************************************************************/ 24 | /**************************************************************************************** 25 | * Created on: 09-02-2017 26 | * Supported Hardware: ID150119-02 Nexus board with RFM95 27 | ****************************************************************************************/ 28 | 29 | /* 30 | ***************************************************************************************** 31 | * INCLUDE FILES 32 | ***************************************************************************************** 33 | */ 34 | 35 | #include "Encrypt.h" 36 | #include "AES-128.h" 37 | #include "Struct.h" 38 | 39 | /* 40 | ***************************************************************************************** 41 | * INCLUDE GLOBAL VARIABLES 42 | ***************************************************************************************** 43 | */ 44 | 45 | static void XOR(unsigned char *New_Data,unsigned char *Old_Data) 46 | { 47 | unsigned char i; 48 | 49 | for(i = 0; i < 16; i++) 50 | { 51 | New_Data[i] = New_Data[i] ^ Old_Data[i]; 52 | } 53 | } 54 | 55 | static void Shift_Left(unsigned char *Data) 56 | { 57 | unsigned char i; 58 | unsigned char Overflow = 0; 59 | //unsigned char High_Byte, Low_Byte; 60 | 61 | for(i = 0; i < 16; i++) 62 | { 63 | //Check for overflow on next byte except for the last byte 64 | if(i < 15) 65 | { 66 | //Check if upper bit is one 67 | if((Data[i+1] & 0x80) == 0x80) 68 | { 69 | Overflow = 1; 70 | } 71 | else 72 | { 73 | Overflow = 0; 74 | } 75 | } 76 | else 77 | { 78 | Overflow = 0; 79 | } 80 | 81 | //Shift one left 82 | Data[i] = (Data[i] << 1) + Overflow; 83 | } 84 | } 85 | /* 86 | ***************************************************************************************** 87 | * Description : Function used to encrypt and decrypt the data in a LoRaWAN data message 88 | * 89 | * Arguments : *Buffer pointer to the buffer containing the data to de/encrypt 90 | * *Session_Data pointer to sLoRa_Session sturct 91 | * *Message pointer to sLoRa_Message struct containing the message specific variables 92 | ***************************************************************************************** 93 | */ 94 | void Encrypt_Payload(sBuffer *Buffer, unsigned char *Key, sLoRa_Message *Message) 95 | { 96 | unsigned char i = 0x00; 97 | unsigned char j; 98 | unsigned char Number_of_Blocks = 0x00; 99 | unsigned char Incomplete_Block_Size = 0x00; 100 | 101 | unsigned char Block_A[16]; 102 | 103 | //Calculate number of blocks 104 | Number_of_Blocks = Buffer->Counter / 16; 105 | Incomplete_Block_Size = Buffer->Counter % 16; 106 | if(Incomplete_Block_Size != 0) 107 | { 108 | Number_of_Blocks++; 109 | } 110 | 111 | for(i = 0x00; i < Number_of_Blocks; i++) 112 | { 113 | Block_A[0] = 0x01; 114 | Block_A[1] = 0x00; 115 | Block_A[2] = 0x00; 116 | Block_A[3] = 0x00; 117 | Block_A[4] = 0x00; 118 | 119 | Block_A[5] = Message->Direction; 120 | 121 | Block_A[6] = Message->DevAddr[3]; 122 | Block_A[7] = Message->DevAddr[2]; 123 | Block_A[8] = Message->DevAddr[1]; 124 | Block_A[9] = Message->DevAddr[0]; 125 | 126 | Block_A[10] = (Message->Frame_Counter & 0x00FF); 127 | Block_A[11] = ((Message->Frame_Counter >> 8) & 0x00FF); 128 | 129 | Block_A[12] = 0x00; //Frame counter upper Bytes 130 | Block_A[13] = 0x00; 131 | 132 | Block_A[14] = 0x00; 133 | 134 | Block_A[15] = i + 1; 135 | 136 | //Calculate S 137 | AES_Encrypt(Block_A,Key); 138 | 139 | //Check for last block 140 | if(i != (Number_of_Blocks - 1)) 141 | { 142 | for(j = 0; j < 16; j++) 143 | { 144 | Buffer->Data[(i*16)+j] ^= Block_A[j]; 145 | } 146 | } 147 | else 148 | { 149 | if(Incomplete_Block_Size == 0) 150 | { 151 | Incomplete_Block_Size = 16; 152 | } 153 | for(j = 0; j < Incomplete_Block_Size; j++) 154 | { 155 | Buffer->Data[(i*16)+j] ^= Block_A[j]; 156 | } 157 | } 158 | } 159 | } 160 | 161 | /* 162 | ***************************************************************************************** 163 | * Description : Function used to build a the data that is used for calculating the MIC of a data message 164 | * 165 | * Arguments : *Buffer pointer to the buffer containing the data 166 | * *Session_Data pointer to sLoRa_Session sturct 167 | * *Message pointer to sLoRa_Message struct containing the message specific variables 168 | ***************************************************************************************** 169 | */ 170 | void Construct_Data_MIC(sBuffer *Buffer, sLoRa_Session *Session_Data, sLoRa_Message *Message) 171 | { 172 | unsigned char i; 173 | unsigned char MIC_Data[MAX_UPLINK_PAYLOAD_SIZE+65]; 174 | sBuffer MIC_Buffer = { &MIC_Data[0], 0x00 }; 175 | 176 | unsigned char Block_B[16]; 177 | 178 | //Construct Block B 179 | Block_B[0] = 0x49; 180 | Block_B[1] = 0x00; 181 | Block_B[2] = 0x00; 182 | Block_B[3] = 0x00; 183 | Block_B[4] = 0x00; 184 | 185 | Block_B[5] = Message->Direction; 186 | 187 | Block_B[6] = Message->DevAddr[3]; 188 | Block_B[7] = Message->DevAddr[2]; 189 | Block_B[8] = Message->DevAddr[1]; 190 | Block_B[9] = Message->DevAddr[0]; 191 | 192 | Block_B[10] = (Message->Frame_Counter & 0x00FF); 193 | Block_B[11] = ((Message->Frame_Counter >> 8) & 0x00FF); 194 | 195 | Block_B[12] = 0x00; //Frame counter upper bytes 196 | Block_B[13] = 0x00; 197 | 198 | Block_B[14] = 0x00; 199 | Block_B[15] = Buffer->Counter; 200 | 201 | //Copy Block B into MIC data 202 | for(i = 0x00; i < 16; i++) 203 | { 204 | MIC_Data[i] = Block_B[i]; 205 | } 206 | 207 | //Add data to it 208 | for(i = 0x00; i < Buffer->Counter; i++) 209 | { 210 | MIC_Data[i + 16] = Buffer->Data[i]; 211 | } 212 | 213 | //Calculate the correct buffer length 214 | MIC_Buffer.Counter = 16 + Buffer->Counter; 215 | 216 | //Calculate the MIC 217 | Calculate_MIC(&MIC_Buffer, Session_Data->NwkSKey, Message); 218 | } 219 | 220 | /* 221 | ***************************************************************************************** 222 | * Description : Function used to calculate the MIC of data 223 | * 224 | * Arguments : *Buffer pointer to the buffer containing the data the MIC should be calculated from 225 | * *Key pointer to key used for the MIC calculation 226 | * *Message pointer to sLoRa_Message struct containing the message specific variables 227 | ***************************************************************************************** 228 | */ 229 | void Calculate_MIC(sBuffer *Buffer, unsigned char *Key, sLoRa_Message *Message) 230 | { 231 | unsigned char i, j; 232 | unsigned char Key_K1[16] = { 233 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 234 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 235 | }; 236 | unsigned char Key_K2[16] = { 237 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 238 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 239 | }; 240 | 241 | unsigned char Old_Data[16] = { 242 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 243 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 244 | }; 245 | unsigned char New_Data[16] = { 246 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 247 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 248 | }; 249 | 250 | unsigned char Number_of_Blocks = 0x00; 251 | unsigned char Incomplete_Block_Size = 0x00; 252 | 253 | //Calculate number of Blocks and blocksize of last block 254 | Number_of_Blocks = Buffer->Counter / 16; 255 | Incomplete_Block_Size = Buffer->Counter % 16; 256 | 257 | //if there is an incomplete block at the end add 1 to the number of blocks 258 | if(Incomplete_Block_Size != 0) 259 | { 260 | Number_of_Blocks++; 261 | } 262 | 263 | Generate_Keys(Key, Key_K1, Key_K2); 264 | 265 | //Perform full calculating until n-1 message blocks 266 | for(j = 0x0; j < (Number_of_Blocks - 1); j++) 267 | { 268 | //Copy data into array 269 | for(i = 0; i < 16; i++) 270 | { 271 | New_Data[i] = Buffer->Data[(j*16)+i]; 272 | } 273 | 274 | //Perform XOR with old data 275 | XOR(New_Data,Old_Data); 276 | 277 | //Perform AES encryption 278 | AES_Encrypt(New_Data,Key); 279 | 280 | //Copy New_Data to Old_Data 281 | for(i = 0; i < 16; i++) 282 | { 283 | Old_Data[i] = New_Data[i]; 284 | } 285 | } 286 | 287 | //Perform calculation on last block 288 | //Check if Datalength is a multiple of 16 289 | if(Incomplete_Block_Size == 0) 290 | { 291 | //Copy last data into array 292 | for(i = 0; i < 16; i++) 293 | { 294 | New_Data[i] = Buffer->Data[((Number_of_Blocks -1)*16)+i]; 295 | } 296 | 297 | //Perform XOR with Key 1 298 | XOR(New_Data,Key_K1); 299 | 300 | //Perform XOR with old data 301 | XOR(New_Data,Old_Data); 302 | 303 | //Perform last AES routine 304 | AES_Encrypt(New_Data,Key); 305 | } 306 | else 307 | { 308 | //Copy the remaining data and fill the rest 309 | for(i = 0; i < 16; i++) 310 | { 311 | if(i < Incomplete_Block_Size) 312 | { 313 | New_Data[i] = Buffer->Data[((Number_of_Blocks -1)*16)+i]; 314 | } 315 | if(i == Incomplete_Block_Size) 316 | { 317 | New_Data[i] = 0x80; 318 | } 319 | if(i > Incomplete_Block_Size) 320 | { 321 | New_Data[i] = 0x00; 322 | } 323 | } 324 | 325 | //Perform XOR with Key 2 326 | XOR(New_Data,Key_K2); 327 | 328 | //Perform XOR with Old data 329 | XOR(New_Data,Old_Data); 330 | 331 | //Perform last AES routine 332 | AES_Encrypt(New_Data,Key); 333 | } 334 | 335 | Message->MIC[0] = New_Data[0]; 336 | Message->MIC[1] = New_Data[1]; 337 | Message->MIC[2] = New_Data[2]; 338 | Message->MIC[3] = New_Data[3]; 339 | } 340 | 341 | /* 342 | ***************************************************************************************** 343 | * Description : Function used to generate keys for the MIC calculation 344 | * 345 | * Arguments : *Key pointer to key used for the MIC calculation 346 | * *K1 pointer to Key1 347 | * *K2 pointer ot Key2 348 | ***************************************************************************************** 349 | */ 350 | void Generate_Keys(unsigned char *Key, unsigned char *K1, unsigned char *K2) 351 | { 352 | unsigned char i; 353 | unsigned char MSB_Key; 354 | 355 | //Encrypt the zeros in K1 with the NwkSkey 356 | AES_Encrypt(K1,Key); 357 | 358 | //Create K1 359 | //Check if MSB is 1 360 | if((K1[0] & 0x80) == 0x80) 361 | { 362 | MSB_Key = 1; 363 | } 364 | else 365 | { 366 | MSB_Key = 0; 367 | } 368 | 369 | //Shift K1 one bit left 370 | Shift_Left(K1); 371 | 372 | //if MSB was 1 373 | if(MSB_Key == 1) 374 | { 375 | K1[15] = K1[15] ^ 0x87; 376 | } 377 | 378 | //Copy K1 to K2 379 | for( i = 0; i < 16; i++) 380 | { 381 | K2[i] = K1[i]; 382 | } 383 | 384 | //Check if MSB is 1 385 | if((K2[0] & 0x80) == 0x80) 386 | { 387 | MSB_Key = 1; 388 | } 389 | else 390 | { 391 | MSB_Key = 0; 392 | } 393 | 394 | //Shift K2 one bit left 395 | Shift_Left(K2); 396 | 397 | //Check if MSB was 1 398 | if(MSB_Key == 1) 399 | { 400 | K2[15] = K2[15] ^ 0x87; 401 | } 402 | } 403 | 404 | 405 | 406 | 407 | 408 | -------------------------------------------------------------------------------- /src/arduino-rfm/lorawan-arduino-rfm.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | loraid-arduino-rfm.cpp 3 | Copyright © 2018 lora.id. All right reserved. 4 | 5 | Author: Andri Rahmadhani 6 | Date: 2018-04-25 7 | 8 | Encapsulate Ideetron LoRaWAN simple node demonstrator 9 | *This fimrware supports 10 | *Over The Air Activation 11 | *Activation By Personalization 12 | *Class switching between Class A and Class C motes 13 | *Channel hopping 14 | * 15 | *The following settings can be done 16 | *Channel Receive and Transmit 17 | *Datarate Receive and Transmit 18 | *Transmit power 19 | *Confirmed or unconfirmed messages 20 | *Device Address 21 | *Application Session Key 22 | *Network Session Key 23 | *Device EUI 24 | *Application EUI 25 | *Application key 26 | *Mote Class 27 | 28 | Use of this source code is governed by the MIT license that can be found in the LICENSE file. 29 | */ 30 | 31 | #include "lorawan-arduino-rfm.h" 32 | #include "Conversions.h" 33 | 34 | // define lora objet 35 | LoRaWANClass lora; 36 | 37 | LoRaWANClass::LoRaWANClass() 38 | { 39 | } 40 | 41 | LoRaWANClass::~LoRaWANClass() 42 | { 43 | } 44 | 45 | bool LoRaWANClass::init(void) 46 | { 47 | // Lora Setting Class 48 | dev_class = CLASS_A; 49 | // Random seed 50 | #ifdef ESP32 51 | randomSeed(esp_random()); 52 | #else 53 | randomSeed(analogRead(0)); 54 | #endif 55 | 56 | // Status 57 | RFM_Command_Status = NO_RFM_COMMAND; 58 | Rx_Status = NO_RX; 59 | Ack_Status = NO_ACK; 60 | 61 | // current channel 62 | currentChannel = MULTI; 63 | 64 | // Initialise session data struct (Semtech default key) 65 | memset(Address_Tx, 0x00, 4); 66 | memset(NwkSKey, 0x00, 16); 67 | memset(AppSKey, 0x00, 16); 68 | 69 | Frame_Counter_Tx = 0x0000; 70 | Session_Data.NwkSKey = NwkSKey; 71 | Session_Data.AppSKey = AppSKey; 72 | Session_Data.DevAddr = Address_Tx; 73 | Session_Data.Frame_Counter = &Frame_Counter_Tx; 74 | 75 | //Initialize OTAA data struct 76 | memset(DevEUI, 0x00, 8); 77 | memset(AppEUI, 0x00, 8); 78 | 79 | memset(AppKey, 0x00, 16); 80 | memset(DevNonce, 0x00, 2); 81 | memset(AppNonce, 0x00, 3); 82 | memset(NetID, 0x00, 3); 83 | OTAA_Data.DevEUI = DevEUI; 84 | OTAA_Data.AppEUI = AppEUI; 85 | OTAA_Data.AppKey = AppKey; 86 | OTAA_Data.DevNonce = DevNonce; 87 | OTAA_Data.AppNonce = AppNonce; 88 | OTAA_Data.NetID = NetID; 89 | 90 | // Device Class 91 | LoRa_Settings.Mote_Class = 0x00; //0x00 is type A, 0x01 is type C 92 | 93 | // Rx 94 | #if defined(AS_923) || defined(AS_923_2) 95 | LoRa_Settings.Datarate_Rx = 0x02; //set to SF10 BW 125 kHz 96 | #elif defined(EU_868) 97 | LoRa_Settings.Datarate_Rx = 0x03; //set to SF9 BW 125 kHz 98 | #elif defined(IN_865) 99 | LoRa_Settings.Datarate_Rx = 0x02; //set to SF10 BW 125 kHz 100 | #else //US_915 or AU_915 101 | LoRa_Settings.Datarate_Rx = 0x0C; //set to SF8 BW 500 kHz 102 | #endif 103 | LoRa_Settings.Channel_Rx = 0x08; // set to recv channel 104 | 105 | // Tx 106 | #if defined(US_915) 107 | LoRa_Settings.Datarate_Tx = drate_common = 0x02; //set to SF7 BW 125 kHz 108 | #elif defined(AU_915) 109 | LoRa_Settings.Datarate_Tx = drate_common = 0x02; //set to SF7 BW 125 kHz 110 | #else 111 | LoRa_Settings.Datarate_Tx = drate_common = 0x00; //set to SF12 BW 125 kHz 112 | #endif 113 | LoRa_Settings.Channel_Tx = 0x00; // set to channel 0 114 | 115 | LoRa_Settings.Confirm = 0x00; //0x00 unconfirmed, 0x01 confirmed 116 | LoRa_Settings.Channel_Hopping = 0x00; //0x00 no channel hopping, 0x01 channel hopping 117 | 118 | // Set default rx delay and window 119 | LoRa_Settings.RX1_Window = 1000; 120 | LoRa_Settings.RX2_Window = 1000; 121 | 122 | LoRa_Settings.Rx1_Delay = 5000; // Thing stack seems to be 5000 ms (so Rx2_delay 6000 ms) 123 | LoRa_Settings.Rx2_Delay = LoRa_Settings.Rx1_Delay + LoRa_Settings.RX1_Window; 124 | 125 | 126 | // Initialise buffer for data to transmit 127 | memset(Data_Tx, 0x00, sizeof(Data_Tx)); 128 | Buffer_Tx.Data = Data_Tx; 129 | Buffer_Tx.Counter = 0x00; 130 | // Initialise buffer for data to receive 131 | memset(Data_Rx, 0x00, sizeof(Data_Rx)); 132 | Buffer_Rx.Data = Data_Rx; 133 | Buffer_Rx.Counter = 0x00; 134 | Message_Rx.Direction = 0x01; //Set down direction for Rx message 135 | 136 | //Initialize I/O pins 137 | if (RFM_pins.DIO0 != -1) 138 | { 139 | pinMode(RFM_pins.DIO0, INPUT); 140 | pinMode(RFM_pins.DIO1, INPUT); 141 | pinMode(RFM_pins.DIO2, INPUT); 142 | } 143 | #ifdef BOARD_DRAGINO_SHIELD 144 | pinMode(RFM_pins.DIO5, INPUT); 145 | #endif 146 | pinMode(RFM_pins.CS, OUTPUT); 147 | digitalWrite(RFM_pins.CS, HIGH); 148 | 149 | // Reset 150 | if (RFM_pins.RST != -1) 151 | { 152 | pinMode(RFM_pins.RST, OUTPUT); 153 | digitalWrite(RFM_pins.RST, HIGH); 154 | delay(10); 155 | digitalWrite(RFM_pins.RST, LOW); 156 | delay(10); 157 | digitalWrite(RFM_pins.RST, HIGH); 158 | } 159 | 160 | //Initialise the SPI port 161 | SPI.begin(); 162 | 163 | /*** This prevents the use of other SPI devices with different settings ***/ 164 | //SPI.beginTransaction(SPISettings(4000000,MSBFIRST,SPI_MODE0)); 165 | 166 | //Wait until RFM module is started 167 | delay(50); 168 | 169 | //Initialize RFM module 170 | if (!RFM_Init()) 171 | { 172 | return 0; 173 | } 174 | return 1; 175 | } 176 | 177 | bool LoRaWANClass::join(void) 178 | { 179 | bool join_status; 180 | const unsigned long timeout = 6000; 181 | unsigned long prev_millis; 182 | 183 | if (currentChannel == MULTI) 184 | { 185 | randomChannel(); 186 | } 187 | // join request 188 | LoRa_Send_JoinReq(&OTAA_Data, &LoRa_Settings); 189 | // delay(900); 190 | // loop for wait for join accept 191 | prev_millis = millis(); 192 | do 193 | { 194 | join_status = LORA_join_Accept(&Buffer_Rx, &Session_Data, &OTAA_Data, &Message_Rx, &LoRa_Settings); 195 | 196 | } while ((millis() - prev_millis) < timeout && !join_status); 197 | 198 | return join_status; 199 | } 200 | 201 | void LoRaWANClass::sleep(void) 202 | { 203 | //Switch RFM to sleep 204 | //DON'T USE Switch mode function 205 | RFM_Write(RFM_REG_OP_MODE, RFM_MODE_SLEEP); 206 | } 207 | 208 | void LoRaWANClass::wakeUp(void) 209 | { 210 | RFM_Switch_Mode(RFM_MODE_STANDBY); 211 | } 212 | 213 | void LoRaWANClass::setDevEUI(const char *devEUI_in) 214 | { 215 | for (byte i = 0; i < 8; ++i) 216 | DevEUI[i] = ASCII2Hex(devEUI_in[i * 2], devEUI_in[(i * 2) + 1]); 217 | //Reset frame counter 218 | Frame_Counter_Tx = 0x0000; 219 | 220 | //Reset RFM command status 221 | RFM_Command_Status = NO_RFM_COMMAND; 222 | } 223 | 224 | void LoRaWANClass::setAppEUI(const char *appEUI_in) 225 | { 226 | for (byte i = 0; i < 8; ++i) 227 | AppEUI[i] = ASCII2Hex(appEUI_in[i * 2], appEUI_in[(i * 2) + 1]); 228 | //Reset frame counter 229 | Frame_Counter_Tx = 0x0000; 230 | 231 | //Reset RFM command status 232 | RFM_Command_Status = NO_RFM_COMMAND; 233 | } 234 | 235 | void LoRaWANClass::setAppKey(const char *appKey_in) 236 | { 237 | for (byte i = 0; i < 16; ++i) 238 | AppKey[i] = ASCII2Hex(appKey_in[i * 2], appKey_in[(i * 2) + 1]); 239 | //Reset frame counter 240 | Frame_Counter_Tx = 0x0000; 241 | 242 | //Reset RFM command status 243 | RFM_Command_Status = NO_RFM_COMMAND; 244 | } 245 | 246 | void LoRaWANClass::setNwkSKey(const char *NwkKey_in) 247 | { 248 | for (uint8_t i = 0; i < 16; ++i) 249 | NwkSKey[i] = ASCII2Hex(NwkKey_in[i * 2], NwkKey_in[(i * 2) + 1]); 250 | 251 | //Reset frame counter 252 | Frame_Counter_Tx = 0x0000; 253 | 254 | //Reset RFM command status 255 | RFM_Command_Status = NO_RFM_COMMAND; 256 | } 257 | 258 | void LoRaWANClass::setAppSKey(const char *ApskKey_in) 259 | { 260 | for (uint8_t i = 0; i < 16; ++i) 261 | AppSKey[i] = ASCII2Hex(ApskKey_in[i * 2], ApskKey_in[(i * 2) + 1]); 262 | 263 | //Reset frame counter 264 | Frame_Counter_Tx = 0x0000; 265 | 266 | //Reset RFM command status 267 | RFM_Command_Status = NO_RFM_COMMAND; 268 | } 269 | 270 | void LoRaWANClass::setDevAddr(const char *devAddr_in) 271 | { 272 | size_t devAddrSize = sizeof(Session_Data.DevAddr); 273 | memset(Session_Data.DevAddr, 0x30, devAddrSize); 274 | 275 | //Check if it is a set command and there is enough data sent 276 | Address_Tx[0] = ASCII2Hex(devAddr_in[0], devAddr_in[1]); 277 | Address_Tx[1] = ASCII2Hex(devAddr_in[2], devAddr_in[3]); 278 | Address_Tx[2] = ASCII2Hex(devAddr_in[4], devAddr_in[5]); 279 | Address_Tx[3] = ASCII2Hex(devAddr_in[6], devAddr_in[7]); 280 | 281 | //Reset frame counter 282 | Frame_Counter_Tx = 0x0000; 283 | 284 | //Reset RFM command status 285 | RFM_Command_Status = NO_RFM_COMMAND; 286 | } 287 | 288 | void LoRaWANClass::setTxPower(int level, txPin_t pinTx) 289 | { 290 | RFM_Set_Tx_Power(level, pinTx); 291 | } 292 | 293 | int LoRaWANClass::getRssi() 294 | { 295 | // return rssi value in dBm - convertion according to sx1276 datasheet 296 | return -157 + RFM_Get_Rssi(); 297 | } 298 | 299 | void LoRaWANClass::setDeviceClass(devclass_t dev_class) 300 | { 301 | LoRa_Settings.Mote_Class = (dev_class == CLASS_A) ? CLASS_A : CLASS_C; 302 | 303 | if (LoRa_Settings.Mote_Class == CLASS_A) 304 | { 305 | RFM_Switch_Mode(RFM_MODE_STANDBY); 306 | } 307 | else 308 | { 309 | RFM_Continuous_Receive(&LoRa_Settings); 310 | } 311 | 312 | //Reset RFM command 313 | RFM_Command_Status = NO_RFM_COMMAND; 314 | } 315 | 316 | void LoRaWANClass::sendUplink(char *data, unsigned int len, unsigned char confirm, unsigned char mport) 317 | { 318 | lora.setDeviceClass(CLASS_A); // start as class a device 319 | 320 | if (currentChannel == MULTI) 321 | { 322 | randomChannel(); 323 | } 324 | LoRa_Settings.Confirm = (confirm == 0) ? 0 : 1; 325 | if (mport == 0) 326 | mport = 1; 327 | if (mport > 223) 328 | mport = 1; 329 | LoRa_Settings.Mport = mport; 330 | //Set new command for RFM 331 | RFM_Command_Status = NEW_RFM_COMMAND; 332 | Buffer_Tx.Counter = len; 333 | memcpy(Buffer_Tx.Data, data, len); 334 | } 335 | 336 | void LoRaWANClass::setDataRate(unsigned char data_rate) 337 | { 338 | drate_common = data_rate; 339 | #if defined(US_915) 340 | if (drate_common <= 0x04) 341 | { 342 | LoRa_Settings.Datarate_Tx = drate_common; 343 | LoRa_Settings.Datarate_Rx = data_rate + 0x0A; 344 | } 345 | #elif defined(AU_915) 346 | if (drate_common <= 0x04) 347 | { 348 | LoRa_Settings.Datarate_Tx = drate_common; 349 | LoRa_Settings.Datarate_Rx = data_rate + 0x0A; 350 | } 351 | #else 352 | //Check if the value is oke 353 | if (drate_common <= 0x06) 354 | { 355 | LoRa_Settings.Datarate_Tx = drate_common; 356 | LoRa_Settings.Datarate_Rx = drate_common; 357 | } 358 | 359 | #endif 360 | RFM_Command_Status = NO_RFM_COMMAND; 361 | } 362 | 363 | void LoRaWANClass::setChannel(unsigned char channel) 364 | { 365 | if (channel <= 7) 366 | { 367 | currentChannel = channel; 368 | LoRa_Settings.Channel_Tx = channel; 369 | #ifdef US_915 370 | LoRa_Settings.Channel_Rx = channel + 0x08; 371 | #elif defined(AU_915) 372 | LoRa_Settings.Channel_Rx = channel + 0x08; 373 | #elif defined(EU_868) 374 | LoRa_Settings.Channel_Rx = channel; 375 | #elif defined(IN_865) 376 | LoRa_Settings.Channel_Rx = channel; 377 | #elif defined(AS_923) || defined(AS_923_2) 378 | LoRa_Settings.Channel_Rx = channel; 379 | #endif 380 | } 381 | else if (channel == MULTI) 382 | { 383 | currentChannel = MULTI; 384 | } 385 | } 386 | 387 | unsigned char LoRaWANClass::getChannel() 388 | { 389 | return LoRa_Settings.Channel_Tx; 390 | } 391 | 392 | unsigned char LoRaWANClass::getDataRate() 393 | { 394 | return LoRa_Settings.Datarate_Tx; 395 | } 396 | 397 | void LoRaWANClass::setTxPower1(unsigned char power_idx) 398 | { 399 | unsigned char RFM_Data; 400 | LoRa_Settings.Transmit_Power = (power_idx > 0x0F) ? 0x0F : power_idx; 401 | RFM_Data = LoRa_Settings.Transmit_Power + 0xF0; 402 | RFM_Write(RFM_REG_PA_CONFIG, RFM_Data); 403 | } 404 | 405 | void LoRaWANClass::setRx1Delay(unsigned int ms) 406 | { 407 | LoRa_Settings.Rx1_Delay = ms; 408 | LoRa_Settings.Rx2_Delay = max(LoRa_Settings.Rx2_Delay,LoRa_Settings.Rx1_Delay + LoRa_Settings.RX1_Window); 409 | } 410 | 411 | void LoRaWANClass::setRx2Delay(unsigned int ms) 412 | { 413 | LoRa_Settings.Rx2_Delay = max(ms,LoRa_Settings.Rx1_Delay + LoRa_Settings.RX1_Window); 414 | } 415 | 416 | void LoRaWANClass::setRx1Window(unsigned int ms) 417 | { 418 | LoRa_Settings.RX1_Window = ms; 419 | LoRa_Settings.Rx2_Delay = max(LoRa_Settings.Rx2_Delay,LoRa_Settings.Rx1_Delay + LoRa_Settings.RX1_Window); 420 | } 421 | 422 | void LoRaWANClass::setRx2Window(unsigned int ms) 423 | { 424 | LoRa_Settings.RX2_Window = ms; 425 | } 426 | 427 | int LoRaWANClass::readData(char *outBuff) 428 | { 429 | int res = 0; 430 | //If there is new data 431 | if (Rx_Status == NEW_RX) 432 | { 433 | res = Buffer_Rx.Counter; 434 | memset(outBuff, 0x00, res + 1); 435 | memcpy(outBuff, Buffer_Rx.Data, res); 436 | 437 | // Clear Buffer counter 438 | Buffer_Rx.Counter = 0x00; 439 | 440 | Rx_Status = NO_RX; 441 | } 442 | 443 | return res; 444 | } 445 | 446 | /** 447 | * Get ACK flag from downlink packet 448 | * 449 | * @return true in case of ACK received 450 | */ 451 | bool LoRaWANClass::readAck(void) 452 | { 453 | if (Ack_Status == NEW_ACK) 454 | { 455 | Ack_Status = NO_ACK; 456 | return true; 457 | } 458 | return false; 459 | } 460 | 461 | void LoRaWANClass::update(void) 462 | { 463 | //Type A mote transmit receive cycle 464 | if ((RFM_Command_Status == NEW_RFM_COMMAND || RFM_Command_Status == JOIN) && LoRa_Settings.Mote_Class == CLASS_A) 465 | { 466 | //LoRaWAN TX/RX cycle 467 | LORA_Cycle(&Buffer_Tx, &Buffer_Rx, &RFM_Command_Status, &Session_Data, &OTAA_Data, &Message_Rx, &LoRa_Settings); 468 | 469 | if ((Message_Rx.Frame_Control & 0x20) > 0){ // ack get only in RX1 window 470 | Ack_Status = NEW_ACK; 471 | } 472 | 473 | if (Buffer_Rx.Counter != 0x00) 474 | { 475 | Rx_Status = NEW_RX; 476 | } 477 | 478 | RFM_Command_Status = NO_RFM_COMMAND; 479 | 480 | } 481 | 482 | //Type C mote transmit and receive handling 483 | if (LoRa_Settings.Mote_Class == CLASS_C) 484 | { 485 | //Transmit -> this will be never used in Class C since device start as class A all the time. 486 | // in Class C mode we will only listen upcoming messages 487 | if (RFM_Command_Status == NEW_RFM_COMMAND) 488 | { 489 | //LoRaWAN TX/RX cycle 490 | LORA_Cycle(&Buffer_Tx, &Buffer_Rx, &RFM_Command_Status, &Session_Data, &OTAA_Data, &Message_Rx, &LoRa_Settings); 491 | if (Buffer_Rx.Counter != 0x00) 492 | { 493 | Rx_Status = NEW_RX; 494 | } 495 | RFM_Command_Status = NO_RFM_COMMAND; 496 | } 497 | 498 | //Receive in Class C mode 499 | bool isRxDone; 500 | if (RFM_pins.DIO0 != -1) 501 | { 502 | isRxDone = digitalRead(RFM_pins.DIO0) == HIGH; 503 | } 504 | else 505 | { 506 | isRxDone = RFM_isRxDone(); 507 | } 508 | if (isRxDone) 509 | { 510 | LORA_Receive_Data(&Buffer_Rx, &Session_Data, &OTAA_Data, &Message_Rx, &LoRa_Settings); 511 | if (Buffer_Rx.Counter != 0x00) 512 | { 513 | Rx_Status = NEW_RX; 514 | } 515 | } 516 | 517 | RFM_Command_Status = NO_RFM_COMMAND; 518 | } 519 | 520 | } 521 | 522 | void LoRaWANClass::randomChannel() 523 | { 524 | unsigned char freq_idx; 525 | #ifdef AS_923 526 | freq_idx = random(0, 8); 527 | LoRa_Settings.Channel_Rx=freq_idx; // same rx and tx channel 528 | #elif defined(AS_923_2) 529 | freq_idx = random(0, 8); 530 | LoRa_Settings.Channel_Rx=freq_idx; // same rx and tx channel 531 | #elif defined(EU_868) 532 | freq_idx = random(0,8); 533 | LoRa_Settings.Channel_Rx=freq_idx; // same rx and tx channel 534 | #elif defined(IN_865) 535 | freq_idx = random(0,3); 536 | LoRa_Settings.Channel_Rx=freq_idx; // same rx and tx channel 537 | #else // US_915 or AU_915 538 | freq_idx = random(0, 8); 539 | LoRa_Settings.Channel_Rx = freq_idx + 0x08; 540 | #endif 541 | LoRa_Settings.Channel_Tx = freq_idx; 542 | } 543 | 544 | unsigned int LoRaWANClass::getFrameCounter() 545 | { 546 | return Frame_Counter_Tx; 547 | } 548 | 549 | void LoRaWANClass::setFrameCounter(unsigned int FrameCounter) 550 | { 551 | Frame_Counter_Tx = FrameCounter; 552 | } 553 | 554 | 555 | -------------------------------------------------------------------------------- /src/arduino-rfm/LoRaMAC.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | * Copyright 2017 Ideetron B.V. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | ******************************************************************************************/ 17 | /**************************************************************************************** 18 | * File: LoRaMAC.cpp 19 | * Author: Gerben den Hartog 20 | * Compagny: Ideetron B.V. 21 | * Website: http://www.ideetron.nl/LoRa 22 | * E-mail: info@ideetron.nl 23 | ****************************************************************************************/ 24 | /**************************************************************************************** 25 | * Created on: 06-01-2017 26 | ****************************************************************************************/ 27 | /* 28 | ***************************************************************************************** 29 | * INCLUDE FILES 30 | ***************************************************************************************** 31 | */ 32 | 33 | #include 34 | #include "AES-128.h" 35 | #include "RFM95.h" 36 | #include "Encrypt.h" 37 | #include "LoRaMAC.h" 38 | #include "Struct.h" 39 | #include "Config.h" 40 | #include "Arduino.h" 41 | 42 | 43 | /* 44 | ***************************************************************************************** 45 | * FUNCTIONS 46 | ***************************************************************************************** 47 | */ 48 | 49 | 50 | /* 51 | ***************************************************************************************** 52 | * Description : Function that handles a send and receive cycle with timing for receive slots. 53 | * This function is only used for Class A motes. The wait times are tested with 54 | * the iot.semtech.com site. 55 | * 56 | * Arguments : *Data_Tx pointer to tranmit buffer 57 | * *Data_Rx pointer to receive buffer 58 | * *RFM_Command pointer to current RFM state 59 | * *Session_Data pointer to sLoRa_Session sturct 60 | * *OTAA_Data pointer to sLoRa_OTAA struct 61 | * *Message_Rx pointer to sLoRa_Message struct used for the received message information 62 | * *LoRa_Settings pointer to sSetting struct 63 | ***************************************************************************************** 64 | */ 65 | void LORA_Cycle(sBuffer *Data_Tx, sBuffer *Data_Rx, RFM_command_t *RFM_Command, sLoRa_Session *Session_Data, 66 | sLoRa_OTAA *OTAA_Data, sLoRa_Message *Message_Rx, sSettings *LoRa_Settings) 67 | { 68 | static const unsigned int Receive_Delay_1 = LoRa_Settings->Rx1_Delay; 69 | static const unsigned int Receive_Delay_2 = LoRa_Settings->Rx2_Delay; 70 | static const unsigned int RX1_Window = LoRa_Settings->RX1_Window; 71 | static const unsigned int RX2_Window = LoRa_Settings->RX2_Window; 72 | 73 | unsigned long prevTime = 0; 74 | unsigned char rx1_ch = LoRa_Settings->Channel_Rx; 75 | #ifdef US_915 76 | unsigned char rx1_dr = LoRa_Settings->Datarate_Tx+10; 77 | #elif defined(AU_915) 78 | unsigned char rx1_dr = LoRa_Settings->Datarate_Tx+10; 79 | #elif defined(EU_868) 80 | unsigned char rx1_dr = LoRa_Settings->Datarate_Tx; 81 | #elif defined(IN_865) 82 | unsigned char rx1_dr = LoRa_Settings->Datarate_Tx; 83 | #else // AS_923 and AS_923_2 84 | unsigned char rx1_dr = LoRa_Settings->Datarate_Tx; 85 | #endif 86 | 87 | //Transmit 88 | if(*RFM_Command == NEW_RFM_COMMAND){ 89 | #if (SAMR34) 90 | pinMode(RFM_SWITCH,OUTPUT); 91 | digitalWrite(RFM_SWITCH,0); //Rf switch inside RAK module change to Tx 92 | #endif 93 | //Lora send data 94 | LORA_Send_Data(Data_Tx, Session_Data, LoRa_Settings); 95 | prevTime = millis(); 96 | 97 | #if (SAMR34) 98 | digitalWrite(RFM_SWITCH,1); //Rf switch inside RAK module change to Rx 99 | #endif 100 | 101 | // Class C open RX2 immediately after sending data 102 | if(LoRa_Settings->Mote_Class == CLASS_C){ 103 | #ifdef US_915 104 | LoRa_Settings->Channel_Rx = 0x08; // set Rx2 channel 923.3 MHZ 105 | LoRa_Settings->Datarate_Rx = SF12BW500; //set RX2 datarate 12 106 | #elif defined(EU_868) 107 | LoRa_Settings->Channel_Rx = CHRX2; // set Rx2 channel 923.3 MHZ 108 | LoRa_Settings->Datarate_Rx = SF12BW125; //set RX2 datarate 12 109 | #elif defined(IN_865) 110 | LoRa_Settings->Channel_Rx = CHRX2; // set Rx2 channel 866.550 MHZ 111 | LoRa_Settings->Datarate_Rx = SF10BW125; //set RX2 datarate 10 112 | #elif defined(AS_923) || defined(AS_923_2) 113 | LoRa_Settings->Channel_Rx = 0x00; // set Rx2 channel 923.2 (AS_923) or 921.4 (AS_923_2) 114 | LoRa_Settings->Datarate_Rx = SF10BW125; //set RX2 datarate 10 115 | /* Added the band AU_915 for use in class C */ 116 | #elif defined(AU_915) 117 | LoRa_Settings->Channel_Rx = 0x08; // set Rx2 channel 923.3 MHZ 118 | LoRa_Settings->Datarate_Rx = SF12BW500; //set RX2 datarate 12 119 | #endif 120 | LORA_Receive_Data(Data_Rx, Session_Data, OTAA_Data, Message_Rx, LoRa_Settings); //BUG DETECT SENDED PACKET ALWAYS (IT DOES UPDATE) 121 | } 122 | 123 | //LoRaWAN Link Layer Specification v1.0.4 line 375 124 | //Wait rx1 window delay, TO TEST check if class c receives anything 125 | do{ 126 | yield(); // Do nothing during rx1 window delay 127 | }while(millis() - prevTime < Receive_Delay_1); 128 | 129 | //RX1 Window 130 | //Return to datarate and channel for RX1 131 | LoRa_Settings->Channel_Rx = rx1_ch; // set RX1 channel 132 | LoRa_Settings->Datarate_Rx = rx1_dr; // set RX1 datarate 133 | 134 | do{ 135 | LORA_Receive_Data(Data_Rx, Session_Data, OTAA_Data, Message_Rx, LoRa_Settings); 136 | }while(millis() - prevTime < Receive_Delay_1 + RX1_Window); 137 | //Return if message on RX1 138 | if (Data_Rx->Counter>0){ 139 | return; 140 | } 141 | 142 | // Class C open RX2 immediately after first rx window 143 | if(LoRa_Settings->Mote_Class == CLASS_C){ 144 | #ifdef US_915 145 | LoRa_Settings->Channel_Rx = 0x08; // set Rx2 channel 923.3 MHZ 146 | LoRa_Settings->Datarate_Rx = SF12BW500; //set RX2 datarate 12 147 | #elif defined(EU_868) 148 | LoRa_Settings->Channel_Rx = CHRX2; // set Rx2 channel 923.3 MHZ 149 | LoRa_Settings->Datarate_Rx = SF12BW125; //set RX2 datarate 12 150 | #elif defined(IN_865) 151 | LoRa_Settings->Channel_Rx = CHRX2; // set Rx2 channel 866.550 MHZ 152 | LoRa_Settings->Datarate_Rx = SF10BW125; //set RX2 datarate 10 153 | #elif defined(AS_923) || defined(AS_923_2) 154 | LoRa_Settings->Channel_Rx = 0x00; // set Rx2 channel 923.2 (AS_923) or 921.4 (AS_923_2) 155 | LoRa_Settings->Datarate_Rx = SF10BW125; //set RX2 datarate 10 156 | /* Added the band AU_915 for use in class C */ 157 | #elif defined(AU_915) 158 | LoRa_Settings->Channel_Rx = 0x08; // set Rx2 channel 923.3 MHZ 159 | LoRa_Settings->Datarate_Rx = SF12BW500; //set RX2 datarate 12 160 | #endif 161 | LORA_Receive_Data(Data_Rx, Session_Data, OTAA_Data, Message_Rx, LoRa_Settings); //BUG DETECT SENDED PACKET ALWAYS (IT DOES UPDATE) 162 | } 163 | 164 | //LoRaWAN Link Layer Specification v1.0.4 line 375 165 | //Wait rx2 window delay, TO TEST check if class c receives anything 166 | do{ 167 | yield(); // Do nothing during rx2 window delay 168 | }while(millis() - prevTime < Receive_Delay_2); 169 | 170 | //RX2 Window 171 | //Configure datarate and channel for RX2 172 | #ifdef US_915 173 | LoRa_Settings->Channel_Rx = 0x08; // set Rx2 channel 923.3 MHZ 174 | LoRa_Settings->Datarate_Rx = SF12BW500; //set RX2 datarate 12 175 | #elif defined(EU_868) 176 | LoRa_Settings->Channel_Rx = CHRX2; // set Rx2 channel 923.3 MHZ 177 | LoRa_Settings->Datarate_Rx = SF12BW125; //set RX2 datarate 12 178 | #elif defined(IN_865) 179 | LoRa_Settings->Channel_Rx = CHRX2; // set Rx2 channel 866.550 MHZ 180 | LoRa_Settings->Datarate_Rx = SF10BW125; //set RX2 datarate 10 181 | #elif defined(AS_923) || defined(AS_923_2) 182 | LoRa_Settings->Channel_Rx = 0x00; // set Rx2 channel 923.2 (AS_923) or 921.4 (AS_923_2) 183 | LoRa_Settings->Datarate_Rx = SF10BW125; //set RX2 datarate 10 184 | #elif defined(AU_915) 185 | LoRa_Settings->Channel_Rx = 0x08; // set Rx2 channel 923.3 MHZ 186 | LoRa_Settings->Datarate_Rx = SF12BW500; //set RX2 datarate 12 187 | #endif 188 | 189 | //Receive Data RX2 190 | do{ 191 | LORA_Receive_Data(Data_Rx, Session_Data, OTAA_Data, Message_Rx, LoRa_Settings); 192 | }while(millis() - prevTime < Receive_Delay_2 + RX2_Window); 193 | 194 | //Return if message on RX2 195 | if (Data_Rx->Counter>0){ 196 | return; 197 | } 198 | 199 | } 200 | } 201 | 202 | /* 203 | ***************************************************************************************** 204 | * Description : Function that is used to build a LoRaWAN data message and then tranmit it. 205 | * 206 | * Arguments : *Data_Tx pointer to tranmit buffer 207 | * *Session_Data pointer to sLoRa_Session sturct 208 | * *LoRa_Settings pointer to sSetting struct 209 | ***************************************************************************************** 210 | */ 211 | void LORA_Send_Data(sBuffer *Data_Tx, sLoRa_Session *Session_Data, sSettings *LoRa_Settings) 212 | { 213 | //Define variables 214 | unsigned char i; 215 | 216 | //Initialise RFM buffer 217 | unsigned char RFM_Data[MAX_UPLINK_PAYLOAD_SIZE+65]; 218 | sBuffer RFM_Package = {&RFM_Data[0], 0x00}; 219 | 220 | //Initialise Message struct for a transmit message 221 | sLoRa_Message Message; 222 | 223 | Message.MAC_Header = 0x00; 224 | Message.Frame_Port = 0x01; //Frame port always 1 for now 225 | Message.Frame_Control = 0x00; 226 | 227 | //Load device address from session data into the message 228 | Message.DevAddr[0] = Session_Data->DevAddr[0]; 229 | Message.DevAddr[1] = Session_Data->DevAddr[1]; 230 | Message.DevAddr[2] = Session_Data->DevAddr[2]; 231 | Message.DevAddr[3] = Session_Data->DevAddr[3]; 232 | 233 | //Set up direction 234 | Message.Direction = 0x00; 235 | 236 | //Load the frame counter from the session data into the message 237 | Message.Frame_Counter = *Session_Data->Frame_Counter; 238 | 239 | //Set confirmation 240 | //Unconfirmed 241 | if(LoRa_Settings->Confirm == 0x00) 242 | { 243 | Message.MAC_Header = Message.MAC_Header | 0x40; 244 | } 245 | //Confirmed 246 | else 247 | { 248 | Message.MAC_Header = Message.MAC_Header | 0x80; 249 | } 250 | 251 | //Build the Radio Package 252 | //Load mac header 253 | RFM_Package.Data[0] = Message.MAC_Header; 254 | 255 | //Load device address 256 | RFM_Package.Data[1] = Message.DevAddr[3]; 257 | RFM_Package.Data[2] = Message.DevAddr[2]; 258 | RFM_Package.Data[3] = Message.DevAddr[1]; 259 | RFM_Package.Data[4] = Message.DevAddr[0]; 260 | 261 | //Load frame control 262 | RFM_Package.Data[5] = Message.Frame_Control; 263 | 264 | //Load frame counter 265 | RFM_Package.Data[6] = (*Session_Data->Frame_Counter & 0x00FF); 266 | RFM_Package.Data[7] = ((*Session_Data->Frame_Counter >> 8) & 0x00FF); 267 | 268 | //Set data counter to 8 269 | RFM_Package.Counter = 8; 270 | 271 | //If there is data load the Frame_Port field 272 | //Encrypt the data and load the data 273 | if(Data_Tx->Counter > 0x00) 274 | { 275 | //Load Frame port field 276 | //RFM_Data[8] = Message.Frame_Port; 277 | RFM_Package.Data[8] = LoRa_Settings->Mport; 278 | 279 | //Raise package counter 280 | RFM_Package.Counter++; 281 | 282 | //Encrypt the data 283 | Encrypt_Payload(Data_Tx, Session_Data->AppSKey, &Message); 284 | 285 | //Load Data 286 | for(i = 0; i < Data_Tx->Counter; i++) 287 | { 288 | RFM_Package.Data[RFM_Package.Counter++] = Data_Tx->Data[i]; 289 | } 290 | 291 | 292 | } 293 | 294 | //Calculate MIC 295 | Construct_Data_MIC(&RFM_Package, Session_Data, &Message); 296 | 297 | //Load MIC in package 298 | for(i = 0; i < 4; i++) 299 | { 300 | RFM_Package.Data[RFM_Package.Counter++] = Message.MIC[i]; 301 | } 302 | 303 | //Send Package 304 | RFM_Send_Package(&RFM_Package, LoRa_Settings); 305 | 306 | //Raise Frame counter 307 | if(*Session_Data->Frame_Counter != 0xFFFF) 308 | { 309 | //Raise frame counter 310 | *Session_Data->Frame_Counter = *Session_Data->Frame_Counter + 1; 311 | } 312 | else 313 | { 314 | *Session_Data->Frame_Counter = 0x0000; 315 | } 316 | 317 | //Change channel for next message if hopping is activated 318 | if(LoRa_Settings->Channel_Hopping == 0x01) 319 | { 320 | if(LoRa_Settings->Channel_Tx < 0x07) 321 | { 322 | LoRa_Settings->Channel_Tx++; 323 | } 324 | else 325 | { 326 | LoRa_Settings->Channel_Tx = 0x00; 327 | } 328 | } 329 | } 330 | 331 | /* 332 | ***************************************************************************************** 333 | * Description : Function that is used to receive a LoRaWAN message and retrieve the data from the RFM 334 | * Also checks on CRC, MIC and Device Address 335 | * This function is used for Class A and C motes. 336 | * 337 | * Arguments : *Data_Rx pointer to receive buffer 338 | * *Session_Data pointer to sLoRa_Session sturct 339 | * *OTAA_Data pointer to sLoRa_OTAA struct 340 | * *Message_Rx pointer to sLoRa_Message struct used for the received message information 341 | * *LoRa_Settings pointer to sSetting struct 342 | ***************************************************************************************** 343 | */ 344 | void LORA_Receive_Data(sBuffer *Data_Rx, sLoRa_Session *Session_Data, sLoRa_OTAA *OTAA_Data, sLoRa_Message *Message, sSettings *LoRa_Settings) 345 | { 346 | unsigned char i; 347 | 348 | //Initialise RFM buffer 349 | unsigned char RFM_Data[MAX_DOWNLINK_PAYLOAD_SIZE+65]; 350 | sBuffer RFM_Package = {&RFM_Data[0], 0x00}; 351 | 352 | unsigned char MIC_Check; 353 | unsigned char Address_Check; 354 | 355 | unsigned char Frame_Options_Length; 356 | 357 | unsigned char Data_Location; 358 | 359 | message_t Message_Status = NO_MESSAGE; 360 | 361 | //If it is a type A device switch RFM to single receive 362 | if(LoRa_Settings->Mote_Class == CLASS_A) 363 | { 364 | Message_Status = RFM_Single_Receive(LoRa_Settings); 365 | } 366 | else 367 | { 368 | //Switch RFM to standby 369 | RFM_Switch_Mode(RFM_MODE_STANDBY); 370 | Message_Status = NEW_MESSAGE; 371 | } 372 | 373 | //If there is a message received get the data from the RFM 374 | if(Message_Status == NEW_MESSAGE) 375 | { 376 | Message_Status = RFM_Get_Package(&RFM_Package); 377 | 378 | //If mote class C switch RFM back to continuous receive 379 | if(LoRa_Settings->Mote_Class == CLASS_C) 380 | { 381 | //Switch RFM to Continuous Receive 382 | RFM_Continuous_Receive(LoRa_Settings); 383 | } 384 | } 385 | //if CRC ok breakdown package 386 | if(Message_Status == CRC_OK) 387 | { 388 | //Get MAC_Header 389 | Message->MAC_Header = RFM_Data[0]; 390 | 391 | //Data message 392 | if(Message->MAC_Header == 0x40 || Message->MAC_Header == 0x60 || Message->MAC_Header == 0x80 || Message->MAC_Header == 0xA0) 393 | { 394 | //Get device address from received data 395 | Message->DevAddr[0] = RFM_Data[4]; 396 | Message->DevAddr[1] = RFM_Data[3]; 397 | Message->DevAddr[2] = RFM_Data[2]; 398 | Message->DevAddr[3] = RFM_Data[1]; 399 | 400 | //Get frame control field 401 | Message->Frame_Control = RFM_Data[5]; 402 | 403 | //Get frame counter 404 | Message->Frame_Counter = RFM_Data[7]; 405 | Message->Frame_Counter = (Message->Frame_Counter << 8) + RFM_Data[6]; 406 | 407 | //Lower Package length with 4 to remove MIC length 408 | RFM_Package.Counter -= 4; 409 | 410 | //Calculate MIC 411 | Construct_Data_MIC(&RFM_Package, Session_Data, Message); 412 | 413 | MIC_Check = 0x00; 414 | 415 | //Compare MIC 416 | for(i = 0x00; i < 4; i++) 417 | { 418 | if(RFM_Data[RFM_Package.Counter + i] == Message->MIC[i]) 419 | { 420 | MIC_Check++; 421 | } 422 | } 423 | 424 | //Check MIC 425 | if(MIC_Check == 0x04) 426 | { 427 | Message_Status = MIC_OK; 428 | } 429 | else 430 | { 431 | Message_Status = WRONG_MESSAGE; 432 | } 433 | 434 | Address_Check = 0; 435 | 436 | //Check address 437 | if(MIC_Check == 0x04) 438 | { 439 | for(i = 0x00; i < 4; i++) 440 | { 441 | if(Session_Data->DevAddr[i] == Message->DevAddr[i]) 442 | { 443 | Address_Check++; 444 | } 445 | } 446 | } 447 | if(Address_Check == 0x04) 448 | { 449 | Message_Status = ADDRESS_OK; 450 | } 451 | else 452 | { 453 | Message_Status = WRONG_MESSAGE; 454 | } 455 | 456 | //if the address is OK then decrypt the data 457 | //Send the data to USB 458 | if(Message_Status == ADDRESS_OK) 459 | { 460 | 461 | Data_Location = 8; 462 | 463 | //Get length of frame options field 464 | Frame_Options_Length = (Message->Frame_Control & 0x0F); 465 | 466 | //Add length of frame options field to data location 467 | Data_Location = Data_Location + Frame_Options_Length; 468 | 469 | //Check if ther is data in the package 470 | if(RFM_Package.Counter == Data_Location) 471 | { 472 | Data_Rx->Counter = 0x00; 473 | } 474 | else 475 | { 476 | //Get port field when ther is data 477 | Message->Frame_Port = RFM_Data[8]; 478 | 479 | //Calculate the amount of data in the package 480 | Data_Rx->Counter = (RFM_Package.Counter - Data_Location -1); 481 | 482 | //Correct the data location by 1 for the Fport field 483 | Data_Location = (Data_Location + 1); 484 | } 485 | 486 | //Copy and decrypt the data 487 | if(Data_Rx->Counter != 0x00) 488 | { 489 | for(i = 0; i < Data_Rx->Counter; i++) 490 | { 491 | Data_Rx->Data[i] = RFM_Data[Data_Location + i]; 492 | } 493 | 494 | //Check frame port fiels. When zero it is a mac command message encrypted with NwkSKey 495 | if(Message->Frame_Port == 0x00) 496 | { 497 | Encrypt_Payload(Data_Rx, Session_Data->NwkSKey, Message); 498 | } 499 | else 500 | { 501 | Encrypt_Payload(Data_Rx, Session_Data->AppSKey, Message); 502 | } 503 | Message_Status = MESSAGE_DONE; 504 | } 505 | } 506 | } 507 | 508 | if(Message_Status == WRONG_MESSAGE) 509 | { 510 | Data_Rx->Counter = 0x00; 511 | } 512 | } 513 | } 514 | /* 515 | ***************************************************************************************** 516 | * Description : Function that is used to generate device nonce used in the join request function 517 | * This is based on a pseudo random function in the arduino library 518 | * 519 | * Arguments : *Devnonce pointer to the devnonce arry of withc is unsigned char[2] 520 | ***************************************************************************************** 521 | */ 522 | static void Generate_DevNonce(unsigned char *DevNonce) 523 | { 524 | #ifdef ESP32 525 | // Use the built-in random number generator of ESP32 to obtain a random value 526 | esp_fill_random(DevNonce, 2); // Fill the first 2 bytes of DevNonce with random values 527 | #else 528 | unsigned int RandNumber; 529 | RandNumber = random(0xFFFF); 530 | DevNonce[0] = RandNumber & 0x00FF; 531 | DevNonce[1] = (RandNumber >> 8) & 0x00FF; 532 | #endif 533 | } 534 | /* 535 | ***************************************************************************************** 536 | * Description : Function that is used to send a join request to a network. 537 | * 538 | * Arguments : *OTAA_Data pointer to sLoRa_OTAA struct 539 | * *LoRa_Settings pointer to sSetting struct 540 | ***************************************************************************************** 541 | */ 542 | void LoRa_Send_JoinReq(sLoRa_OTAA *OTAA_Data, sSettings *LoRa_Settings) 543 | { 544 | unsigned char i; 545 | 546 | //Initialise RFM data buffer 547 | unsigned char RFM_Data[23]; 548 | sBuffer RFM_Package = { &RFM_Data[0], 0x00}; 549 | 550 | //Initialise message sturct 551 | sLoRa_Message Message; 552 | 553 | Message.MAC_Header = 0x00; //Join request 554 | Message.Direction = 0x00; //Set up Direction 555 | 556 | //Construct OTAA Request message 557 | //Load Header in package 558 | RFM_Data[0] = Message.MAC_Header; 559 | 560 | //Load AppEUI in package 561 | for(i = 0x00; i < 8; i++) 562 | { 563 | RFM_Data[i+1] = OTAA_Data->AppEUI[7-i]; 564 | } 565 | 566 | //Load DevEUI in package 567 | for(i= 0x00; i < 8; i++) 568 | { 569 | RFM_Data[i+9] = OTAA_Data->DevEUI[7-i]; 570 | } 571 | 572 | //Generate DevNonce 573 | Generate_DevNonce(OTAA_Data->DevNonce); 574 | 575 | //Load DevNonce in package 576 | RFM_Data[17] = OTAA_Data->DevNonce[0]; 577 | RFM_Data[18] = OTAA_Data->DevNonce[1]; 578 | 579 | //Set length of package 580 | RFM_Package.Counter = 19; 581 | 582 | //Get MIC 583 | Calculate_MIC(&RFM_Package, OTAA_Data->AppKey, &Message); 584 | 585 | //Load MIC in package 586 | RFM_Data[19] = Message.MIC[0]; 587 | RFM_Data[20] = Message.MIC[1]; 588 | RFM_Data[21] = Message.MIC[2]; 589 | RFM_Data[22] = Message.MIC[3]; 590 | 591 | //Set length of package to the right length 592 | RFM_Package.Counter = 23; 593 | 594 | //Send Package 595 | RFM_Send_Package(&RFM_Package, LoRa_Settings); 596 | } 597 | bool LORA_join_Accept(sBuffer *Data_Rx,sLoRa_Session *Session_Data, sLoRa_OTAA *OTAA_Data, sLoRa_Message *Message, sSettings *LoRa_Settings) 598 | { 599 | bool joinStatus = false; 600 | unsigned char i; 601 | //Initialise RFM buffer 602 | unsigned char RFM_Data[64]; 603 | sBuffer RFM_Package = {&RFM_Data[0], 0x00}; 604 | unsigned char MIC_Check; 605 | 606 | message_t Message_Status = NO_MESSAGE; 607 | 608 | //RFM to single receive 609 | Message_Status = RFM_Single_Receive(LoRa_Settings); 610 | //If there is a message received get the data from the RFM 611 | if(Message_Status == NEW_MESSAGE) 612 | Message_Status = RFM_Get_Package(&RFM_Package); 613 | 614 | #if defined(ESP8266) || defined(ESP32) 615 | yield(); 616 | #endif 617 | 618 | //if CRC ok breakdown package 619 | if(Message_Status == CRC_OK) 620 | { 621 | //Get MAC_Header 622 | Message->MAC_Header = RFM_Data[0]; 623 | 624 | //Join Accept message 625 | if(Message->MAC_Header == 0x20) 626 | { 627 | //Copy the data into the data array 628 | for(i = 0x00; i < RFM_Package.Counter; i++) 629 | Data_Rx->Data[i] = RFM_Package.Data[i]; 630 | 631 | //Set data counter 632 | Data_Rx->Counter = RFM_Package.Counter; 633 | 634 | //Decrypt the data 635 | for(i = 0x00; i < ((Data_Rx->Counter - 1) / 16); i++) 636 | AES_Encrypt(&(Data_Rx->Data[(i*16)+1]),OTAA_Data->AppKey); 637 | 638 | //Calculate MIC 639 | //Remove MIC from number of bytes 640 | Data_Rx->Counter -= 4; 641 | 642 | //Get MIC 643 | Calculate_MIC(Data_Rx, OTAA_Data->AppKey, Message); 644 | 645 | //Clear MIC check counter 646 | MIC_Check = 0x00; 647 | 648 | //Compare MIC 649 | for(i = 0x00; i < 4; i++) 650 | if(Data_Rx->Data[Data_Rx->Counter + i] == Message->MIC[i]) 651 | MIC_Check++; 652 | 653 | //Check if MIC compares 654 | if(MIC_Check == 0x04) 655 | Message_Status = MIC_OK; 656 | else 657 | Message_Status = WRONG_MESSAGE; 658 | 659 | //Get Key's and data from package when MIC is OK 660 | if(Message_Status == MIC_OK) 661 | { 662 | //Get AppNonce 663 | for(i = 0; i< 3; i++) 664 | OTAA_Data->AppNonce[i] = Data_Rx->Data[i+1]; 665 | 666 | //Get Net ID 667 | for(i = 0; i< 3; i++) 668 | OTAA_Data->NetID[i] = Data_Rx->Data[i + 4]; 669 | 670 | //Get session Device address 671 | for(i = 0; i< 4; i++) 672 | Session_Data->DevAddr[3-i] = Data_Rx->Data[i + 7]; 673 | 674 | //Calculate Network Session Key 675 | Session_Data->NwkSKey[0] = 0x01; 676 | 677 | //Load AppNonce 678 | for(i = 0; i < 3; i++) 679 | Session_Data->NwkSKey[i+1] = OTAA_Data->AppNonce[i]; 680 | 681 | //Load NetID 682 | for(i = 0; i < 3; i++) 683 | Session_Data->NwkSKey[i+4] = OTAA_Data->NetID[i]; 684 | 685 | //Load Dev Nonce 686 | Session_Data->NwkSKey[7] = OTAA_Data->DevNonce[0]; 687 | Session_Data->NwkSKey[8] = OTAA_Data->DevNonce[1]; 688 | 689 | //Pad with zeros 690 | for(i = 9; i <= 15; i++) 691 | Session_Data->NwkSKey[i] = 0x00; 692 | 693 | //Copy to AppSkey 694 | for(i = 0x00; i < 16; i++) 695 | Session_Data->AppSKey[i] = Session_Data->NwkSKey[i]; 696 | 697 | //Change first byte of AppSKey 698 | Session_Data->AppSKey[0] = 0x02; 699 | 700 | //Calculate the keys 701 | AES_Encrypt(Session_Data->NwkSKey,OTAA_Data->AppKey); 702 | AES_Encrypt(Session_Data->AppSKey,OTAA_Data->AppKey); 703 | 704 | //Reset Frame counter 705 | *Session_Data->Frame_Counter = 0x0000; 706 | 707 | //Clear Data counter 708 | Data_Rx->Counter = 0x00; 709 | 710 | #ifdef DEBUG 711 | Serial.print(F("NwkSKey: ")); 712 | for(byte i = 0; i < 16 ;++i) 713 | Serial.print(Session_Data->NwkSKey[i],HEX); 714 | Serial.print(F("\nAppSKey: ")); 715 | for(byte i = 0; i < 16 ;++i) 716 | Serial.print(Session_Data->AppSKey[i],HEX); 717 | Serial.println(); 718 | #endif 719 | joinStatus = true; 720 | } 721 | } 722 | } 723 | return joinStatus; 724 | } 725 | 726 | 727 | 728 | -------------------------------------------------------------------------------- /src/arduino-rfm/RFM95.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************************** 2 | * Copyright 2017 Ideetron B.V. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | ******************************************************************************************/ 17 | /**************************************************************************************** 18 | * File: RFM95.cpp 19 | * Author: Gerben den Hartog 20 | * Compagny: Ideetron B.V. 21 | * Website: http://www.ideetron.nl/LoRa 22 | * E-mail: info@ideetron.nl 23 | ****************************************************************************************/ 24 | /**************************************************************************************** 25 | * Created on: 06-01-2017 26 | * Supported Hardware: ID150119-02 Nexus board with RFM95 27 | ****************************************************************************************/ 28 | 29 | #include 30 | #include 31 | #include "RFM95.h" 32 | #include "Config.h" 33 | 34 | /** 35 | * Lora Frequencies 36 | * Tested on all subbands in US915 37 | */ 38 | #if defined(US_915) 39 | #if defined(SUBND_0)//[902.3 - 903.7] MHz 40 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 41 | { 0xE1, 0x93, 0x59 }, //Channel [0], 902.3 MHz / 61.035 Hz = 14783321 = 0xE19359 42 | { 0xE1, 0xA0, 0x26 }, //Channel [1], 902.5 MHz / 61.035 Hz = 14786598 = 0xE1A026 43 | { 0xE1, 0xAC, 0xF3 }, //Channel [2], 902.7 MHz / 61.035 Hz = 14789875 = 0xE1ACF3 44 | { 0xE1, 0xB9, 0xBF }, //Channel [3], 902.9 MHz / 61.035 Hz = 14793151 = 0xE1B9BF 45 | { 0xE1, 0xC6, 0x8C }, //Channel [4], 903.1 MHz / 61.035 Hz = 14796428 = 0xE1C68C 46 | { 0xE1, 0xD3, 0x59 }, //Channel [5], 903.3 MHz / 61.035 Hz = 14799705 = 0xE1D359 47 | { 0xE1, 0xE0, 0x26 }, //Channel [6], 903.5 MHz / 61.035 Hz = 14802982 = 0xE1E026 48 | { 0xE1, 0xEC, 0xF3 }, //Channel [7], 903.7 MHz / 61.035 Hz = 14806259 = 0xE1ECF3 49 | }; 50 | #elif defined(SUBND_1)//[903.9 - 905.3] MHz 51 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 52 | { 0xE1, 0xF9, 0xC0 }, //Channel [0], 903.9 MHz / 61.035 Hz = 14809536 = 0xE1F9C0 53 | { 0xE2, 0x06, 0x8C }, //Channel [1], 904.1 MHz / 61.035 Hz = 14812812 = 0xE2068C 54 | { 0xE2, 0x13, 0x59 }, //Channel [2], 904.3 MHz / 61.035 Hz = 14816089 = 0xE21359 55 | { 0xE2, 0x20, 0x26 }, //Channel [3], 904.5 MHz / 61.035 Hz = 14819366 = 0xE22026 56 | { 0xE2, 0x2C, 0xF3 }, //Channel [4], 904.7 MHz / 61.035 Hz = 14822643 = 0xE22CF3 57 | { 0xE2, 0x39, 0xC0 }, //Channel [5], 904.9 MHz / 61.035 Hz = 14825920 = 0xE239C0 58 | { 0xE2, 0x46, 0x8C }, //Channel [6], 905.1 MHz / 61.035 Hz = 14829196 = 0xE2468C 59 | { 0xE2, 0x53, 0x59 }, //Channel [7], 905.3 MHz / 61.035 Hz = 14832473 = 0xE25359 60 | }; 61 | #elif defined(SUBND_2)//[905.5 - 906.9] MHz 62 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 63 | { 0xE2, 0x60, 0x26 }, //Channel [0], 905.5 MHz / 61.035 Hz = 14835750 = 0xE26026 64 | { 0xE2, 0x6C, 0xF3 }, //Channel [1], 905.7 MHz / 61.035 Hz = 14839027 = 0xE26CF3 65 | { 0xE2, 0x79, 0xC0 }, //Channel [2], 905.9 MHz / 61.035 Hz = 14842304 = 0xE279C0 66 | { 0xE2, 0x86, 0x8C }, //Channel [3], 906.1 MHz / 61.035 Hz = 14845580 = 0xE2868C 67 | { 0xE2, 0x93, 0x59 }, //Channel [4], 906.3 MHz / 61.035 Hz = 14848857 = 0xE29359 68 | { 0xE2, 0xA0, 0x26 }, //Channel [5], 906.5 MHz / 61.035 Hz = 14852134 = 0xE2A026 69 | { 0xE2, 0xAC, 0xF3 }, //Channel [6], 906.7 MHz / 61.035 Hz = 14855411 = 0xE2ACF3 70 | { 0xE2, 0xB9, 0xC0 }, //Channel [7], 906.9 MHz / 61.035 Hz = 14858688 = 0xE2B9C0 71 | }; 72 | #elif defined(SUBND_3)//[907.1 - 908.5] MHz 73 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 74 | { 0xE2, 0xC6, 0x8C }, //Channel [0], 907.1 MHz / 61.035 Hz = 14861964 = 0xE2C68C 75 | { 0xE2, 0xD3, 0x59 }, //Channel [1], 907.3 MHz / 61.035 Hz = 14865241 = 0xE2D359 76 | { 0xE2, 0xE0, 0x26 }, //Channel [2], 907.5 MHz / 61.035 Hz = 14868518 = 0xE2E026 77 | { 0xE2, 0xEC, 0xF3 }, //Channel [3], 907.7 MHz / 61.035 Hz = 14871795 = 0xE2ECF3 78 | { 0xE2, 0xF9, 0xC0 }, //Channel [4], 907.9 MHz / 61.035 Hz = 14875072 = 0xE2F9C0 79 | { 0xE3, 0x06, 0x8C }, //Channel [5], 908.1 MHz / 61.035 Hz = 14878348 = 0xE3068C 80 | { 0xE3, 0x13, 0x59 }, //Channel [6], 908.3 MHz / 61.035 Hz = 14881625 = 0xE31359 81 | { 0xE3, 0x20, 0x26 }, //Channel [7], 908.5 MHz / 61.035 Hz = 14884902 = 0xE32026 82 | }; 83 | #elif defined(SUBND_4)//[908.7 - 910.1] MHz 84 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 85 | { 0xE3, 0x2C, 0xF3 }, //Channel [0], 908.7 MHz / 61.035 Hz = 14888179 = 0xE32CF3 86 | { 0xE3, 0x39, 0xC0 }, //Channel [1], 908.9 MHz / 61.035 Hz = 14891456 = 0xE339C0 87 | { 0xE3, 0x46, 0x8D }, //Channel [2], 909.1 MHz / 61.035 Hz = 14894733 = 0xE3468D 88 | { 0xE3, 0x53, 0x59 }, //Channel [3], 909.3 MHz / 61.035 Hz = 14898009 = 0xE35359 89 | { 0xE3, 0x60, 0x26 }, //Channel [4], 909.5 MHz / 61.035 Hz = 14901286 = 0xE36026 90 | { 0xE3, 0x6C, 0xF3 }, //Channel [5], 909.7 MHz / 61.035 Hz = 14904563 = 0xE36CF3 91 | { 0xE3, 0x79, 0xC0 }, //Channel [6], 909.9 MHz / 61.035 Hz = 14907840 = 0xE379C0 92 | { 0xE3, 0x86, 0x8D }, //Channel [7], 910.1 MHz / 61.035 Hz = 14911117 = 0xE3868D 93 | }; 94 | #elif defined(SUBND_5)//[910.3 - 911.7] MHz 95 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 96 | { 0xE3, 0x93, 0x59 }, //Channel [0], 910.3 MHz / 61.035 Hz = 14914393 = 0xE39359 97 | { 0xE3, 0xA0, 0x26 }, //Channel [1], 910.5 MHz / 61.035 Hz = 14917670 = 0xE3A026 98 | { 0xE3, 0xAC, 0xF3 }, //Channel [2], 910.7 MHz / 61.035 Hz = 14920947 = 0xE3ACF3 99 | { 0xE3, 0xB9, 0xC0 }, //Channel [3], 910.9 MHz / 61.035 Hz = 14924224 = 0xE3B9C0 100 | { 0xE3, 0xC6, 0x8D }, //Channel [4], 911.1 MHz / 61.035 Hz = 14927501 = 0xE3C68D 101 | { 0xE3, 0xD3, 0x59 }, //Channel [5], 911.3 MHz / 61.035 Hz = 14930777 = 0xE3D359 102 | { 0xE3, 0xE0, 0x26 }, //Channel [6], 911.5 MHz / 61.035 Hz = 14934054 = 0xE3E026 103 | { 0xE3, 0xEC, 0xF3 }, //Channel [7], 911.7 MHz / 61.035 Hz = 14937331 = 0xE3ECF3 104 | }; 105 | #elif defined(SUBND_6)//[911.9 - 913.3] MHz 106 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 107 | { 0xE3, 0xF9, 0xC0 }, //Channel [0], 911.9 MHz / 61.035 Hz = 14940608 = 0xE3F9C0 108 | { 0xE4, 0x06, 0x8D }, //Channel [1], 912.1 MHz / 61.035 Hz = 14943885 = 0xE4068D 109 | { 0xE4, 0x13, 0x59 }, //Channel [2], 912.3 MHz / 61.035 Hz = 14947161 = 0xE41359 110 | { 0xE4, 0x20, 0x26 }, //Channel [3], 912.5 MHz / 61.035 Hz = 14950438 = 0xE42026 111 | { 0xE4, 0x2C, 0xF3 }, //Channel [4], 912.7 MHz / 61.035 Hz = 14953715 = 0xE42CF3 112 | { 0xE4, 0x39, 0xC0 }, //Channel [5], 912.9 MHz / 61.035 Hz = 14956992 = 0xE439C0 113 | { 0xE4, 0x46, 0x8D }, //Channel [6], 913.1 MHz / 61.035 Hz = 14960269 = 0xE4468D 114 | { 0xE4, 0x53, 0x5A }, //Channel [7], 913.3 MHz / 61.035 Hz = 14963546 = 0xE4535A 115 | }; 116 | #elif defined(SUBND_7)//[913.5 - 914.9] MHz 117 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 118 | { 0xE4, 0x60, 0x26 }, //Channel [0], 913.5 MHz / 61.035 Hz = 14966822 = 0xE46026 119 | { 0xE4, 0x6C, 0xF3 }, //Channel [1], 913.7 MHz / 61.035 Hz = 14970099 = 0xE46CF3 120 | { 0xE4, 0x79, 0xC0 }, //Channel [2], 913.9 MHz / 61.035 Hz = 14973376 = 0xE479C0 121 | { 0xE4, 0x86, 0x8D }, //Channel [3], 914.1 MHz / 61.035 Hz = 14976653 = 0xE4868D 122 | { 0xE4, 0x93, 0x5A }, //Channel [4], 914.3 MHz / 61.035 Hz = 14979930 = 0xE4935A 123 | { 0xE4, 0xA0, 0x26 }, //Channel [5], 914.5 MHz / 61.035 Hz = 14983206 = 0xE4A026 124 | { 0xE4, 0xAC, 0xF3 }, //Channel [6], 914.7 MHz / 61.035 Hz = 14986483 = 0xE4ACF3 125 | { 0xE4, 0xB9, 0xC0 }, //Channel [7], 914.9 MHz / 61.035 Hz = 14989760 = 0xE4B9C0 126 | }; 127 | #endif 128 | static const PROGMEM unsigned char LoRa_RX_Freq[8][3] = { 129 | { 0xE6, 0xD3, 0x5A}, //Rcv Channel [0] 923.3 Mhz / 61.035 Hz = 15127386 = 0xE6D35A 130 | { 0xE6, 0xF9, 0xC0}, //Rcv Channel [1] 923.9 Mhz / 61.035 Hz = 15137216 = 0xE6F9C0 131 | { 0xE7, 0x20, 0x27}, //Rcv Channel [2] 924.5 Mhz / 61.035 Hz = 15147047 = 0xE72027 132 | { 0xE7, 0x46, 0x8D}, //Rcv Channel [3] 925.1 Mhz / 61.035 Hz = 15156877 = 0xE7468D 133 | { 0xE7, 0x6C, 0xF4}, //Rcv Channel [4] 925.7 Mhz / 61.035 Hz = 15166708 = 0xE76CF4 134 | { 0xE7, 0x93, 0x5A}, //Rcv Channel [5] 926.3 Mhz / 61.035 Hz = 15176538 = 0xE7935A 135 | { 0xE7, 0xB9, 0xC0}, //Rcv Channel [6] 926.9 Mhz / 61.035 Hz = 15186368 = 0xE7B9C0 136 | { 0xE7, 0xE0, 0x27}, //Rcv Channel [7] 927.5 Mhz / 61.035 Hz = 15196199 = 0xE7E027 137 | }; 138 | #elif defined(AU_915) 139 | #if defined(SUBND_0)//[915.2 - 916.6] MHz 140 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 141 | { 0xE4, 0xCC, 0xF3 }, //Channel [0], 915,2 MHz / 61.035 Hz = 14994675 = 0xE4CCF3 142 | { 0xE4, 0xD9, 0xC0 }, //Channel [1], 915,4 MHz / 61.035 Hz = 14997952 = 0xE4D9C0 143 | { 0xE4, 0xE6, 0x8D }, //Channel [2], 915,6 MHz / 61.035 Hz = 15001229 = 0xE4E68D 144 | { 0xE4, 0xF3, 0x5A }, //Channel [3], 915,8 MHz / 61.035 Hz = 15004506 = 0xE4F35A 145 | { 0xE5, 0x00, 0x26 }, //Channel [4], 916,0 MHz / 61.035 Hz = 15007782 = 0xE50026 146 | { 0xE5, 0x0C, 0xF3 }, //Channel [5], 916,2 MHz / 61.035 Hz = 15011059 = 0xE50CF3 147 | { 0xE5, 0x19, 0xC0 }, //Channel [6], 916,4 MHz / 61.035 Hz = 15014336 = 0xE519C0 148 | { 0xE5, 0x26, 0x8D }, //Channel [7], 916,6 MHz / 61.035 Hz = 15017613 = 0xE5268D 149 | }; 150 | #elif defined(SUBND_1)//[916.8 - 918.2] MHz 151 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 152 | { 0xE5, 0x33, 0x5A }, //Channel [0], 916,8 MHz / 61.035 Hz = 15020890 = 0xE5335A 153 | { 0xE5, 0x40, 0x26 }, //Channel [1], 917,0 MHz / 61.035 Hz = 15024166 = 0xE54026 154 | { 0xE5, 0x4C, 0xF3 }, //Channel [2], 917,2 MHz / 61.035 Hz = 15027443 = 0xE54CF3 155 | { 0xE5, 0x59, 0xC0 }, //Channel [3], 917,4 MHz / 61.035 Hz = 15030720 = 0xE559C0 156 | { 0xE5, 0x66, 0x8D }, //Channel [4], 917,6 MHz / 61.035 Hz = 15033997 = 0xE5668D 157 | { 0xE5, 0x73, 0x5A }, //Channel [5], 917,8 MHz / 61.035 Hz = 15037274 = 0xE5735A 158 | { 0xE5, 0x80, 0x27 }, //Channel [6], 918,0 MHz / 61.035 Hz = 15040551 = 0xE58027 159 | { 0xE5, 0x8C, 0xF3 }, //Channel [7], 918,2 MHz / 61.035 Hz = 15043827 = 0xE58CF3 160 | }; 161 | #elif defined(SUBND_2)//[918.4 - 919.8] MHz 162 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 163 | { 0xE5, 0x99, 0xC0 }, //Channel [0], 918,4 MHz / 61.035 Hz = 15047104 = 0xE599C0 164 | { 0xE5, 0xA6, 0x8D }, //Channel [1], 918,6 MHz / 61.035 Hz = 15050381 = 0xE5A68D 165 | { 0xE5, 0xB3, 0x5A }, //Channel [2], 918,8 MHz / 61.035 Hz = 15053658 = 0xE5B35A 166 | { 0xE5, 0xC0, 0x27 }, //Channel [3], 919,0 MHz / 61.035 Hz = 15056935 = 0xE5C027 167 | { 0xE5, 0xCC, 0xF3 }, //Channel [4], 919,2 MHz / 61.035 Hz = 15060211 = 0xE5CCF3 168 | { 0xE5, 0xD9, 0xC0 }, //Channel [5], 919,4 MHz / 61.035 Hz = 15063488 = 0xE5D9C0 169 | { 0xE5, 0xE6, 0x8D }, //Channel [6], 919,6 MHz / 61.035 Hz = 15066765 = 0xE5E68D 170 | { 0xE5, 0xF3, 0x5A }, //Channel [7], 919,8 MHz / 61.035 Hz = 15070042 = 0xE5F35A 171 | }; 172 | #elif defined(SUBND_3)//[920.0 - 921.4] MHz 173 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 174 | { 0xE6, 0x00, 0x27 }, //Channel [0], 920,0 MHz / 61.035 Hz = 15073319 = 0xE60027 175 | { 0xE6, 0x0C, 0xF3 }, //Channel [1], 920,2 MHz / 61.035 Hz = 15076595 = 0xE60CF3 176 | { 0xE6, 0x19, 0xC0 }, //Channel [2], 920,4 MHz / 61.035 Hz = 15079872 = 0xE619C0 177 | { 0xE6, 0x26, 0x8D }, //Channel [3], 920,6 MHz / 61.035 Hz = 15083149 = 0xE6268D 178 | { 0xE6, 0x33, 0x5A }, //Channel [4], 920,8 MHz / 61.035 Hz = 15086426 = 0xE6335A 179 | { 0xE6, 0x40, 0x27 }, //Channel [5], 921,0 MHz / 61.035 Hz = 15089703 = 0xE64027 180 | { 0xE6, 0x4C, 0xF3 }, //Channel [6], 921,2 MHz / 61.035 Hz = 15092979 = 0xE64CF3 181 | { 0xE6, 0x59, 0xC0 }, //Channel [7], 921,4 MHz / 61.035 Hz = 15096256 = 0xE659C0 182 | }; 183 | #elif defined(SUBND_4)//[921.6 - 923.0] MHz 184 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 185 | { 0xE6, 0x66, 0x8D }, //Channel [0], 921,6 MHz / 61.035 Hz = 15099533 = 0xE6668D 186 | { 0xE6, 0x73, 0x5A }, //Channel [1], 921,8 MHz / 61.035 Hz = 15102810 = 0xE6735A 187 | { 0xE6, 0x80, 0x27 }, //Channel [2], 922,0 MHz / 61.035 Hz = 15106087 = 0xE68027 188 | { 0xE6, 0x8C, 0xF3 }, //Channel [3], 922,2 MHz / 61.035 Hz = 15109363 = 0xE68CF3 189 | { 0xE6, 0x99, 0xC0 }, //Channel [4], 922,4 MHz / 61.035 Hz = 15112640 = 0xE699C0 190 | { 0xE6, 0xA6, 0x8D }, //Channel [5], 922,6 MHz / 61.035 Hz = 15115917 = 0xE6A68D 191 | { 0xE6, 0xB3, 0x5A }, //Channel [6], 922,8 MHz / 61.035 Hz = 15119194 = 0xE6B35A 192 | { 0xE6, 0xC0, 0x27 }, //Channel [7], 923,0 MHz / 61.035 Hz = 15122471 = 0xE6C027 193 | }; 194 | #elif defined(SUBND_5)//[923.2 - 924.6] MHz 195 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 196 | { 0xE6, 0xCC, 0xF4 }, //Channel [0], 923,2 MHz / 61.035 Hz = 15125748 = 0xE6CCF4 197 | { 0xE6, 0xD9, 0xC0 }, //Channel [1], 923,4 MHz / 61.035 Hz = 15129024 = 0xE6D9C0 198 | { 0xE6, 0xE6, 0x8D }, //Channel [2], 923,6 MHz / 61.035 Hz = 15132301 = 0xE6E68D 199 | { 0xE6, 0xF3, 0x5A }, //Channel [3], 923,8 MHz / 61.035 Hz = 15135578 = 0xE6F35A 200 | { 0xE7, 0x00, 0x27 }, //Channel [4], 924,0 MHz / 61.035 Hz = 15138855 = 0xE70027 201 | { 0xE7, 0x0C, 0xF4 }, //Channel [5], 924,2 MHz / 61.035 Hz = 15142132 = 0xE70CF4 202 | { 0xE7, 0x19, 0xC0 }, //Channel [6], 924,4 MHz / 61.035 Hz = 15145408 = 0xE719C0 203 | { 0xE7, 0x26, 0x8D }, //Channel [7], 924,6 MHz / 61.035 Hz = 15148685 = 0xE7268D 204 | }; 205 | #elif defined(SUBND_6)//[924.8 - 926.2] MHz 206 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 207 | { 0xE7, 0x33, 0x5A }, //Channel [0], 924,8 MHz / 61.035 Hz = 15151962 = 0xE7335A 208 | { 0xE7, 0x40, 0x27 }, //Channel [1], 925,0 MHz / 61.035 Hz = 15155239 = 0xE74027 209 | { 0xE7, 0x4C, 0xF4 }, //Channel [2], 925,2 MHz / 61.035 Hz = 15158516 = 0xE74CF4 210 | { 0xE7, 0x59, 0xC0 }, //Channel [3], 925,4 MHz / 61.035 Hz = 15161792 = 0xE759C0 211 | { 0xE7, 0x66, 0x8D }, //Channel [4], 925,6 MHz / 61.035 Hz = 15165069 = 0xE7668D 212 | { 0xE7, 0x73, 0x5A }, //Channel [5], 925,8 MHz / 61.035 Hz = 15168346 = 0xE7735A 213 | { 0xE7, 0x80, 0x27 }, //Channel [6], 926,0 MHz / 61.035 Hz = 15171623 = 0xE78027 214 | { 0xE7, 0x8C, 0xF4 }, //Channel [7], 926,2 MHz / 61.035 Hz = 15174900 = 0xE78CF4 215 | }; 216 | #elif defined(SUBND_7)//[926.4 - 927.8] MHz 217 | static const PROGMEM unsigned char LoRa_TX_Freq[8][3] = { 218 | { 0xE7, 0x99, 0xC0 }, //Channel [0], 926,4 MHz / 61.035 Hz = 15178176 = 0xE799C0 219 | { 0xE7, 0xA6, 0x8D }, //Channel [1], 926,6 MHz / 61.035 Hz = 15181453 = 0xE7A68D 220 | { 0xE7, 0xB3, 0x5A }, //Channel [2], 926,8 MHz / 61.035 Hz = 15184730 = 0xE7B35A 221 | { 0xE7, 0xC0, 0x27 }, //Channel [3], 927,0 MHz / 61.035 Hz = 15188007 = 0xE7C027 222 | { 0xE7, 0xCC, 0xF4 }, //Channel [4], 927,2 MHz / 61.035 Hz = 15191284 = 0xE7CCF4 223 | { 0xE7, 0xD9, 0xC0 }, //Channel [5], 927,4 MHz / 61.035 Hz = 15194560 = 0xE7D9C0 224 | { 0xE7, 0xE6, 0x8D }, //Channel [6], 927,6 MHz / 61.035 Hz = 15197837 = 0xE7E68D 225 | { 0xE7, 0xF3, 0x5A }, //Channel [7], 927,8 MHz / 61.035 Hz = 15201114 = 0xE7F35A 226 | }; 227 | #endif 228 | static const PROGMEM unsigned char LoRa_RX_Freq[8][3] = { 229 | { 0xE6, 0xD3, 0x5A}, //Rcv Channel [0] 923.3 Mhz / 61.035 Hz = 15127386 = 0xE6D35A 230 | { 0xE6, 0xF9, 0xC0}, //Rcv Channel [1] 923.9 Mhz / 61.035 Hz = 15137216 = 0xE6F9C0 231 | { 0xE7, 0x20, 0x27}, //Rcv Channel [2] 924.5 Mhz / 61.035 Hz = 15147047 = 0xE72027 232 | { 0xE7, 0x46, 0x8D}, //Rcv Channel [3] 925.1 Mhz / 61.035 Hz = 15156877 = 0xE7468D 233 | { 0xE7, 0x6C, 0xF4}, //Rcv Channel [4] 925.7 Mhz / 61.035 Hz = 15166708 = 0xE76CF4 234 | { 0xE7, 0x93, 0x5A}, //Rcv Channel [5] 926.3 Mhz / 61.035 Hz = 15176538 = 0xE7935A 235 | { 0xE7, 0xB9, 0xC0}, //Rcv Channel [6] 926.9 Mhz / 61.035 Hz = 15186368 = 0xE7B9C0 236 | { 0xE7, 0xE0, 0x27}, //Rcv Channel [7] 927.5 Mhz / 61.035 Hz = 15196199 = 0xE7E027 237 | }; 238 | #elif defined(AS_923) 239 | static const PROGMEM unsigned char LoRa_Frequency[8][3] = {//[922.0 - 923.4] MHz - New AS923/AS923-1 (920-923Mhz) 240 | { 0xE6, 0xCC, 0xF4 }, //Channel [0], 923.2 MHz / 61.035 Hz = 15125748 = 0xE6CCF4 - Default OTAA - RX2 241 | { 0xE6, 0xD9, 0xC0 }, //Channel [1], 923.4 MHz / 61.035 Hz = 15129024 = 0xE6D9C0 - Default OTAA 242 | { 0xE6, 0x8C, 0xF3 }, //Channel [2], 922.2 MHz / 61.035 Hz = 15109363 = 0xE68CF3 243 | { 0xE6, 0x99, 0xC0 }, //Channel [3], 922.4 MHz / 61.035 Hz = 15112640 = 0xE699C0 244 | { 0xE6, 0xA6, 0x8D }, //Channel [4], 922.6 MHz / 61.035 Hz = 15115917 = 0xE6A68D 245 | { 0xE6, 0xB3, 0x5A }, //Channel [5], 922.8 MHz / 61.035 Hz = 15119194 = 0xE6B35A 246 | { 0xE6, 0xC0, 0x27 }, //Channel [6], 923.0 MHz / 61.035 Hz = 15122471 = 0xE6C027 247 | { 0xE6, 0x80, 0x27 }, //Channel [7], 922.0 MHz / 61.035 Hz = 15106087 = 0xE68027 248 | }; 249 | #elif defined(AS_923_2) 250 | static const PROGMEM unsigned char LoRa_Frequency[8][3] = {//[921.4 - 922.8] MHz 251 | { 0xE6, 0x59, 0xC0 }, //Channel [0], 921.4 MHz / 61.035 Hz = 15096256 = 0xE659C0 - Default OTAA - RX2 252 | { 0xE6, 0x66, 0x8D }, //Channel [1], 921.6 MHz / 61.035 Hz = 15099533 = 0xE6668D - Default OTAA 253 | { 0xE6, 0x73, 0x5A }, //Channel [2], 921.8 MHz / 61.035 Hz = 15102810 = 0xE6735A 254 | { 0xE6, 0x80, 0x27 }, //Channel [3], 922.0 MHz / 61.035 Hz = 15106087 = 0xE68027 255 | { 0xE6, 0x8C, 0xF3 }, //Channel [4], 922.2 MHz / 61.035 Hz = 15109363 = 0xE68CF3 256 | { 0xE6, 0x99, 0xC0 }, //Channel [5], 922.4 MHz / 61.035 Hz = 15112640 = 0xE699C0 257 | { 0xE6, 0xA6, 0x8D }, //Channel [6], 922.6 MHz / 61.035 Hz = 15115917 = 0xE6A68D 258 | { 0xE6, 0xB3, 0x5A }, //Channel [7], 922.8 MHz / 61.035 Hz = 15119194 = 0xE6B35A 259 | }; 260 | #elif defined(EU_868) 261 | static const PROGMEM unsigned char LoRa_Frequency[9][3] = {//[868.1 - 867.9] MHz 262 | { 0xD9, 0x06, 0x8B }, //Channel [0], 868.1 MHz / 61.035 Hz = 14222987 = 0xD9068B 263 | { 0xD9, 0x13, 0x58 }, //Channel [1], 868.3 MHz / 61.035 Hz = 14226264 = 0xD91358 264 | { 0xD9, 0x20, 0x24 }, //Channel [2], 868.5 MHz / 61.035 Hz = 14229540 = 0xD92024 265 | { 0xD8, 0xC6, 0x8B }, //Channel [3], 867.1 MHz / 61.035 Hz = 14206603 = 0xD8C68B 266 | { 0xD8, 0xD3, 0x58 }, //Channel [4], 867.3 MHz / 61.035 Hz = 14209880 = 0xD8D358 267 | { 0xD8, 0xE0, 0x24 }, //Channel [5], 867.5 MHz / 61.035 Hz = 14213156 = 0xD8E024 268 | { 0xD8, 0xEC, 0xF1 }, //Channel [6], 867.7 MHz / 61.035 Hz = 14216433 = 0xD8ECF1 269 | { 0xD8, 0xF9, 0xBE }, //Channel [7], 867.9 MHz / 61.035 Hz = 14219710 = 0xD8F9BE 270 | { 0xD9, 0x61, 0xBE }, // RX2 Receive channel 869.525 MHz / 61.035 Hz = 14246334 = 0xD961BE 271 | }; 272 | #elif defined(IN_865) 273 | static const PROGMEM unsigned char LoRa_Frequency[4][3] = { 274 | { 0xD8, 0x44, 0x24 }, //Channel [0], 865.0625 MHz / 61.035 Hz = 14173220 = 0xD84424 275 | { 0xD8, 0x59, 0xE7 }, //Channel [1], 865.4025 MHz / 61.035 Hz = 14178791 = 0xD859E7 276 | { 0xD8, 0x7F, 0x2F }, //Channel [2], 865.9850 MHz / 61.035 Hz = 14188335 = 0xD87F2F 277 | { 0xD8, 0xA3, 0x58 }, // RX2 Receive channel 866.550 MHz / 61.035 Hz = 14197592 = 0xD8A358 278 | }; 279 | #endif 280 | 281 | /* 282 | ***************************************************************************************** 283 | * Description : Function that reads a register from the RFM and returns the value 284 | * 285 | * Arguments : RFM_Address Address of register to be read 286 | * 287 | * Returns : Value of the register 288 | ***************************************************************************************** 289 | */ 290 | 291 | static unsigned char RFM_Read(unsigned char RFM_Address) 292 | { 293 | unsigned char RFM_Data; 294 | 295 | //Add transactions in Read and Write methods 296 | SPI.beginTransaction(SPISettings(4000000,MSBFIRST,SPI_MODE0)); 297 | 298 | //Set NSS pin low to start SPI communication 299 | digitalWrite(RFM_pins.CS,LOW); 300 | 301 | //Send Address 302 | SPI.transfer(RFM_Address); 303 | //Send 0x00 to be able to receive the answer from the RFM 304 | RFM_Data = SPI.transfer(0x00); 305 | 306 | //Set NSS high to end communication 307 | digitalWrite(RFM_pins.CS,HIGH); 308 | 309 | //End the transaction so that other hardware can use the bus 310 | SPI.endTransaction(); 311 | 312 | #ifdef DEBUG 313 | Serial.print("SPI Read ADDR: "); 314 | Serial.print(RFM_Address, HEX); 315 | Serial.print(" DATA: "); 316 | Serial.println(RFM_Data, HEX); 317 | #endif 318 | 319 | //Return received data 320 | return RFM_Data; 321 | } 322 | /******************************************************************************************** 323 | * Description : Change Spread Factor and Band Width 324 | * 325 | * Arguments: _SF = {6,7,8,9,10,11,12} 326 | * _BW = {0x00 -> 7.8khz , 0x01 -> 10.4khz, 0x02 -> 15.6khz, 0x03 -> 20.8khz, 327 | * 0x04 -> 31.25khz , 0x05 -> 41.7khz, 0x06 -> 62.5khz, 0x07 -> 125khz, 328 | * 0x08 -> 250khz , 0x09 -> 500khz} 329 | ********************************************************************************************/ 330 | static void RFM_change_SF_BW(unsigned char _SF, unsigned char _BW) 331 | { 332 | RFM_Write(RFM_REG_MODEM_CONFIG2, (_SF << 4) | 0b0100); //SFx CRC On 333 | RFM_Write(RFM_REG_MODEM_CONFIG1,(_BW << 4) | 0x02); //x kHz 4/5 coding rate explicit header mode 334 | 335 | #if defined(EU_868) || defined(IN_865) || defined(AS_923) || defined(AS_923_2) 336 | if(_SF>10) 337 | RFM_Write(RFM_REG_MODEM_CONFIG3, 0b1100); //Low datarate optimization on AGC auto on 338 | else 339 | RFM_Write(RFM_REG_MODEM_CONFIG3, 0b0100); //Mobile node, low datarate optimization on AGC acorging to register LnaGain 340 | #else 341 | RFM_Write(RFM_REG_MODEM_CONFIG3, 0b0100); //Mobile node, low datarate optimization on AGC acorging to register LnaGain 342 | #endif 343 | 344 | } 345 | /* 346 | ***************************************************************************************** 347 | * Description : Function to change the datarate of the RFM module. Setting the following 348 | * register: Spreading factor, Bandwidth and low datarate optimisation. 349 | * 350 | * Arguments : Datarate the datarate to set 351 | ***************************************************************************************** 352 | */ 353 | static void RFM_Change_Datarate(unsigned char Datarate) 354 | { 355 | #if defined(US_915) 356 | switch (Datarate) { 357 | case 0x00: // SF10BW125 358 | RFM_change_SF_BW(10,0x07); 359 | break; 360 | case 0x01: // SF9BW125 361 | RFM_change_SF_BW(9,0x07); 362 | break; 363 | case 0x02: // SF8BW125 364 | RFM_change_SF_BW(8,0x07); 365 | break; 366 | case 0x03: // SF7BW125 367 | RFM_change_SF_BW(7,0x07); 368 | break; 369 | case 0x04: // SF8BW500 370 | RFM_change_SF_BW(8,0x09); 371 | break; 372 | case 0x08: // SF12BW500 373 | RFM_change_SF_BW(12,0x09); 374 | break; 375 | case 0x09: // SF11BW500 376 | RFM_change_SF_BW(11,0x09); 377 | break; 378 | case 0x0A: // SF10BW500 379 | RFM_change_SF_BW(10,0x09); 380 | break; 381 | case 0x0B: // SF9BW500 382 | RFM_change_SF_BW(9,0x09); 383 | break; 384 | case 0x0C: // SF8BW500 385 | RFM_change_SF_BW(8,0x09); 386 | break; 387 | case 0x0D: // SF7BW500 388 | RFM_change_SF_BW(7,0x09); 389 | break; 390 | } 391 | #elif defined(AU_915) 392 | switch (Datarate) { 393 | case 0x00: // SF10BW125 394 | RFM_change_SF_BW(10,0x07); 395 | break; 396 | case 0x01: // SF9BW125 397 | RFM_change_SF_BW(9,0x07); 398 | break; 399 | case 0x02: // SF8BW125 400 | RFM_change_SF_BW(8,0x07); 401 | break; 402 | case 0x03: // SF7BW125 403 | RFM_change_SF_BW(7,0x07); 404 | break; 405 | case 0x04: // SF8BW500 406 | RFM_change_SF_BW(8,0x09); 407 | break; 408 | case 0x08: // SF12BW500 409 | RFM_change_SF_BW(12,0x09); 410 | break; 411 | case 0x09: // SF11BW500 412 | RFM_change_SF_BW(11,0x09); 413 | break; 414 | case 0x0A: // SF10BW500 415 | RFM_change_SF_BW(10,0x09); 416 | break; 417 | case 0x0B: // SF9BW500 418 | RFM_change_SF_BW(9,0x09); 419 | break; 420 | case 0x0C: // SF8BW500 421 | RFM_change_SF_BW(8,0x09); 422 | break; 423 | case 0x0D: // SF7BW500 424 | RFM_change_SF_BW(7,0x09); 425 | break; 426 | } 427 | #elif defined(IN_865) 428 | switch (Datarate) { 429 | case 0x00: // SF12BW125 430 | RFM_change_SF_BW(12,0x07); 431 | break; 432 | case 0x01: // SF11BW125 433 | RFM_change_SF_BW(11,0x07); 434 | break; 435 | case 0x02: // SF10BW125 436 | RFM_change_SF_BW(10,0x07); 437 | break; 438 | case 0x03: // SF9BW125 439 | RFM_change_SF_BW(9,0x07); 440 | break; 441 | case 0x04: // SF8BW125 442 | RFM_change_SF_BW(8,0x07); 443 | break; 444 | case 0x05: // SF7BW125 445 | RFM_change_SF_BW(7,0x07); 446 | break; 447 | } 448 | #else //EU_868 or AS_923 or AS_923_2 449 | switch (Datarate) { 450 | case 0x00: // SF12BW125 451 | RFM_change_SF_BW(12,0x07); 452 | break; 453 | case 0x01: // SF11BW125 454 | RFM_change_SF_BW(11,0x07); 455 | break; 456 | case 0x02: // SF10BW125 457 | RFM_change_SF_BW(10,0x07); 458 | break; 459 | case 0x03: // SF9BW125 460 | RFM_change_SF_BW(9,0x07); 461 | break; 462 | case 0x04: // SF8BW125 463 | RFM_change_SF_BW(8,0x07); 464 | break; 465 | case 0x05: // SF7BW125 466 | RFM_change_SF_BW(7,0x07); 467 | break; 468 | case 0x06: // SF7BW250 469 | RFM_change_SF_BW(7,0x08); 470 | break; 471 | } 472 | #endif 473 | } 474 | /* 475 | ***************************************************************************************** 476 | * Description : Function to change the channel of the RFM module. Setting the following 477 | * register: Channel 478 | * 479 | * Arguments : Channel the channel to set 480 | ***************************************************************************************** 481 | */ 482 | static void RFM_Change_Channel(unsigned char Channel) 483 | { 484 | #if defined(AS_923) || defined(AS_923_2) 485 | if (Channel <= 0x08) 486 | for(unsigned char i = 0 ; i < 3 ; ++i) 487 | RFM_Write(RFM_REG_FR_MSB + i, pgm_read_byte(&(LoRa_Frequency[Channel][i]))); 488 | else if (Channel == 0x10) 489 | for(unsigned char i = 0 ; i < 3 ; ++i) 490 | RFM_Write(RFM_REG_FR_MSB + i, pgm_read_byte(&(LoRa_Frequency[0][i]))); 491 | #elif defined(EU_868) 492 | // in EU_868 v1.02 uses same freq for uplink and downlink 493 | if (Channel <= 0x08) 494 | for(unsigned char i = 0 ; i < 3 ; ++i) 495 | RFM_Write(RFM_REG_FR_MSB + i, pgm_read_byte(&(LoRa_Frequency[Channel][i]))); 496 | #elif defined(IN_865) 497 | if( Channel <= 0x03) 498 | for(unsigned char i = 0 ; i < 3 ; ++i) 499 | RFM_Write(RFM_REG_FR_MSB + i, pgm_read_byte(&(LoRa_Frequency[Channel][i]))); 500 | #else //US915 or AU_915 501 | if (Channel <= 0x07) 502 | for(unsigned char i = 0 ; i < 3 ; ++i) 503 | RFM_Write(RFM_REG_FR_MSB + i, pgm_read_byte(&(LoRa_TX_Freq[Channel][i]))); 504 | else if (Channel >= 0x08 && Channel <= 0x0F) 505 | for(unsigned char i = 0 ; i < 3 ; ++i) { 506 | RFM_Write(RFM_REG_FR_MSB + i, pgm_read_byte(&(LoRa_RX_Freq[Channel - 0x08][i]))); 507 | } 508 | #endif 509 | } 510 | 511 | /* 512 | ***************************************************************************************** 513 | * Description: Function used to initialize the RFM module on startup 514 | ***************************************************************************************** 515 | */ 516 | bool RFM_Init() 517 | { 518 | 519 | uint8_t ver = RFM_Read(0x42); 520 | if(ver!=18){ 521 | return 0; 522 | } 523 | //Switch RFM to sleep 524 | //DON'T USE Switch mode function 525 | RFM_Write(RFM_REG_OP_MODE, RFM_MODE_SLEEP); 526 | 527 | //Wait until RFM is in sleep mode 528 | delay(50); 529 | 530 | //Set RFM in LoRa mode 531 | //DON'T USE Switch mode function 532 | RFM_Write(RFM_REG_OP_MODE ,RFM_MODE_LORA); 533 | //Switch RFM to standby 534 | RFM_Switch_Mode(RFM_MODE_STANDBY); 535 | //Set channel to channel 0 536 | RFM_Change_Channel(CH0); 537 | //Set default power to maximun on US915 TODO AS/AU/EU config 538 | //Set the default output pin as PA_BOOST 539 | RFM_Set_Tx_Power(20, PA_BOOST_PIN); 540 | 541 | //Switch LNA boost on 542 | RFM_Write(RFM_REG_LNA,0x23); 543 | 544 | //Set RFM To datarate 0 SF12 BW 125 kHz 545 | RFM_Change_Datarate(0x00); 546 | 547 | //Rx Timeout set to 37 symbols 548 | RFM_Write(RFM_REG_SYM_TIMEOUT_LSB, 0x25); 549 | //RFM_Write(RFM_REG_SYM_TIMEOUT_LSB, 0x05); 550 | 551 | //Preamble length set to 8 symbols 552 | //0x0008 + 4 = 12 553 | RFM_Write(RFM_REG_PREAMBLE_MSB,0x00); 554 | RFM_Write(RFM_REG_PREAMBLE_LSB,0x08); 555 | 556 | //Set LoRa sync word 557 | RFM_Write(RFM_REG_SYNC_WORD, 0x34); 558 | 559 | //Set FIFO pointers 560 | //TX base address 561 | RFM_Write(0x0E,0x80); 562 | //Rx base address 563 | RFM_Write(0x0F,0x00); 564 | return 1; 565 | } 566 | 567 | 568 | void RFM_Set_Tx_Power(int level, int outputPin) 569 | { 570 | if (RFO_PIN == outputPin) { 571 | // RFO 572 | if (level < 0) { 573 | level = 0; 574 | } else if (level > 14) { 575 | level = 14; 576 | } 577 | 578 | RFM_Write(RFM_REG_PA_CONFIG, 0x70 | level); 579 | } else { 580 | // PA BOOST 581 | if (level > 17) { 582 | if (level > 20) { 583 | level = 20; 584 | } 585 | 586 | // subtract 3 from level, so 18 - 20 maps to 15 - 17 587 | level -= 3; 588 | 589 | // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.) 590 | RFM_Write(RFM_REG_PA_DAC, 0x87); 591 | RFM_Set_OCP(140); 592 | } else { 593 | if (level < 2) { 594 | level = 2; 595 | } 596 | //Default value PA_HF/LF or +17dBm 597 | RFM_Write(RFM_REG_PA_DAC, 0x84); 598 | RFM_Set_OCP(100); 599 | } 600 | 601 | RFM_Write(RFM_REG_PA_CONFIG, 0x80 | (level - 2)); //PA Boost mask 602 | } 603 | } 604 | 605 | bool RFM_isRxDone() 606 | { 607 | return (RFM_Read(RFM_REG_IRQ_FLAGS & 0x7f) & IRQ_RX_DONE_MASK) != 0; 608 | } 609 | 610 | void RFM_Set_OCP(uint8_t mA) 611 | { 612 | uint8_t ocpTrim = 27; 613 | 614 | if (mA <= 120) { 615 | ocpTrim = (mA - 45) / 5; 616 | } else if (mA <=240) { 617 | ocpTrim = (mA + 30) / 10; 618 | } 619 | 620 | RFM_Write(RFM_REG_OCP, 0x20 | (0x1F & ocpTrim)); 621 | } 622 | 623 | 624 | /* 625 | ***************************************************************************************** 626 | * Description : Function for sending a package with the RFM 627 | * 628 | * Arguments : *RFM_Tx_Package pointer to buffer with data and counter of data 629 | * *LoRa_Settings pointer to sSettings struct 630 | ***************************************************************************************** 631 | */ 632 | 633 | void RFM_Send_Package(sBuffer *RFM_Tx_Package, sSettings *LoRa_Settings) 634 | { 635 | unsigned char i; 636 | unsigned char RFM_Tx_Location = 0x00; 637 | 638 | //Set RFM in Standby mode 639 | RFM_Switch_Mode(RFM_MODE_STANDBY); 640 | 641 | //Switch Datarate 642 | RFM_Change_Datarate(LoRa_Settings->Datarate_Tx); 643 | 644 | //Switch Channel 645 | RFM_Change_Channel(LoRa_Settings->Channel_Tx); 646 | 647 | //Switch DIO0 to TxDone 648 | if (RFM_pins.DIO0 != -1) 649 | RFM_Write(RFM_REG_DIO_MAPPING1, 0x40); 650 | 651 | //Set IQ to normal values 652 | RFM_Write(RFM_REG_INVERT_IQ,0x27); 653 | RFM_Write(RFM_REG_INVERT_IQ2,0x1D); 654 | 655 | //Set payload length to the right length 656 | RFM_Write(RFM_REG_PAYLOAD_LENGTH,RFM_Tx_Package->Counter); 657 | 658 | //Get location of Tx part of FiFo 659 | RFM_Tx_Location = RFM_Read(0x0E); 660 | 661 | //Set SPI pointer to start of Tx part in FiFo 662 | RFM_Write(RFM_REG_FIFO_ADDR_PTR, RFM_Tx_Location); 663 | 664 | //Write Payload to FiFo 665 | for (i = 0;i < RFM_Tx_Package->Counter; i++) 666 | { 667 | RFM_Write(RFM_REG_FIFO, RFM_Tx_Package->Data[i]); 668 | } 669 | 670 | //Switch RFM to Tx 671 | RFM_Write(RFM_REG_OP_MODE,0x83); 672 | 673 | //Wait for TxDone 674 | if (RFM_pins.DIO0 != -1) 675 | while(digitalRead(RFM_pins.DIO0) == LOW); 676 | else 677 | { 678 | while ((RFM_Read(RFM_REG_IRQ_FLAGS & 0x7f) & IRQ_TX_DONE_MASK) == 0); 679 | } 680 | 681 | //Clear interrupt 682 | RFM_Write(RFM_REG_IRQ_FLAGS,0x08); 683 | } 684 | 685 | /* 686 | ***************************************************************************************** 687 | * Description : Function to switch RFM to single receive mode, used for Class A motes 688 | * 689 | * Arguments : *LoRa_Settings pointer to sSettings struct 690 | * 691 | * Return : message_t Status of the received message 692 | ***************************************************************************************** 693 | */ 694 | 695 | message_t RFM_Single_Receive(sSettings *LoRa_Settings) 696 | { 697 | message_t Message_Status = NO_MESSAGE; 698 | 699 | //Change DIO 0 back to RxDone 700 | if (RFM_pins.DIO0 != -1) 701 | RFM_Write(RFM_REG_DIO_MAPPING1, 0x00); 702 | 703 | //Invert IQ Back 704 | RFM_Write(RFM_REG_INVERT_IQ, 0x67); 705 | RFM_Write(RFM_REG_INVERT_IQ2, 0x19); 706 | 707 | //Change Datarate 708 | RFM_Change_Datarate(LoRa_Settings->Datarate_Rx); 709 | 710 | //Change Channel 711 | RFM_Change_Channel(LoRa_Settings->Channel_Rx); 712 | 713 | //Switch RFM to Single reception 714 | RFM_Switch_Mode(RFM_MODE_RXSINGLE); 715 | 716 | //Wait until RxDone or Timeout 717 | //Wait until timeout or RxDone interrupt 718 | byte RegIrqFlags; 719 | if (RFM_pins.DIO0 != -1) 720 | while((digitalRead(RFM_pins.DIO0) == LOW) && (digitalRead(RFM_pins.DIO1) == LOW)); 721 | else 722 | { 723 | RegIrqFlags = RFM_Read(RFM_REG_IRQ_FLAGS & 0x7f); 724 | while ((RegIrqFlags & (IRQ_RX_DONE_MASK | IRQ_RX_TIMEOUT_MASK)) == 0) 725 | { 726 | RegIrqFlags = RFM_Read(RFM_REG_IRQ_FLAGS & 0x7f); 727 | } 728 | } 729 | 730 | //Check for Timeout 731 | bool isTimeout; 732 | if (RFM_pins.DIO0 != -1) 733 | { 734 | isTimeout = digitalRead(RFM_pins.DIO1) == HIGH; 735 | } 736 | else 737 | { 738 | isTimeout = (RegIrqFlags & IRQ_RX_TIMEOUT_MASK) != 0; 739 | } 740 | if (isTimeout) 741 | { 742 | //Clear interrupt register 743 | RFM_Write(RFM_REG_IRQ_FLAGS,0xE0); 744 | Message_Status = TIMEOUT; 745 | } 746 | 747 | //Check for RxDone 748 | bool isRxDone; 749 | if (RFM_pins.DIO0 != -1) 750 | { 751 | isRxDone = digitalRead(RFM_pins.DIO0) == HIGH; 752 | } 753 | else 754 | { 755 | isRxDone = (RegIrqFlags & IRQ_RX_DONE_MASK) != 0; 756 | } 757 | if(isRxDone) 758 | { 759 | Message_Status = NEW_MESSAGE; 760 | } 761 | 762 | return Message_Status; 763 | } 764 | 765 | /* 766 | ***************************************************************************************** 767 | * Description : Function to switch RFM to continuous receive mode, used for Class C motes 768 | * 769 | * Arguments : *LoRa_Settings pointer to sSettings struct 770 | ***************************************************************************************** 771 | */ 772 | void RFM_Continuous_Receive(sSettings *LoRa_Settings) 773 | { 774 | //Change DIO 0 back to RxDone and DIO 1 to rx timeout 775 | if (RFM_pins.DIO0 != -1) 776 | RFM_Write(RFM_REG_DIO_MAPPING1,0x00); 777 | 778 | //Invert IQ Back 779 | RFM_Write(RFM_REG_INVERT_IQ, 0x67); 780 | RFM_Write(RFM_REG_INVERT_IQ2, 0x19); 781 | 782 | //Change Datarate and channel. 783 | RFM_Change_Datarate(LoRa_Settings->Datarate_Rx); 784 | RFM_Change_Channel(LoRa_Settings->Channel_Rx); 785 | 786 | //Switch to continuous receive 787 | RFM_Switch_Mode(RFM_MODE_RXCONT); 788 | } 789 | 790 | /* 791 | ***************************************************************************************** 792 | * Description : Function to retrieve a message received by the RFM 793 | * 794 | * Arguments : *RFM_Rx_Package pointer to sBuffer struct containing the data received 795 | * and number of bytes received 796 | * 797 | * Return : message_t Status of the received message 798 | ***************************************************************************************** 799 | */ 800 | 801 | message_t RFM_Get_Package(sBuffer *RFM_Rx_Package) 802 | { 803 | unsigned char i; 804 | unsigned char RFM_Interrupts = 0x00; 805 | unsigned char RFM_Package_Location = 0x00; 806 | message_t Message_Status; 807 | 808 | //Get interrupt register 809 | RFM_Interrupts = RFM_Read(0x12); 810 | 811 | 812 | if((RFM_Interrupts & 0x40)){ //IRQ_RX_DONE_MASK 813 | if((RFM_Interrupts & 0x20) != 0x20) //Check CRC 814 | { 815 | Message_Status = CRC_OK; 816 | } 817 | else 818 | { 819 | Message_Status = WRONG_MESSAGE; 820 | } 821 | } 822 | RFM_Package_Location = RFM_Read(0x10); /*Read start position of received package*/ 823 | RFM_Rx_Package->Counter = RFM_Read(0x13); /*Read length of received package*/ 824 | 825 | RFM_Write(RFM_REG_FIFO_ADDR_PTR,RFM_Package_Location); /*Set SPI pointer to start of package*/ 826 | 827 | for (i = 0x00; i < RFM_Rx_Package->Counter; i++) 828 | { 829 | RFM_Rx_Package->Data[i] = RFM_Read(RFM_REG_FIFO); 830 | } 831 | 832 | //Clear interrupt register 833 | RFM_Write(RFM_REG_IRQ_FLAGS,RFM_Interrupts); 834 | 835 | return Message_Status; 836 | } 837 | 838 | /* 839 | ***************************************************************************************** 840 | * Description : Function that writes a register from the RFM 841 | * 842 | * Arguments : RFM_Address Address of register to be written 843 | * RFM_Data Data to be written 844 | ***************************************************************************************** 845 | */ 846 | 847 | void RFM_Write(unsigned char RFM_Address, unsigned char RFM_Data) 848 | { 849 | // br: SPI Transfer Debug 850 | #ifdef DEBUG 851 | Serial.print("SPI Write ADDR: "); 852 | Serial.print(RFM_Address, HEX); 853 | Serial.print(" DATA: "); 854 | Serial.println(RFM_Data, HEX); 855 | #endif 856 | 857 | //Add transactions in Read and Write methods 858 | SPI.beginTransaction(SPISettings(4000000,MSBFIRST,SPI_MODE0)); 859 | 860 | //Set NSS pin Low to start communication 861 | digitalWrite(RFM_pins.CS,LOW); 862 | 863 | //Send Address with MSB 1 to make it a writ command 864 | SPI.transfer(RFM_Address | 0x80); 865 | //Send Data 866 | SPI.transfer(RFM_Data); 867 | 868 | //Set NSS pin High to end communication 869 | digitalWrite(RFM_pins.CS,HIGH); 870 | 871 | //End the transaction so that other hardware can use the bus 872 | SPI.endTransaction(); 873 | } 874 | 875 | /* 876 | ***************************************************************************************** 877 | * Description : Function to change the operation mode of the RFM. Switching mode and wait 878 | * for mode ready flag 879 | * DO NOT USE FOR SLEEP 880 | * 881 | * Arguments : Mode the mode to set 882 | ***************************************************************************************** 883 | */ 884 | void RFM_Switch_Mode(unsigned char Mode) 885 | { 886 | Mode = Mode | 0x80; //Set high bit for LoRa mode 887 | 888 | //Switch mode on RFM module 889 | RFM_Write(RFM_REG_OP_MODE,Mode); 890 | } 891 | 892 | /* 893 | ***************************************************************************************** 894 | * Description : Function to retrieve value of the last received packet rssi register 895 | ***************************************************************************************** 896 | */ 897 | unsigned char RFM_Get_Rssi() 898 | { 899 | return RFM_Read(RFM_REG_LAST_RSSI); 900 | } 901 | --------------------------------------------------------------------------------