├── LICENSE ├── README.md ├── images ├── ack.jpg ├── images.txt ├── lcm.jpg ├── man.jpg ├── menu.jpg ├── nack.jpg ├── otaa.jpg ├── set.jpg ├── ssv-1.jpg └── ssv-2.jpg └── networktester ├── bmp_map.c ├── logo320240.c ├── networktester.ino └── src ├── LoRaWan.cpp └── LoRaWan.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Björn Amann 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # M5Stack-LoRaWAN-Network-Tester 2 | 3 | A LoRaWAN Network Tester based on the M5Stack, compatible with TTN (The Things Network) 4 | This version is for the LoRaWAN module (EOL). 5 | 6 | [Version for LoRa 868 Module] 7 | 8 | [Version for COM.LoRaWAN Module] 9 | 10 | ## Setup 11 | The tester is designed to work with the following hardware: 12 | - M5Core (Basic, Gray or Fire) 13 | - M5Go Base (optional) 14 | - M5Stack GPS Module or Mini GPS/BDS Unit (optional) 15 | - M5Stack LoRaWAN Module (Firmware 2.1.19 mandatory for OTAA) 16 | 17 | #### Required Libraries! 18 | - [M5Stack] 19 | - [TinyGPSPlus] 20 | - [NeoPixelBus] 21 | - [M5_UI] - this Fork enables TTN Mapper like colours for RSSI values in the progressbar 22 | - Seeeduino LoRaWan (Already included, patched and bugfixed) 23 | 24 | 25 | #### Installation and Configuration 26 | Upload this sketch to your M5 using the Arduino IDE. M5Stack Fire users have to disable PSRAM, because it will interfer with UART2. 27 | UART2 with GPIO 16 and 17 willbe used for the GPS module and UART1 with GPIO 2 and 5 for the LoRaWAN module. Last can be changed in LoRaWan.cpp The UART Port on the LoRaWAN module has to be changed (solderpads) to use this ports. 28 | 29 | Serial ports declaration 30 | 31 | - LoRaWan.h 32 | - #define SerialLoRa Serial1 33 | - To be used with GPS module and changed UART port on LoRaWAN module 34 | 35 | - #define SerialLoRa Serial2 36 | - To be used without GPS module and unchanged UART port on LoRaWAN module 37 | 38 | - LoRaWan.cpp 39 | - SerialLoRa.begin(9600, SERIAL_8N1, 2, 5); 40 | - To be used with GPS module and changed UART port on LoRaWAN module 41 | 42 | - SerialLoRa.begin(9600, SERIAL_8N1, 16, 17); 43 | - To be used without GPS module and unchanged UART port on LoRaWAN module 44 | 45 | By commenting out #define M5go it is possible to disable the M5GO Base. This will disable all NeoPixel related code and features. 46 | By commenting out #define M5gps ist is possible to disable the M5GPS module. This will disable all GPS related code and features. 47 | 48 | Replace the original files LoraWan.h and LoRaWan.cpp delivered by the M5Stack Library under M5Stack/src with the ones in the src directory. Delete the src directory afterwards or move it. 49 | 50 | Change the your TTN keys in initlora() funtion of the networktester.ino file. They are filled in as is from the TTN console. If you want yo use OTAA you have to register a second device for your application. 51 | 52 | Payload Decoder for TTN (also compatible with TTN Mapper integration): 53 | 54 | ``` 55 | function Decoder(b, port) { 56 | 57 | var lat = (b[0] | b[1]<<8 | b[2]<<16 | (b[2] & 0x80 ? 0xFF<<24 : 0)) / 10000; 58 | var lon = (b[3] | b[4]<<8 | b[5]<<16 | (b[5] & 0x80 ? 0xFF<<24 : 0)) / 10000; 59 | var alt = (b[6] | b[7]<<8 | (b[7] & 0x80 ? 0xFF<<16 : 0)) / 100; 60 | var hdop = b[8] / 10; 61 | 62 | return { 63 | 64 | latitude: lat, 65 | longitude: lon, 66 | altitude: alt, 67 | hdop: hdop 68 | 69 | }; 70 | } 71 | ``` 72 | 73 | ## Instructions for Use 74 | 75 | #### Menu 76 | 77 | On boot you will be presented with the "Boot-Logo" followed by the first working mode. At the moment the tester has 7 modes to select: 78 | - [NACK](#nack) 79 | - [ACK](#ack) 80 | - [MAN](#man) 81 | - [LCM](#lcm) 82 | - [SSV](#ssv) 83 | - [OTAA](#otaa) 84 | - [SET](#set) 85 | 86 | You can move between menu items by pushing the button A. 87 | 88 | ![Menu Image](https://github.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/blob/master/images/menu.jpg "Fig 1. Menu") 89 | 90 | #### NACK 91 | #### (No Acknowladge) 92 | "NACK" is a mode that utlises the current device [settings](#set) to perform periodic transmissions. "NACK" mode is great for use with TTN Mapper. 93 | 94 | ![NACK Image](https://github.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/blob/master/images/nack.jpg "Fig 2. NACK") 95 | 96 | Pushing button B will let you cycle through each spreadfactor. By pushig button C the display and LEDs will be turned off. Pushing button C again will turn them on. 97 | 98 | #### ACK 99 | #### (Acknowladge) 100 | "ACK" will perform the same test as NACK but it will request an ACK for every transmission. The RSSI and SNR values of the received packet will be shown on the display. Pushing button B will let you cycle through each spreadfactor. By pushig button C the display and LEDs will be turned off. Pushing button C again will turn them on. 101 | 102 | ![ACK Image](https://github.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/blob/master/images/ack.jpg "Fig 3. ACK") 103 | 104 | #### MAN 105 | #### (Manual) 106 | "MAN" will send a LoRaWAN packet with ACK by pushing button C. Pushing button B will let you cycle through each spreadfactor. 107 | 108 | ![MAN Image](https://github.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/blob/master/images/man.jpg "Fig 4. MAN") 109 | 110 | #### LCM 111 | #### (LinkCheckMode) 112 | "LCM" is a mode that will trigger a LinkCheckRequest. The TTN backend will report back the number of gateways which received the request. Pushing button B will let you cycle through each spreadfactor. The request is triggered by button C. 113 | 114 | ![LCM Image](https://github.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/blob/master/images/lcm.jpg "Fig 5. LCM") 115 | 116 | #### SSV 117 | #### (SiteSurvey) 118 | "SSV" is supposed as mode for testing a location. During SSV mode the DutyCycle check will be disabled an the Node will send a LinkCheckRequest for every spreadfactor from SF7 to SF12. After the test the node will show you on which datarates a ACK was received back. The data is also stored on the SD card in GeoJSON format an could be analyzed with [geojson.io] 119 | 120 | ![SSV Image](https://github.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/blob/master/images/ssv-1.jpg "Fig 6. SSV running") 121 | ![SSV Image](https://github.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/blob/master/images/ssv-2.jpg "Fig 7. SSV results") 122 | 123 | #### OTAA 124 | #### (OverTheAirActivation) 125 | "OTAA" enables the tester to perform OTAA-Joins. By selecting Join the tester will try to join the TTN Network. After an successful the the tester will start with periodic transmissions. You have the choice between transmission with or without ACK. The RSSI and SNR values of the received packet will be shown on the display. If there is no valid GPS fix, a packet can by manually send by pushing button B. 126 | Firmware version 2.1.19 of the LoRaWAN module is mandatory for OTAA to work. 127 | 128 | ![OTAA Image](https://github.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/blob/master/images/otaa.jpg "Fig 7. OTAA") 129 | 130 | #### SET 131 | #### (Settings) 132 | 133 | "SET" allows to change the transmission intervall in NACK or ACK mode. Possible settings are 15/30/45/60/120 seconds. Pressing button C will active the powersaving mode. The node will go to deep sleep and wakes up according to the transmission intervall. Sleep mode can only be stopped by resetting the devicde. 134 | 135 | ![SET Image](https://github.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/blob/master/images/set.jpg "Fig 7. SET") 136 | 137 | ## Notes 138 | - The DutyCycle check ist activated except for the SSV mode. 139 | - If you have a valid GPS fix the GPS track will be written to the SD card as a GPX file. 140 | - Periodic transmission will only work with a valid GPS fix and an GPS age below 2 seconds 141 | 142 | ## Changelog 143 | 144 | - 17.02.2021 145 | - Replace drawBitmap by pushImage for corrected colours 146 | 147 | - 19.01.2021 148 | - Add Beep to ACK Mode 149 | - reset counters if no ACK received 150 | 151 | - 30.12.2020 152 | - Reset RSSI, SNR and No of GWs values on change of mode 153 | 154 | - 13.03.2020 155 | - Enable ESP32 Deep Sleep mode 156 | - Allign packet counter to TTN console 157 | 158 | - 12.03.2020 159 | - Fix OTAA join, caused by Duty Cycle limitation 160 | - Improve status information 161 | - Add the possability to use the tester without M5GO Base and/or GPS module. 162 | 163 | - 09.03.2020 164 | - Add LowPower mode for LoRaWAN module 165 | - improve SF selection, ADR, and Duty Cycle check 166 | - inform about Duty Cycle restriction in NACK and LCR mode 167 | 168 | - 08.03.2020 169 | - Enforce Duty Cycle check 170 | - Fix SF selection and ADR for ACK and MAN mode 171 | 172 | - 05.03.2020 173 | - Fix SF selection and ADR for NACK mode 174 | - Add OTAA support 175 | 176 | - 04.03.2020 177 | - Move gpsupdate task to core 0 178 | 179 | - 03.03.2020 180 | - improve gspupdate task 181 | 182 | - 28.02.2020 183 | - fix typos 184 | 185 | - 27.02.2020 186 | - switch to M5.Power library 187 | 188 | ## ToDo 189 | - create tasks for M5UI 190 | - improve powersave features (GPS module) 191 | 192 | 193 | [M5Stack]: https://github.com/m5stack/M5Stack 194 | [TinyGPSPlus]: https://github.com/mikalhart/TinyGPSPlus 195 | [NeoPixelBus]: https://github.com/Makuna/NeoPixelBus 196 | [M5_UI]: https://github.com/dsiberia9s/M5_UI 197 | [geojson.io]: http://geojson.io/ 198 | [Version for LoRa 868 Module]: https://github.com/Bjoerns-TB/M5Stack-LoRa-868-Network-Tester 199 | [M5_UI]: https://github.com/Bjoerns-TB/M5_UI/tree/TTN-Mapper-Colours-Progressbar 200 | [Version for COM.LoRaWAN Module]: https://github.com/Bjoerns-TB/M5Stack-COM-LoRaWAN-Network-Tester 201 | 202 | -------------------------------------------------------------------------------- /images/ack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/c38d426f92ad1a412129e75e43c514f137cd0b97/images/ack.jpg -------------------------------------------------------------------------------- /images/images.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /images/lcm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/c38d426f92ad1a412129e75e43c514f137cd0b97/images/lcm.jpg -------------------------------------------------------------------------------- /images/man.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/c38d426f92ad1a412129e75e43c514f137cd0b97/images/man.jpg -------------------------------------------------------------------------------- /images/menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/c38d426f92ad1a412129e75e43c514f137cd0b97/images/menu.jpg -------------------------------------------------------------------------------- /images/nack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/c38d426f92ad1a412129e75e43c514f137cd0b97/images/nack.jpg -------------------------------------------------------------------------------- /images/otaa.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/c38d426f92ad1a412129e75e43c514f137cd0b97/images/otaa.jpg -------------------------------------------------------------------------------- /images/set.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/c38d426f92ad1a412129e75e43c514f137cd0b97/images/set.jpg -------------------------------------------------------------------------------- /images/ssv-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/c38d426f92ad1a412129e75e43c514f137cd0b97/images/ssv-1.jpg -------------------------------------------------------------------------------- /images/ssv-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bjoerns-TB/M5Stack-LoRaWAN-Network-Tester/c38d426f92ad1a412129e75e43c514f137cd0b97/images/ssv-2.jpg -------------------------------------------------------------------------------- /networktester/networktester.ino: -------------------------------------------------------------------------------- 1 | #include // https://github.com/m5stack/M5Stack 2 | #include // https://github.com/Bjoerns-TB/M5_UI/tree/TTN-Mapper-Colours-Progressbar 3 | #include 4 | #include "soc/timer_group_struct.h" 5 | #include "soc/timer_group_reg.h" 6 | 7 | #define M5go 8 | #define M5gps 9 | 10 | #ifdef M5go 11 | #include // https://github.com/Makuna/NeoPixelBus 12 | #endif 13 | 14 | #ifdef M5gps 15 | #include // https://github.com/mikalhart/TinyGPSPlus 16 | #endif 17 | 18 | //Task 19 | TaskHandle_t TaskGPS; 20 | TaskHandle_t TaskPixel; 21 | 22 | //Image 23 | extern const unsigned char gImage_logoM5[]; 24 | 25 | #ifdef M5go 26 | //NeoPixel 27 | const uint16_t PixelCount = 10; 28 | const uint8_t PixelPin = 15; 29 | NeoPixelBrightnessBus strip(PixelCount, PixelPin); 30 | RgbColor red(128, 0, 0); 31 | RgbColor green(0, 128, 0); 32 | RgbColor blue(0, 0, 128); 33 | RgbColor lightblue(0, 95, 128); 34 | RgbColor yellow(128, 128, 0); 35 | RgbColor orange(128, 64, 0); 36 | RgbColor off(0, 0, 0); 37 | #endif 38 | 39 | #ifdef M5gps 40 | //GPS 41 | static const uint32_t GPSBaud = 9600; 42 | TinyGPSPlus gps; 43 | HardwareSerial serialgps(2); 44 | #endif 45 | float latitude, longitude, hdop, alt, hdop2; 46 | int sats; 47 | 48 | 49 | //LoRa 50 | int isf = 0; 51 | int oldisf = 0; 52 | const char *dr[6] = {"DR5", "DR4", "DR3", "DR2", "DR1", "DR0"}; 53 | const char *sf[6] = {"SF7", "SF8", "SF9", "SF10", "SF11", "SF12"}; 54 | RTC_DATA_ATTR int iwm = 0; 55 | const char *workmode[7] = {"NACK", "ACK", "MAN", "LCM", "SSV", "OTAA", "SET"}; 56 | char buffer[256]; 57 | short length; 58 | short rssi; 59 | float snr; 60 | char charsnr[5]; 61 | short gwcnt; 62 | byte coords[9]; 63 | byte ncoords[1]; 64 | long sentMillis = 0; 65 | long currentMillis = 0; 66 | RTC_DATA_ATTR int iiv = 0; 67 | long interval[5] = {15000, 30000, 45000, 60000, 120000}; 68 | const char *ttext[5] = {"15s", "30s", "45s", "60s", "120s"}; 69 | RTC_DATA_ATTR int cnt = -1; 70 | String txcnt; 71 | int otaa = 0; 72 | int otaaack = 0; 73 | 74 | //Battery 75 | int8_t BattLevel = 0; 76 | #define FULL ( 3) 77 | 78 | //SDCard 79 | char filename[] = "/"; 80 | bool cardin = false; 81 | bool sdwrite = false; 82 | File dataFile; 83 | 84 | //GPX 85 | int year; 86 | byte month, day, hour, minute, second; 87 | char filename1[20]; 88 | char date1[22]; 89 | char filepath[20]; 90 | 91 | //SSV 92 | char filename2[20]; 93 | char date2[22]; 94 | char filepath2[20]; 95 | bool firstssv = false; 96 | bool lastssv = false; 97 | String ssvresult = "DR "; 98 | 99 | //M5Stack 100 | bool dim = false; 101 | RTC_DATA_ATTR bool powersave = false; 102 | 103 | /* RootVar's for UI elements (note: not edit manually) */ 104 | String UIInputbox_6nssds = ""; //No GWs for LCR 105 | String UITextbox_vimqus = "SF7"; //SpreadingFactor (B2) 106 | String UITextbox_eq79hh46 = "NACK"; //Workmode (B1) 107 | String UITextbox_67ofwdh = "Dim"; //Dimming (B3) 108 | String UIProgressbar_eymzer = "0"; //Progressbar RSSI 109 | String UITextbox_859t1hi = "-130"; //RSSI 110 | String UIInputbox_awnh87 = "inactive";//Status 111 | String UITextbox_4t0l0bn = "0"; //Stattelites 112 | String UITextbox_q7sl3uo = "0"; //HDOP 113 | String UITextbox_403ohip = "0"; //Battery Level 114 | String UITextbox_olwwlae = "-20.00"; //SNR 115 | String UITextbox_7mnuudb = "SNR"; //SNR 116 | 117 | /* Function for layer default: */ 118 | void LayerFunction_default(String* rootVar) { 119 | /* UI Elements */ 120 | UIInputbox(160, 58, 150, "default", "No of GWs", 0, &UIInputbox_6nssds); 121 | UITextbox(144, 214, 50, 20, 0x0000, "default", &UITextbox_vimqus); 122 | UITextbox(44, 215, 50, 20, 0x0000, "default", &UITextbox_eq79hh46); 123 | UITextbox(227, 215, 50, 20, 0x0000, "default", &UITextbox_67ofwdh); 124 | UIProgressbar(10, 144, 300, "default", "RSSI, dB", &UIProgressbar_eymzer); 125 | UITextbox(124, 142, 50, 20, 0x0000, "default", &UITextbox_859t1hi); 126 | UIInputbox(5, 58, 150, "default", "Status", 0, &UIInputbox_awnh87); 127 | UITextbox(40, 11, 25, 20, 0x0000, "default", &UITextbox_4t0l0bn); 128 | UITextbox(100, 11, 60, 20, 0x0000, "default", &UITextbox_q7sl3uo); 129 | UITextbox(270, 11, 50, 20, 0x0000, "default", &UITextbox_403ohip); 130 | UITextbox(249, 142, 70, 20, 0x0000, "default", &UITextbox_olwwlae); 131 | UITextbox(200, 142, 40, 20, 0x0000, "default", &UITextbox_7mnuudb); 132 | 133 | /* To open this layer use: */ 134 | UILayer("default"); 135 | } 136 | 137 | #ifdef M5gps 138 | static void gpsupdate(void * pcParameters) 139 | { 140 | for (;;) { 141 | unsigned long start = millis(); 142 | do { 143 | while (serialgps.available()) { 144 | gps.encode(serialgps.read()); 145 | } 146 | } while (millis() - start < 1000); 147 | TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; 148 | TIMERG0.wdt_feed = 1; 149 | TIMERG0.wdt_wprotect = 0; 150 | } 151 | } 152 | #endif 153 | 154 | #ifdef M5go 155 | static void pixelupdate(void * pcParameters) 156 | { 157 | for (;;) { 158 | 159 | #ifdef M5gps 160 | //Change NeoPixel 4 161 | if (gps.satellites.value() < 3) { 162 | strip.SetPixelColor(4, red); 163 | } 164 | else if (gps.satellites.value() < 6) { 165 | strip.SetPixelColor(4, yellow); 166 | } 167 | else { 168 | strip.SetPixelColor(4, green); 169 | } 170 | 171 | //Change NeoPixel 0 172 | if (gps.hdop.value() < 500) { 173 | strip.SetPixelColor(0, green); 174 | } 175 | else if (gps.hdop.value() < 1000) { 176 | strip.SetPixelColor(0, yellow); 177 | } 178 | else { 179 | strip.SetPixelColor(0, red); 180 | } 181 | 182 | //Change NeoPixel 2 183 | if (gps.location.isValid() == false) { 184 | strip.SetPixelColor(2, red); 185 | } 186 | else if (gps.location.isValid() && gps.location.age() > 2000) { 187 | strip.SetPixelColor(2, red); 188 | } 189 | else if (gps.location.isValid() == true) { 190 | strip.SetPixelColor(2, green); 191 | } 192 | else { 193 | strip.SetPixelColor(2, green); 194 | } 195 | #endif 196 | 197 | //Change NeoPixel 7 for RX status 198 | if ((iwm == 0) || (iwm == 4) || (iwm == 6)) { 199 | strip.SetPixelColor(7, off); 200 | } 201 | 202 | strip.Show(); 203 | 204 | TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; 205 | TIMERG0.wdt_feed = 1; 206 | TIMERG0.wdt_wprotect = 0; 207 | smartDelay(500); 208 | } 209 | } 210 | #endif 211 | 212 | //Delay without delay 213 | static void smartDelay(unsigned long ms) 214 | { 215 | unsigned long start = millis(); 216 | do 217 | {} while (millis() - start < ms); 218 | } 219 | 220 | #ifdef M5gps 221 | //Write GPS-Data into variables 222 | void gpsdata() { 223 | year = gps.date.year(); 224 | month = gps.date.month(); 225 | day = gps.date.day(); 226 | hour = gps.time.hour(); 227 | minute = gps.time.minute(); 228 | second = gps.time.second(); 229 | latitude = gps.location.lat(); 230 | longitude = gps.location.lng(); 231 | alt = gps.altitude.meters(); 232 | hdop = gps.hdop.value(); 233 | } 234 | #endif 235 | 236 | #ifdef M5gps 237 | //Initialize GPX-Track to SD-Card 238 | void gpxinit() { 239 | if (cardin == true && gps.location.isValid() == true) { 240 | sdwrite = true; 241 | sprintf(filename1, "/%02d-%02d-%02d", day, month, year - 2000); 242 | sprintf(filepath, "/%02d-%02d-%02d/%02d-%02d%s", day, month, year - 2000, hour, minute, ".GPX"); 243 | 244 | SD.mkdir(filename1); 245 | if (!SD.exists(filepath)) { 246 | dataFile = SD.open(filepath, FILE_WRITE); 247 | dataFile.print(F( 248 | "\r\n" 249 | "\r\n" 252 | "\t\r\n\r\n")); 253 | dataFile.print(F("\r\n\r\n\r\n")); 254 | dataFile.close(); 255 | } 256 | } 257 | } 258 | 259 | //Write data to GPX-File 260 | void writegpx() { 261 | if (gps.location.isValid() == true) { 262 | gpsdata(); 263 | sprintf(date1, "%4d-%02d-%02dT%02d:%02d:%02dZ", year, month, day, hour, minute, second); 264 | dataFile = SD.open(filepath, FILE_WRITE); 265 | unsigned long filesize = dataFile.size(); 266 | filesize -= 27; 267 | dataFile.seek(filesize); 268 | dataFile.print(F("")); 273 | dataFile.print(F("")); 276 | dataFile.print(F("")); 277 | dataFile.print(alt, 1); 278 | dataFile.print(F("\r\n")); 279 | dataFile.print(hdop2, 1); 280 | dataFile.println(F("\r\n")); 281 | dataFile.print(F("\r\n\r\n\r\n")); 282 | dataFile.close(); 283 | } 284 | } 285 | #endif 286 | 287 | //Settings for LoRaWAN 288 | void initlora() { 289 | 290 | lora.init(); 291 | 292 | if (powersave == false) { 293 | delay(1000); 294 | 295 | memset(buffer, 0, 256); 296 | lora.getVersion(buffer, 256, 1); 297 | Serial.print(buffer); 298 | 299 | memset(buffer, 0, 256); 300 | lora.getId(buffer, 256, 1); 301 | Serial.print(buffer); 302 | 303 | // void setId(char *DevAddr, char *DevEUI, char *AppEUI); 304 | lora.setId("yourdeviceaddress", NULL, NULL); //for ABP 305 | //lora.setId("ABP-yourdeviceaddress", "OTAA-yourdeviceEUI", "OTAA-yourAppEUI"); //for OTAA 306 | 307 | // setKey(char *NwkSKey, char *AppSKey, char *AppKey); 308 | lora.setKey("yourNetworkSKey", "yourappSKey", NULL); //for ABP 309 | //lora.setKey("ABP-yourNetworkSKey", "ABP-yourappSKey", "OTAAyourAppKey); //for OTAA 310 | 311 | lora.setDeviceMode(LWABP); 312 | lora.setDataRate(DR5, EU868); 313 | 314 | lora.setChannel(0, 868.1); 315 | lora.setChannel(1, 868.3); 316 | lora.setChannel(2, 868.5); 317 | lora.setChannel(3, 867.1); 318 | lora.setChannel(4, 867.3); 319 | lora.setChannel(5, 867.5); 320 | lora.setChannel(6, 867.7); 321 | lora.setChannel(7, 867.9); 322 | 323 | lora.setReceiveWindowFirst(0, 868.1); 324 | lora.setReceiveWindowSecond(869.525, DR3); 325 | 326 | lora.setPower(14); 327 | lora.setPort(1); 328 | lora.setAdaptiveDataRate(false); 329 | lora.setDutyCycle(true); 330 | lora.setDeviceLowPower(); 331 | } 332 | } 333 | 334 | //Settings for LoRaWAN ABP 335 | void initloraabp() { 336 | lora.sendDevicePing(); 337 | lora.setDeviceMode(LWABP); 338 | lora.setAdaptiveDataRate(false); 339 | lora.setDutyCycle(true); 340 | otaa = 0; 341 | cnt = -1; 342 | lora.setDeviceLowPower(); 343 | } 344 | 345 | //Settings for LoRaWAN OTAA 346 | void initloraotaa() { 347 | lora.sendDevicePing(); 348 | lora.setDeviceMode(LWOTAA); 349 | lora.setAdaptiveDataRate(true); 350 | lora.setDutyCycle(false); 351 | UISet(&UIInputbox_awnh87, "Joining"); 352 | while (!lora.setOTAAJoin(JOIN, 10)); 353 | UISet(&UIInputbox_awnh87, "Joined"); 354 | lora.setDutyCycle(true); 355 | otaa = 1; 356 | cnt = -1; 357 | lora.setDeviceLowPower(); 358 | } 359 | 360 | //Send data using LoRaWAN 361 | void sendobject() { 362 | bool result = false; 363 | 364 | int32_t lat = latitude * 10000; 365 | int32_t lon = longitude * 10000; 366 | int16_t altitude = alt * 100; 367 | int8_t hdopGPS = hdop / 10; 368 | 369 | coords[0] = lat; 370 | coords[1] = lat >> 8; 371 | coords[2] = lat >> 16; 372 | 373 | coords[3] = lon; 374 | coords[4] = lon >> 8; 375 | coords[5] = lon >> 16; 376 | 377 | coords[6] = altitude; 378 | coords[7] = altitude >> 8; 379 | 380 | coords[8] = hdopGPS; 381 | 382 | sentMillis = millis(); 383 | 384 | #ifndef M5gps 385 | if (iwm == 0) { 386 | #else 387 | if (iwm == 0 && gps.location.isValid() == true && gps.location.age() < 2000) { 388 | #endif 389 | 390 | UISet(&UIInputbox_awnh87, "Sending"); 391 | lora.sendDevicePing(); 392 | 393 | if (oldisf != isf) { 394 | if (isf == 0) { 395 | lora.setDataRate(DR5, EU868); 396 | lora.setAdaptiveDataRate(false); 397 | lora.setDutyCycle(true); 398 | oldisf = isf; 399 | cnt = -1; 400 | } else if (isf == 1) { 401 | lora.setDataRate(DR4, EU868); 402 | lora.setAdaptiveDataRate(false); 403 | lora.setDutyCycle(true); 404 | oldisf = isf; 405 | cnt = -1; 406 | } else if (isf == 2) { 407 | lora.setDataRate(DR3, EU868); 408 | lora.setAdaptiveDataRate(false); 409 | lora.setDutyCycle(true); 410 | oldisf = isf; 411 | cnt = -1; 412 | } else if (isf == 3) { 413 | lora.setDataRate(DR2, EU868); 414 | lora.setAdaptiveDataRate(false); 415 | lora.setDutyCycle(true); 416 | oldisf = isf; 417 | cnt = -1; 418 | } else if (isf == 4) { 419 | lora.setDataRate(DR1, EU868); 420 | lora.setAdaptiveDataRate(false); 421 | lora.setDutyCycle(true); 422 | oldisf = isf; 423 | cnt = -1; 424 | } else if (isf == 5) { 425 | lora.setDataRate(DR0, EU868); 426 | lora.setAdaptiveDataRate(false); 427 | lora.setDutyCycle(true); 428 | oldisf = isf; 429 | cnt = -1; 430 | } 431 | } 432 | 433 | #ifdef M5gps 434 | result = lora.transferPacket(coords, sizeof(coords), 5); 435 | #else 436 | result = lora.transferPacket(ncoords, sizeof(ncoords), 5); 437 | #endif 438 | 439 | if (result == true) { 440 | cnt++; 441 | txcnt = String("Sent " + String(cnt)); 442 | UISet(&UIInputbox_awnh87, txcnt); 443 | } else if (lora.dutycycle == true) { 444 | UISet(&UIInputbox_awnh87, "DutyCycle"); 445 | } else { 446 | UISet(&UIInputbox_awnh87, "Error"); 447 | } 448 | #ifndef M5gps 449 | } else if ((iwm == 1) || (iwm == 2)) { 450 | #else 451 | } else if (((iwm == 1) && gps.location.isValid() == true && gps.location.age() < 2000) || (iwm == 2)) { 452 | #endif 453 | 454 | UISet(&UIInputbox_awnh87, "ACK"); 455 | lora.sendDevicePing(); 456 | 457 | if (isf == 0) { 458 | lora.setDataRate(DR5, EU868); 459 | oldisf = 6; 460 | cnt = -1; 461 | } else if (isf == 1) { 462 | lora.setDataRate(DR4, EU868); 463 | oldisf = 6; 464 | cnt = -1; 465 | } else if (isf == 2) { 466 | lora.setDataRate(DR3, EU868); 467 | oldisf = 6; 468 | cnt = -1; 469 | } else if (isf == 3) { 470 | lora.setDataRate(DR2, EU868); 471 | oldisf = 6; 472 | cnt = -1; 473 | } else if (isf == 4) { 474 | lora.setDataRate(DR1, EU868); 475 | oldisf = 6; 476 | cnt = -1; 477 | } else if (isf == 5) { 478 | lora.setDataRate(DR0, EU868); 479 | oldisf = 6; 480 | cnt = -1; 481 | } 482 | 483 | #ifdef M5gps 484 | result = lora.transferPacketWithConfirmed(coords, sizeof(coords), 5); 485 | #else 486 | result = lora.transferPacketWithConfirmed(ncoords, sizeof(ncoords), 5); 487 | #endif 488 | 489 | 490 | if (result == true) { 491 | cnt++; 492 | 493 | UISet(&UIInputbox_awnh87, "ACK OK"); 494 | if (iwm == 1){ 495 | M5.Speaker.beep(); 496 | } 497 | 498 | short length; 499 | short rssi; 500 | float snr; 501 | char charsnr[5]; 502 | short gwcnt; 503 | 504 | memset(buffer, 0, 256); 505 | length = lora.receivePacket(buffer, 256, &rssi, &snr, &gwcnt); 506 | 507 | dtostrf(snr, 5, 1, charsnr); 508 | 509 | UISet(&UIProgressbar_eymzer, (rssi + 130) * 2); 510 | UISet(&UITextbox_859t1hi, rssi); 511 | UISet(&UITextbox_olwwlae, charsnr); 512 | 513 | #ifdef M5go 514 | if (rssi < -120) { 515 | strip.SetPixelColor(7, blue); 516 | } else if (rssi < -115) { 517 | strip.SetPixelColor(7, lightblue); 518 | } else if (rssi < -110) { 519 | strip.SetPixelColor(7, green); 520 | } else if (rssi < -105) { 521 | strip.SetPixelColor(7, yellow); 522 | } else if (rssi < -100) { 523 | strip.SetPixelColor(7, orange); 524 | } else { 525 | strip.SetPixelColor(7, red); 526 | } 527 | #endif 528 | 529 | } else { 530 | UISet(&UIInputbox_awnh87, "Error"); 531 | UISet(&UITextbox_859t1hi, "-130"); 532 | UISet(&UITextbox_olwwlae, "-20.0"); 533 | UISet(&UIProgressbar_eymzer, 0); 534 | } 535 | } else if (iwm == 3) { 536 | 537 | UISet(&UIInputbox_awnh87, "LCR"); 538 | lora.sendDevicePing(); 539 | 540 | if (oldisf != isf) { 541 | if (isf == 0) { 542 | lora.setDataRate(DR5, EU868); 543 | lora.setAdaptiveDataRate(false); 544 | lora.setDutyCycle(true); 545 | oldisf = isf; 546 | cnt = -1; 547 | } else if (isf == 1) { 548 | lora.setDataRate(DR4, EU868); 549 | lora.setAdaptiveDataRate(false); 550 | lora.setDutyCycle(true); 551 | oldisf = isf; 552 | cnt = -1; 553 | } else if (isf == 2) { 554 | lora.setDataRate(DR3, EU868); 555 | lora.setAdaptiveDataRate(false); 556 | lora.setDutyCycle(true); 557 | oldisf = isf; 558 | cnt = -1; 559 | } else if (isf == 3) { 560 | lora.setDataRate(DR2, EU868); 561 | lora.setAdaptiveDataRate(false); 562 | lora.setDutyCycle(true); 563 | oldisf = isf; 564 | cnt = -1; 565 | } else if (isf == 4) { 566 | lora.setDataRate(DR1, EU868); 567 | lora.setAdaptiveDataRate(false); 568 | lora.setDutyCycle(true); 569 | oldisf = isf; 570 | cnt = -1; 571 | } else if (isf == 5) { 572 | lora.setDataRate(DR0, EU868); 573 | lora.setAdaptiveDataRate(false); 574 | lora.setDutyCycle(true); 575 | oldisf = isf; 576 | cnt = -1; 577 | } 578 | } 579 | 580 | result = lora.transferPacketLinkCheckReq(5); 581 | 582 | if (result == true) { 583 | cnt++; 584 | 585 | UISet(&UIInputbox_awnh87, "LCR OK"); 586 | 587 | short length; 588 | short rssi; 589 | float snr; 590 | char charsnr[5]; 591 | short gwcnt; 592 | 593 | memset(buffer, 0, 256); 594 | length = lora.receivePacket(buffer, 256, &rssi, &snr, &gwcnt); 595 | 596 | dtostrf(snr, 5, 1, charsnr); 597 | 598 | UISet(&UIProgressbar_eymzer, (rssi + 130) * 2); 599 | UISet(&UITextbox_859t1hi, rssi); 600 | UISet(&UITextbox_olwwlae, charsnr); 601 | UISet(&UIInputbox_6nssds, gwcnt); 602 | 603 | #ifdef M5go 604 | if (rssi < -120) { 605 | strip.SetPixelColor(7, blue); 606 | } else if (rssi < -115) { 607 | strip.SetPixelColor(7, lightblue); 608 | } else if (rssi < -110) { 609 | strip.SetPixelColor(7, green); 610 | } else if (rssi < -105) { 611 | strip.SetPixelColor(7, yellow); 612 | } else if (rssi < -100) { 613 | strip.SetPixelColor(7, orange); 614 | } else { 615 | strip.SetPixelColor(7, red); 616 | } 617 | #endif 618 | 619 | } else if (lora.dutycycle == true) { 620 | UISet(&UIInputbox_awnh87, "DutyCycle"); 621 | } else { 622 | UISet(&UIInputbox_awnh87, "Error"); 623 | UISet(&UITextbox_859t1hi, "-130"); 624 | UISet(&UITextbox_olwwlae, "-20.0"); 625 | UISet(&UIProgressbar_eymzer, 0); 626 | UISet(&UIInputbox_6nssds, 0); 627 | } 628 | } 629 | lora.setDeviceLowPower(); 630 | } 631 | 632 | void sendobjectotaa() { 633 | 634 | bool result = false; 635 | 636 | int32_t lat = latitude * 10000; 637 | int32_t lon = longitude * 10000; 638 | int16_t altitude = alt * 100; 639 | int8_t hdopGPS = hdop / 10; 640 | 641 | coords[0] = lat; 642 | coords[1] = lat >> 8; 643 | coords[2] = lat >> 16; 644 | 645 | coords[3] = lon; 646 | coords[4] = lon >> 8; 647 | coords[5] = lon >> 16; 648 | 649 | coords[6] = altitude; 650 | coords[7] = altitude >> 8; 651 | 652 | coords[8] = hdopGPS; 653 | 654 | sentMillis = millis(); 655 | 656 | lora.sendDevicePing(); 657 | 658 | UISet(&UIInputbox_awnh87, "Sending"); 659 | 660 | #ifdef M5gps 661 | if (otaaack == 0) { 662 | result = lora.transferPacket(coords, sizeof(coords), 5); 663 | } else if (otaaack == 1) { 664 | result = lora.transferPacketWithConfirmed(coords, sizeof(coords), 5); 665 | } 666 | #else 667 | if (otaaack == 0) { 668 | result = lora.transferPacket(ncoords, sizeof(ncoords), 5); 669 | } else if (otaaack == 1) { 670 | result = lora.transferPacketWithConfirmed(ncoords, sizeof(ncoords), 5); 671 | } 672 | #endif 673 | 674 | if (result == true) { 675 | cnt++; 676 | txcnt = String("Sent " + String(cnt)); 677 | UISet(&UIInputbox_awnh87, txcnt); 678 | 679 | short length; 680 | short rssi; 681 | float snr; 682 | char charsnr[5]; 683 | short gwcnt; 684 | 685 | memset(buffer, 0, 256); 686 | length = lora.receivePacket(buffer, 256, &rssi, &snr, &gwcnt); 687 | 688 | dtostrf(snr, 5, 1, charsnr); 689 | if (rssi >= -200) { 690 | UISet(&UIProgressbar_eymzer, rssi + 130); 691 | UISet(&UITextbox_859t1hi, rssi); 692 | UISet(&UITextbox_olwwlae, charsnr); 693 | 694 | #ifdef M5go 695 | if (rssi < -120) { 696 | strip.SetPixelColor(7, blue); 697 | } else if (rssi < -115) { 698 | strip.SetPixelColor(7, lightblue); 699 | } else if (rssi < -110) { 700 | strip.SetPixelColor(7, green); 701 | } else if (rssi < -105) { 702 | strip.SetPixelColor(7, yellow); 703 | } else if (rssi < -100) { 704 | strip.SetPixelColor(7, orange); 705 | } else { 706 | strip.SetPixelColor(7, red); 707 | } 708 | #endif 709 | 710 | } 711 | } else if (lora.dutycycle == true) { 712 | UISet(&UIInputbox_awnh87, "DutyCycle"); 713 | } else { 714 | UISet(&UIInputbox_awnh87, "Error"); 715 | } 716 | lora.setDeviceLowPower(); 717 | } 718 | 719 | #ifdef M5gps 720 | //SiteSurvey function 721 | void ssv() { 722 | 723 | UISet(&UIInputbox_awnh87, "SSV running"); 724 | ssvinit(); 725 | lora.sendDevicePing(); 726 | lora.setDutyCycle(false); 727 | 728 | bool result = false; 729 | ssvresult = "DR "; 730 | 731 | lora.setDataRate(DR5, EU868); 732 | isf = 0; 733 | result = lora.transferPacketLinkCheckReq(5); 734 | 735 | if (result == true) { 736 | memset(buffer, 0, 256); 737 | length = lora.receivePacket(buffer, 256, &rssi, &snr, &gwcnt); 738 | writessv(); 739 | bool result = false; 740 | ssvresult += "5"; 741 | } 742 | 743 | lora.setDataRate(DR4, EU868); 744 | isf = 1; 745 | result = lora.transferPacketLinkCheckReq(5); 746 | 747 | if (result == true) { 748 | memset(buffer, 0, 256); 749 | length = lora.receivePacket(buffer, 256, &rssi, &snr, &gwcnt); 750 | writessv(); 751 | bool result = false; 752 | ssvresult += "4"; 753 | } 754 | 755 | lora.setDataRate(DR3, EU868); 756 | isf = 2; 757 | result = lora.transferPacketLinkCheckReq(5); 758 | 759 | if (result == true) { 760 | memset(buffer, 0, 256); 761 | length = lora.receivePacket(buffer, 256, &rssi, &snr, &gwcnt); 762 | writessv(); 763 | bool result = false; 764 | ssvresult += "3"; 765 | } 766 | 767 | lora.setDataRate(DR2, EU868); 768 | isf = 3; 769 | result = lora.transferPacketLinkCheckReq(5); 770 | 771 | if (result == true) { 772 | memset(buffer, 0, 256); 773 | length = lora.receivePacket(buffer, 256, &rssi, &snr, &gwcnt); 774 | writessv(); 775 | bool result = false; 776 | ssvresult += "2"; 777 | } 778 | 779 | lora.setDataRate(DR1, EU868); 780 | isf = 4; 781 | result = lora.transferPacketLinkCheckReq(5); 782 | 783 | if (result == true) { 784 | memset(buffer, 0, 256); 785 | length = lora.receivePacket(buffer, 256, &rssi, &snr, &gwcnt); 786 | writessv(); 787 | bool result = false; 788 | ssvresult += "1"; 789 | } 790 | 791 | lora.setDataRate(DR0, EU868); 792 | isf = 5; 793 | result = lora.transferPacketLinkCheckReq(5); 794 | 795 | if (result == true) { 796 | memset(buffer, 0, 256); 797 | length = lora.receivePacket(buffer, 256, &rssi, &snr, &gwcnt); 798 | writessv(); 799 | bool result = false; 800 | ssvresult += "0"; 801 | } 802 | 803 | lastssv = true; 804 | writessv(); 805 | lastssv = false; 806 | firstssv = false; 807 | 808 | UISet(&UIInputbox_awnh87, ssvresult); 809 | 810 | lora.setDutyCycle(true); 811 | lora.setDataRate(DR5, EU868); 812 | lora.setDeviceLowPower(); 813 | isf = 0; 814 | cnt = -1; 815 | } 816 | 817 | //Initialize GeoJSON file 818 | void ssvinit() { 819 | if (cardin == true && gps.location.isValid() == true) { 820 | sprintf(filename2, "/%02d-%02d-%02d", day, month, year - 2000); 821 | sprintf(filepath2, "/%02d-%02d-%02d/%02d-%02d%s", day, month, year - 2000, hour, minute, ".json"); 822 | 823 | SD.mkdir(filename2); 824 | if (!SD.exists(filepath2)) { 825 | dataFile = SD.open(filepath2, FILE_WRITE); 826 | dataFile.close(); 827 | } 828 | } 829 | } 830 | 831 | //Write data to GeoJSON file 832 | void writessv() { 833 | if (gps.location.isValid() == true) { 834 | gpsdata(); 835 | sprintf(date1, "%4d-%02d-%02dT%02d:%02d:%02dZ", year, month, day, hour, minute, second); 836 | 837 | dataFile = SD.open(filepath2, FILE_WRITE); 838 | unsigned long filesize = dataFile.size(); 839 | dataFile.seek(filesize); 840 | 841 | if (lastssv == false) { 842 | if (firstssv == false) { 843 | firstssv = true; 844 | dataFile.println(F("{")); 845 | dataFile.println(F("\"type\": \"FeatureCollection\",")); 846 | dataFile.println(F("\"features\": [{")); 847 | } else { 848 | dataFile.println(F(",{")); 849 | } 850 | dataFile.println(F("\"type\": \"Feature\",")); 851 | dataFile.println(F("\"properties\": {")); 852 | dataFile.print(F("\"sf\": \"")); 853 | dataFile.print(sf[isf]); 854 | dataFile.print(F("\",\r\n")); 855 | dataFile.print(F("\"rssi\": \"")); 856 | dataFile.print(rssi); 857 | dataFile.print(F("\",\r\n")); 858 | dataFile.print(F("\"snr\": \"")); 859 | dataFile.print(snr); 860 | dataFile.print(F("\",\r\n")); 861 | dataFile.print(F("\"gwcnt\": \"")); 862 | dataFile.print(gwcnt); 863 | dataFile.print(F("\",\r\n")); 864 | dataFile.println(F("\"marker-color\": \"#008800\",")); 865 | dataFile.println(F("\"marker-symbol\": \"lighthouse\"")); 866 | dataFile.println(F("},")); 867 | dataFile.println(F("\"geometry\": {")); 868 | dataFile.println(F("\"type\": \"Point\",")); 869 | dataFile.print(F("\"coordinates\": [")); 870 | dataFile.print(longitude, 7); 871 | dataFile.print(F(", ")); 872 | dataFile.print(latitude, 7); 873 | dataFile.print(F("]\r\n")); 874 | dataFile.println(F("}")); 875 | dataFile.println(F("}")); 876 | dataFile.close(); 877 | } else { 878 | dataFile.println(F("]}")); 879 | dataFile.close(); 880 | } 881 | } 882 | } 883 | #endif 884 | 885 | 886 | //initial setup 887 | void setup() { 888 | /* Prepare M5STACK */ 889 | M5.begin(); 890 | M5.Power.begin(); 891 | Wire.begin(); 892 | #ifdef M5gps 893 | serialgps.begin(9600, SERIAL_8N1, 16, 17); 894 | #endif 895 | M5.Lcd.setBrightness(50); 896 | //M5.Lcd.pushImage(0, 0, 320, 240, (uint16_t *)imgName); 897 | M5.Lcd.pushImage(0, 0, 320, 240, (uint16_t *)gImage_logoM5); 898 | initlora(); 899 | 900 | #ifdef M5gps 901 | xTaskCreatePinnedToCore( 902 | gpsupdate, 903 | "TaskGPS", 904 | 10000, 905 | NULL, 906 | 1, 907 | &TaskGPS, 908 | 0); 909 | #endif 910 | 911 | #ifdef M5go 912 | strip.Begin(); 913 | strip.Show(); 914 | 915 | if (powersave == false) { 916 | strip.SetBrightness(50); 917 | } else { 918 | strip.SetBrightness(0); 919 | } 920 | 921 | xTaskCreatePinnedToCore( 922 | pixelupdate, 923 | "TaskPixel", 924 | 10000, 925 | NULL, 926 | 1, 927 | &TaskPixel, 928 | 0); 929 | #endif 930 | 931 | /* Prepare UI */ 932 | UIBegin(); 933 | LayerFunction_default(0); 934 | 935 | #ifdef M5gps 936 | M5.Lcd.pushImage(5, 2, 24, 24, (uint16_t *)ICON_10_24); 937 | M5.Lcd.pushImage(65, 5, 24, 24, (uint16_t *)ICON_23_24); 938 | #endif 939 | 940 | if (SD.exists(filename)) { 941 | M5.Lcd.pushImage(200, 5, 24, 24, (uint16_t *)ICON_22_24); 942 | cardin = true; 943 | } 944 | 945 | //Prepare UI for iwm = 0 946 | UISet(&UITextbox_vimqus, sf[isf]); 947 | UIDisable(true, &UIProgressbar_eymzer); 948 | UIDisable(true, &UITextbox_859t1hi); 949 | UIDisable(true, &UITextbox_olwwlae); 950 | UIDisable(true, &UIInputbox_6nssds); 951 | UIDisable(true, &UITextbox_7mnuudb); 952 | UIDisable(false, &UIInputbox_awnh87); 953 | 954 | #ifndef M5gps 955 | UIDisable(true, &UITextbox_4t0l0bn); 956 | UIDisable(true, &UITextbox_q7sl3uo); 957 | #endif 958 | 959 | Serial.println("Started"); 960 | 961 | if (powersave == true) { 962 | smartDelay(1000); 963 | #ifdef M5gps 964 | gpsdata(); 965 | #endif 966 | sendobject(); 967 | esp_sleep_enable_timer_wakeup(interval[iiv] * 1000); 968 | esp_deep_sleep_start(); 969 | } 970 | 971 | } 972 | 973 | void loop() { 974 | 975 | //update button status 976 | if (M5.BtnA.wasPressed()) { 977 | if (iwm == 6) { 978 | iwm = 0; 979 | UISet(&UITextbox_eq79hh46, workmode[iwm]); 980 | if (otaa == 1) { 981 | initloraabp(); 982 | } 983 | } else if (iwm == 3) { 984 | iwm++; 985 | #ifndef M5gps 986 | iwm++; 987 | #endif 988 | UISet(&UITextbox_eq79hh46, workmode[iwm]); 989 | } else { 990 | iwm++; 991 | UISet(&UITextbox_eq79hh46, workmode[iwm]); 992 | } 993 | 994 | if (iwm == 0) { 995 | UISet(&UITextbox_vimqus, sf[isf]); 996 | UIDisable(false, &UIInputbox_awnh87); 997 | UISet(&UITextbox_67ofwdh, "Dim"); 998 | } else if (iwm == 1) { 999 | UIDisable(false, &UIProgressbar_eymzer); 1000 | UIDisable(false, &UITextbox_859t1hi); 1001 | UIDisable(false, &UITextbox_olwwlae); 1002 | UIDisable(false, &UITextbox_7mnuudb); 1003 | UISet(&UITextbox_859t1hi, "-130"); 1004 | UISet(&UITextbox_olwwlae, "-20.0"); 1005 | UISet(&UIProgressbar_eymzer, 0); 1006 | } else if (iwm == 2) { 1007 | UISet(&UITextbox_67ofwdh, "Send"); 1008 | UISet(&UITextbox_859t1hi, "-130"); 1009 | UISet(&UITextbox_olwwlae, "-20.0"); 1010 | UISet(&UIProgressbar_eymzer, 0); 1011 | } else if (iwm == 3) { 1012 | UIDisable(false, &UIInputbox_6nssds); 1013 | UISet(&UITextbox_859t1hi, "-130"); 1014 | UISet(&UITextbox_olwwlae, "-20.0"); 1015 | UISet(&UIProgressbar_eymzer, 0); 1016 | UISet(&UIInputbox_6nssds, ""); 1017 | } else if (iwm == 4) { 1018 | UIDisable(true, &UIProgressbar_eymzer); 1019 | UIDisable(true, &UITextbox_859t1hi); 1020 | UIDisable(true, &UITextbox_olwwlae); 1021 | UIDisable(true, &UIInputbox_6nssds); 1022 | UIDisable(true, &UITextbox_7mnuudb); 1023 | } else if (iwm == 5) { 1024 | #ifndef M5gps 1025 | UIDisable(true, &UIInputbox_6nssds); 1026 | UIDisable(true, &UITextbox_7mnuudb); 1027 | #endif 1028 | UISet(&UITextbox_vimqus, "Join"); 1029 | UISet(&UITextbox_67ofwdh, "NACK"); 1030 | UIDisable(false, &UIProgressbar_eymzer); 1031 | UIDisable(false, &UITextbox_859t1hi); 1032 | UIDisable(false, &UITextbox_olwwlae); 1033 | UISet(&UITextbox_859t1hi, "-130"); 1034 | UISet(&UITextbox_olwwlae, "-20.0"); 1035 | UISet(&UIProgressbar_eymzer, 0); 1036 | //strip.SetPixelColor(7, off); 1037 | } else if (iwm == 6) { 1038 | UISet(&UITextbox_vimqus, ttext[iiv]); 1039 | UIDisable(true, &UIInputbox_awnh87); 1040 | UIDisable(true, &UIProgressbar_eymzer); 1041 | UIDisable(true, &UITextbox_859t1hi); 1042 | UIDisable(true, &UITextbox_olwwlae); 1043 | UISet(&UITextbox_67ofwdh, "PS"); 1044 | } 1045 | } 1046 | 1047 | if (M5.BtnB.wasPressed()) { 1048 | if (isf == 5) { 1049 | isf = 0; 1050 | UISet(&UITextbox_vimqus, sf[isf]); 1051 | } else if (iwm == 5 && otaa == 0) { 1052 | initloraotaa(); //OTAA Join 1053 | UISet(&UITextbox_vimqus, "Send"); 1054 | } else if (iwm == 5 && otaa == 1) { 1055 | sendobjectotaa(); //Manual send 1056 | } else if (iwm == 6) { 1057 | if (iiv == 4) { 1058 | iiv = 0; 1059 | UISet(&UITextbox_vimqus, ttext[iiv]); 1060 | } else { 1061 | iiv++; 1062 | UISet(&UITextbox_vimqus, ttext[iiv]); 1063 | } 1064 | } else { 1065 | isf++; 1066 | UISet(&UITextbox_vimqus, sf[isf]); 1067 | } 1068 | } 1069 | 1070 | if (M5.BtnC.wasPressed()) { 1071 | if (iwm < 2 && dim == false) { 1072 | dim = true; 1073 | M5.Lcd.setBrightness(0); 1074 | #ifdef M5go 1075 | strip.SetBrightness(0); 1076 | #endif 1077 | } else if (iwm < 2 && dim == true) { 1078 | dim = false; 1079 | M5.Lcd.setBrightness(50); 1080 | #ifdef M5go 1081 | strip.SetBrightness(50); 1082 | #endif 1083 | } else if (iwm == 4) { 1084 | #ifdef M5gps 1085 | ssv(); 1086 | #endif 1087 | } else if (iwm == 5 && otaaack == 0) { 1088 | otaaack = 1; 1089 | UISet(&UITextbox_67ofwdh, "ACK"); 1090 | } else if (iwm == 5 && otaaack == 1) { 1091 | otaaack = 0; 1092 | UISet(&UITextbox_67ofwdh, "NACK"); 1093 | } else if (iwm == 6 && powersave == false) { 1094 | powersave = true; 1095 | iwm = 0; 1096 | } else if (iwm == 6 && powersave == true) { 1097 | powersave = false; 1098 | } else if (iwm > 1) { 1099 | sendobject(); 1100 | } 1101 | } 1102 | 1103 | #ifdef M5gps 1104 | //Update GPS Data 1105 | gpsdata(); 1106 | #endif 1107 | 1108 | #ifdef M5gps 1109 | //Print satellites and change NeoPixel 4 1110 | sats = gps.satellites.value(); 1111 | UISet(&UITextbox_4t0l0bn, sats); 1112 | 1113 | //Print HDOP and change NeoPixel 0 1114 | hdop = gps.hdop.value(); 1115 | hdop2 = hdop / 100.0; 1116 | String stringhdop = String(hdop2); 1117 | UISet(&UITextbox_q7sl3uo, stringhdop); 1118 | 1119 | //Print GPS fix status und change NeoPixel 2 1120 | if (gps.location.isValid() == false) { 1121 | M5.Lcd.pushImage(160, 5, 24, 24, (uint16_t *)ICON_25_24); 1122 | } 1123 | else if (gps.location.isValid() && gps.location.age() > 2000) { 1124 | M5.Lcd.pushImage(160, 5, 24, 24, (uint16_t *)ICON_25_24); 1125 | } 1126 | else if (gps.location.isValid() == true) { 1127 | M5.Lcd.pushImage(160, 5, 24, 24, (uint16_t *)ICON_20_24); 1128 | } 1129 | else { 1130 | M5.Lcd.pushImage(160, 5, 24, 24, (uint16_t *)ICON_20_24); 1131 | } 1132 | #endif 1133 | 1134 | //Battery Status 1135 | if (M5.Power.isCharging() == true) { 1136 | M5.Lcd.pushImage(240, 5, 24, 24, (uint16_t *)ICON_40_24); 1137 | } 1138 | 1139 | if (M5.Power.isChargeFull() == true) { 1140 | UISet(&UITextbox_403ohip, "Full"); 1141 | } 1142 | else { 1143 | BattLevel = M5.Power.getBatteryLevel(); 1144 | String strbattlevel = String(BattLevel); 1145 | strbattlevel = String(strbattlevel + "%"); 1146 | UISet(&UITextbox_403ohip, strbattlevel); 1147 | } 1148 | 1149 | #ifdef M5go 1150 | strip.Show(); 1151 | #endif 1152 | 1153 | #ifdef M5gps 1154 | //Init of SD Card for GPX-file 1155 | if (sdwrite == false) { 1156 | gpxinit(); 1157 | } 1158 | 1159 | //Write GPS-Track 1160 | if (sdwrite == true) { 1161 | writegpx(); 1162 | } 1163 | #endif 1164 | 1165 | //Sending intervall 1166 | currentMillis = millis(); 1167 | if ((currentMillis - sentMillis > interval[iiv]) && iwm < 2) { 1168 | sendobject(); 1169 | } 1170 | 1171 | #ifdef M5gps 1172 | if ((currentMillis - sentMillis > interval[iiv]) && iwm == 5 && otaa == 1 && gps.location.isValid() == true && gps.location.age() < 2000) { 1173 | sendobjectotaa(); 1174 | } 1175 | #else 1176 | if ((currentMillis - sentMillis > interval[iiv]) && iwm == 5 && otaa == 1) { 1177 | sendobjectotaa(); 1178 | } 1179 | #endif 1180 | 1181 | //light sleep timer 1182 | if (powersave == true) { 1183 | #ifdef M5go 1184 | strip.SetBrightness(0); 1185 | #endif 1186 | esp_sleep_enable_timer_wakeup(interval[iiv] * 1000); 1187 | esp_deep_sleep_start(); 1188 | } 1189 | 1190 | //used to deflicker the display, more possible, but with less reactive buttons 1191 | smartDelay(200); 1192 | 1193 | M5.update(); 1194 | } 1195 | -------------------------------------------------------------------------------- /networktester/src/LoRaWan.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | LoRaWAN.cpp 3 | 2013 Copyright (c) Seeed Technology Inc. All right reserved. 4 | 5 | Author: Wayne Weng 6 | Date: 2016-10-17 7 | 8 | add rgb backlight fucnction @ 2013-10-15 9 | 10 | The MIT License (MIT) 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy 13 | of this software and associated documentation files (the "Software"), to deal 14 | in the Software without restriction, including without limitation the rights 15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the Software is 17 | furnished to do so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be included in 20 | all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | THE SOFTWARE.1 USA 29 | */ 30 | 31 | #include "LoRaWan.h" 32 | 33 | static void smartDelay(unsigned long ms) { 34 | unsigned long start = millis(); 35 | do 36 | {} while (millis() - start < ms); 37 | } 38 | 39 | 40 | LoRaWanClass::LoRaWanClass(void) 41 | { 42 | memset(_buffer, 0, 256); 43 | } 44 | 45 | void LoRaWanClass::init(void) 46 | { 47 | SerialLoRa.begin(9600, SERIAL_8N1, 2, 5); 48 | //SerialLoRa.begin(9600, SERIAL_8N1, 16, 17); 49 | } 50 | 51 | void LoRaWanClass::getVersion(char *buffer, short length, unsigned char timeout) 52 | { 53 | if(buffer) 54 | { 55 | while(SerialLoRa.available())SerialLoRa.read(); 56 | sendCommand("AT+VER=?\r\n"); 57 | readBuffer(buffer, length, timeout); 58 | } 59 | } 60 | 61 | void LoRaWanClass::getId(char *buffer, short length, unsigned char timeout) 62 | { 63 | if(buffer) 64 | { 65 | while(SerialLoRa.available())SerialLoRa.read(); 66 | sendCommand("AT+ID=?\r\n"); 67 | readBuffer(buffer, length, timeout); 68 | } 69 | } 70 | 71 | void LoRaWanClass::setId(char *DevAddr, char *DevEUI, char *AppEUI) 72 | { 73 | char cmd[64]; 74 | 75 | if(DevAddr) 76 | { 77 | memset(cmd, 0, 64); 78 | sprintf(cmd, "AT+ID=DevAddr,\"%s\"\r\n", DevAddr); 79 | sendCommand(cmd); 80 | smartDelay(DEFAULT_TIMEWAIT); 81 | } 82 | 83 | if(DevEUI) 84 | { 85 | memset(cmd, 0, 64); 86 | sprintf(cmd, "AT+ID=DevEui,\"%s\"\r\n", DevEUI); 87 | sendCommand(cmd); 88 | smartDelay(DEFAULT_TIMEWAIT); 89 | } 90 | 91 | if(AppEUI) 92 | { 93 | memset(cmd, 0, 64); 94 | sprintf(cmd, "AT+ID=AppEui,\"%s\"\r\n", AppEUI); 95 | sendCommand(cmd); 96 | smartDelay(DEFAULT_TIMEWAIT); 97 | } 98 | } 99 | 100 | void LoRaWanClass::setKey(char *NwkSKey, char *AppSKey, char *AppKey) 101 | { 102 | char cmd[64]; 103 | 104 | if(NwkSKey) 105 | { 106 | memset(cmd, 0, 64); 107 | sprintf(cmd, "AT+KEY=NWKSKEY,\"%s\"\r\n", NwkSKey); 108 | sendCommand(cmd); 109 | #if _DEBUG_SERIAL_ 110 | loraDebugPrint(DEFAULT_DEBUGTIME); 111 | #endif 112 | smartDelay(DEFAULT_TIMEWAIT); 113 | } 114 | 115 | if(AppSKey) 116 | { 117 | memset(cmd, 0, 64); 118 | sprintf(cmd, "AT+KEY=APPSKEY,\"%s\"\r\n", AppSKey); 119 | sendCommand(cmd); 120 | #if _DEBUG_SERIAL_ 121 | loraDebugPrint(DEFAULT_DEBUGTIME); 122 | #endif 123 | smartDelay(DEFAULT_TIMEWAIT); 124 | } 125 | 126 | if(AppKey) 127 | { 128 | memset(cmd, 0, 64); 129 | sprintf(cmd, "AT+KEY= APPKEY,\"%s\"\r\n", AppKey); 130 | sendCommand(cmd); 131 | #if _DEBUG_SERIAL_ 132 | loraDebugPrint(DEFAULT_DEBUGTIME); 133 | #endif 134 | smartDelay(DEFAULT_TIMEWAIT); 135 | } 136 | } 137 | 138 | void LoRaWanClass::setDataRate(_data_rate_t dataRate, _physical_type_t physicalType) 139 | { 140 | char cmd[32]; 141 | 142 | if(physicalType == EU434)sendCommand("AT+DR=EU433\r\n"); 143 | else if(physicalType == EU868)sendCommand("AT+DR=EU868\r\n"); 144 | else if(physicalType == US915)sendCommand("AT+DR=US915\r\n"); 145 | else if(physicalType == US915HYBRID)sendCommand("AT+DR=US915HYBRID\r\n"); 146 | else if(physicalType == AU915)sendCommand("AT+DR=AU915\r\n"); 147 | else if(physicalType == AU915OLD)sendCommand("AT+DR=AU915OLD\r\n"); 148 | else if(physicalType == CN470)sendCommand("AT+DR=CN470\r\n"); 149 | else if(physicalType == CN779)sendCommand("AT+DR=CN779\r\n"); 150 | else if(physicalType == AS923)sendCommand("AT+DR=AS923\r\n"); 151 | else if(physicalType == KR920)sendCommand("AT+DR=KR920\r\n"); 152 | else if(physicalType == IN865)sendCommand("AT+DR=IN865\r\n"); 153 | 154 | #if _DEBUG_SERIAL_ 155 | loraDebugPrint(DEFAULT_DEBUGTIME); 156 | #endif 157 | smartDelay(DEFAULT_TIMEWAIT); 158 | 159 | memset(cmd, 0, 32); 160 | sprintf(cmd, "AT+DR=%d\r\n", dataRate); 161 | sendCommand(cmd); 162 | #if _DEBUG_SERIAL_ 163 | loraDebugPrint(DEFAULT_DEBUGTIME); 164 | #endif 165 | smartDelay(DEFAULT_TIMEWAIT); 166 | } 167 | 168 | void LoRaWanClass::setPower(short power) 169 | { 170 | char cmd[32]; 171 | 172 | memset(cmd, 0, 32); 173 | sprintf(cmd, "AT+POWER=%d\r\n", power); 174 | sendCommand(cmd); 175 | #if _DEBUG_SERIAL_ 176 | loraDebugPrint(DEFAULT_DEBUGTIME); 177 | #endif 178 | smartDelay(DEFAULT_TIMEWAIT); 179 | } 180 | 181 | void LoRaWanClass::setPort(int port) 182 | { 183 | char cmd[32]; 184 | 185 | memset(cmd, 0, 32); 186 | sprintf(cmd, "AT+PORT=%d\r\n", port); 187 | sendCommand(cmd); 188 | #if _DEBUG_SERIAL_ 189 | loraDebugPrint(DEFAULT_DEBUGTIME); 190 | #endif 191 | smartDelay(DEFAULT_TIMEWAIT); 192 | } 193 | 194 | void LoRaWanClass::setAdaptiveDataRate(bool command) 195 | { 196 | if(command)sendCommand("AT+ADR=ON\r\n"); 197 | else sendCommand("AT+ADR=OFF\r\n"); 198 | #if _DEBUG_SERIAL_ 199 | loraDebugPrint(DEFAULT_DEBUGTIME); 200 | #endif 201 | smartDelay(DEFAULT_TIMEWAIT); 202 | } 203 | 204 | void LoRaWanClass::setChannel(unsigned char channel, float frequency) 205 | { 206 | char cmd[32]; 207 | 208 | memset(cmd, 0, 32); 209 | sprintf(cmd, "AT+CH=%d,%d.%d\r\n", channel, (short)frequency, short(frequency * 10) % 10); 210 | sendCommand(cmd); 211 | #if _DEBUG_SERIAL_ 212 | loraDebugPrint(DEFAULT_DEBUGTIME); 213 | #endif 214 | smartDelay(DEFAULT_TIMEWAIT); 215 | } 216 | 217 | void LoRaWanClass::setChannel(unsigned char channel, float frequency, _data_rate_t dataRata) 218 | { 219 | char cmd[32]; 220 | 221 | memset(cmd, 0, 32); 222 | sprintf(cmd, "AT+CH=%d,%d.%d,%d\r\n", channel, (short)frequency, short(frequency * 10) % 10, dataRata); 223 | sendCommand(cmd); 224 | #if _DEBUG_SERIAL_ 225 | loraDebugPrint(DEFAULT_DEBUGTIME); 226 | #endif 227 | smartDelay(DEFAULT_TIMEWAIT); 228 | } 229 | 230 | void LoRaWanClass::setChannel(unsigned char channel, float frequency, _data_rate_t dataRataMin, _data_rate_t dataRataMax) 231 | { 232 | char cmd[32]; 233 | 234 | memset(cmd, 0, 32); 235 | sprintf(cmd, "AT+CH=%d,%d.%d,%d,%d\r\n", channel, (short)frequency, short(frequency * 10) % 10, dataRataMin, dataRataMax); 236 | sendCommand(cmd); 237 | #if _DEBUG_SERIAL_ 238 | loraDebugPrint(DEFAULT_DEBUGTIME); 239 | #endif 240 | smartDelay(DEFAULT_TIMEWAIT); 241 | } 242 | 243 | bool LoRaWanClass::transferPacket(char *buffer, unsigned char timeout) 244 | { 245 | unsigned char length = strlen(buffer); 246 | 247 | while(SerialLoRa.available())SerialLoRa.read(); 248 | 249 | sendCommand("AT+MSG=\""); 250 | for(int i = 0; i < length; i ++)SerialLoRa.write(buffer[i]); 251 | sendCommand("\"\r\n"); 252 | 253 | memset(_buffer, 0, BEFFER_LENGTH_MAX); 254 | readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout); 255 | #if _DEBUG_SERIAL_ 256 | SerialUSB.print(_buffer); 257 | #endif 258 | if(strstr(_buffer, "+MSG: Done"))return true; 259 | return false; 260 | } 261 | 262 | bool LoRaWanClass::transferPacket(unsigned char *buffer, int length, unsigned char timeout) 263 | { 264 | char temp[3] = {0}; //char temp[3] = {0}; In C strings are 0-terminated, meaning that after the call to sprintf, _int_char contains { 'a', 'b', '\0' }. This is an array of 3 chars and you only reserver memory for 2 265 | while(SerialLoRa.available())SerialLoRa.read(); 266 | sendCommand("AT+MSGHEX=\""); 267 | for(int i = 0; i < length; i++) 268 | { 269 | sprintf(temp,"%02x", buffer[i]); //snprintf(temp, 2, "%02x", buffer[i]); 270 | SerialLoRa.write(temp); 271 | } 272 | sendCommand("\"\r\n"); 273 | memset(_buffer, 0, BEFFER_LENGTH_MAX); 274 | readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout); 275 | #if _DEBUG_SERIAL_ 276 | SerialUSB.print(_buffer); 277 | #endif 278 | dutycycle = false; 279 | if(strstr(_buffer, "+MSGHEX: No band")){ 280 | dutycycle = true; 281 | } 282 | if(strstr(_buffer, "+MSGHEX: Done"))return true; 283 | return false; 284 | } 285 | 286 | bool LoRaWanClass::transferPacketWithConfirmed(char *buffer, unsigned char timeout) 287 | { 288 | unsigned char length = strlen(buffer); 289 | 290 | while(SerialLoRa.available())SerialLoRa.read(); 291 | 292 | sendCommand("AT+CMSG=\""); 293 | for(int i = 0; i < length; i ++)SerialLoRa.write(buffer[i]); 294 | sendCommand("\"\r\n"); 295 | 296 | memset(_buffer, 0, BEFFER_LENGTH_MAX); 297 | readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout); 298 | #if _DEBUG_SERIAL_ 299 | SerialUSB.print(_buffer); 300 | #endif 301 | if(strstr(_buffer, "+CMSG: ACK Received"))return true; 302 | return false; 303 | } 304 | 305 | bool LoRaWanClass::transferPacketWithConfirmed(unsigned char *buffer, int length, unsigned char timeout) 306 | { 307 | char temp[3] = {0}; 308 | 309 | while(SerialLoRa.available())SerialLoRa.read(); 310 | 311 | sendCommand("AT+CMSGHEX=\""); 312 | for(int i = 0; i < length; i ++) 313 | { 314 | sprintf(temp,"%02x", buffer[i]); 315 | SerialLoRa.write(temp); 316 | } 317 | sendCommand("\"\r\n"); 318 | 319 | memset(_buffer, 0, BEFFER_LENGTH_MAX); 320 | readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout); 321 | #if _DEBUG_SERIAL_ 322 | SerialUSB.print(_buffer); 323 | #endif 324 | if(strstr(_buffer, "+CMSGHEX: ACK Received"))return true; 325 | return false; 326 | } 327 | 328 | bool LoRaWanClass::transferPacketLinkCheckReq(unsigned char timeout) 329 | { 330 | 331 | while(SerialLoRa.available())SerialLoRa.read(); 332 | 333 | sendCommand("AT+MSG\r\n"); 334 | 335 | memset(_buffer, 0, BEFFER_LENGTH_MAX); 336 | readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout); 337 | #if _DEBUG_SERIAL_ 338 | SerialUSB.print(_buffer); 339 | #endif 340 | dutycycle = false; 341 | if(strstr(_buffer, "+MSG: No band")){ 342 | dutycycle = true; 343 | } 344 | if(strstr(_buffer, "+MSG: Done"))return true; 345 | return false; 346 | } 347 | 348 | short LoRaWanClass::receivePacket(char *buffer, int length, short *rssi, float *snr, short *gwcnt) 349 | { 350 | char *ptr; 351 | short number = 0; 352 | 353 | ptr = strstr(_buffer, "Link"); 354 | if(ptr)*gwcnt = atoi(ptr + 9); 355 | else *gwcnt = 0; 356 | 357 | ptr = strstr(_buffer, "RSSI "); 358 | if(ptr)*rssi = atoi(ptr + 5); 359 | else *rssi = -255; 360 | 361 | ptr = strstr(_buffer, "SNR "); 362 | if(ptr)*snr = atof(ptr + 4); 363 | else *snr = -20.00; 364 | SerialUSB.println(*snr); 365 | 366 | ptr = strstr(_buffer, "RX: \""); 367 | if(ptr) 368 | { 369 | ptr += 5; 370 | 371 | uint8_t bitStep = 0; 372 | if(*(ptr + 2) == ' ')bitStep = 3; // Firmware version 2.0.10 373 | else bitStep = 2; // Firmware version 2.1.15 374 | 375 | for(short i = 0; ; i ++) 376 | { 377 | char temp[2] = {0}; 378 | unsigned char tmp, result = 0; 379 | 380 | temp[0] = *(ptr + i * bitStep); 381 | temp[1] = *(ptr + i * bitStep + 1); 382 | 383 | for(unsigned char j = 0; j < 2; j ++) 384 | { 385 | if((temp[j] >= '0') && (temp[j] <= '9')) 386 | tmp = temp[j] - '0'; 387 | else if((temp[j] >= 'A') && (temp[j] <= 'F')) 388 | tmp = temp[j] - 'A' + 10; 389 | else if((temp[j] >= 'a') && (temp[j] <= 'f')) 390 | tmp = temp[j] - 'a' + 10; 391 | 392 | result = result * 16 + tmp; 393 | } 394 | 395 | if(i < length)buffer[i] = result; 396 | 397 | if(*(ptr + (i + 1) * bitStep) == '\"' && *(ptr + (i + 1) * bitStep + 1) == '\r' && *(ptr + (i + 1) * bitStep + 2) == '\n') 398 | { 399 | number = i + 1; 400 | break; 401 | } 402 | } 403 | } 404 | 405 | memset(_buffer, 0, BEFFER_LENGTH_MAX); 406 | 407 | return number; 408 | } 409 | 410 | bool LoRaWanClass::transferProprietaryPacket(char *buffer, unsigned char timeout) 411 | { 412 | unsigned char length = strlen(buffer); 413 | 414 | while(SerialLoRa.available())SerialLoRa.read(); 415 | 416 | sendCommand("AT+PMSG=\""); 417 | for(int i = 0; i < length; i ++)SerialLoRa.write(buffer[i]); 418 | sendCommand("\"\r\n"); 419 | 420 | memset(_buffer, 0, BEFFER_LENGTH_MAX); 421 | readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout); 422 | #if _DEBUG_SERIAL_ 423 | SerialUSB.print(_buffer); 424 | #endif 425 | if(strstr(_buffer, "+PMSG: Done"))return true; 426 | return false; 427 | } 428 | 429 | bool LoRaWanClass::transferProprietaryPacket(unsigned char *buffer, unsigned char length, unsigned char timeout) 430 | { 431 | char temp[3] = {0}; 432 | 433 | while(SerialLoRa.available())SerialLoRa.read(); 434 | 435 | sendCommand("AT+PMSGHEX=\""); 436 | for(int i = 0; i < length; i ++) 437 | { 438 | sprintf(temp,"%02x", buffer[i]); 439 | SerialLoRa.write(temp); 440 | } 441 | sendCommand("\"\r\n"); 442 | 443 | memset(_buffer, 0, BEFFER_LENGTH_MAX); 444 | readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout); 445 | #if _DEBUG_SERIAL_ 446 | SerialUSB.print(_buffer); 447 | #endif 448 | if(strstr(_buffer, "+PMSGHEX: Done"))return true; 449 | return false; 450 | } 451 | 452 | 453 | void LoRaWanClass::setUnconfirmedMessageRepeatTime(unsigned char time) 454 | { 455 | char cmd[32]; 456 | 457 | if(time > 15) time = 15; 458 | else if(time == 0) time = 1; 459 | 460 | memset(cmd, 0, 32); 461 | sprintf(cmd, "AT+REPT=%d\r\n", time); 462 | sendCommand(cmd); 463 | #if _DEBUG_SERIAL_ 464 | loraDebugPrint(DEFAULT_DEBUGTIME); 465 | #endif 466 | smartDelay(DEFAULT_TIMEWAIT); 467 | } 468 | 469 | void LoRaWanClass::setConfirmedMessageRetryTime(unsigned char time) 470 | { 471 | char cmd[32]; 472 | 473 | if(time > 15) time = 15; 474 | else if(time == 0) time = 1; 475 | 476 | memset(cmd, 0, 32); 477 | sprintf(cmd, "AT+RETRY=%d\r\n", time); 478 | sendCommand(cmd); 479 | #if _DEBUG_SERIAL_ 480 | loraDebugPrint(DEFAULT_DEBUGTIME); 481 | #endif 482 | smartDelay(DEFAULT_TIMEWAIT); 483 | } 484 | 485 | void LoRaWanClass::setReceiveWindowFirst(bool command) 486 | { 487 | if(command)sendCommand("AT+RXWIN1=ON\r\n"); 488 | else sendCommand("AT+RXWIN1=OFF\r\n"); 489 | #if _DEBUG_SERIAL_ 490 | loraDebugPrint(DEFAULT_DEBUGTIME); 491 | #endif 492 | smartDelay(DEFAULT_TIMEWAIT); 493 | } 494 | 495 | void LoRaWanClass::setReceiveWindowFirst(unsigned char channel, float frequency) 496 | { 497 | char cmd[32]; 498 | 499 | memset(cmd, 0, 32); 500 | sprintf(cmd, "AT+RXWIN1=%d,%d.%d\r\n", channel, (short)frequency, short(frequency * 10) % 10); 501 | sendCommand(cmd); 502 | #if _DEBUG_SERIAL_ 503 | loraDebugPrint(DEFAULT_DEBUGTIME); 504 | #endif 505 | smartDelay(DEFAULT_TIMEWAIT); 506 | } 507 | 508 | void LoRaWanClass::setReceiveWindowSecond(float frequency, _data_rate_t dataRate) 509 | { 510 | char cmd[32]; 511 | 512 | memset(cmd, 0, 32); 513 | sprintf(cmd, "AT+RXWIN2=%d.%d,%d\r\n", (short)frequency, short(frequency * 10) % 10, dataRate); 514 | sendCommand(cmd); 515 | #if _DEBUG_SERIAL_ 516 | loraDebugPrint(DEFAULT_DEBUGTIME); 517 | #endif 518 | smartDelay(DEFAULT_TIMEWAIT); 519 | } 520 | 521 | void LoRaWanClass::setReceiveWindowSecond(float frequency, _spreading_factor_t spreadingFactor, _band_width_t bandwidth) 522 | { 523 | char cmd[32]; 524 | 525 | memset(cmd, 0, 32); 526 | sprintf(cmd, "AT+RXWIN2=%d.%d,%d,%d\r\n", (short)frequency, short(frequency * 10) % 10, spreadingFactor, bandwidth); 527 | sendCommand(cmd); 528 | #if _DEBUG_SERIAL_ 529 | loraDebugPrint(DEFAULT_DEBUGTIME); 530 | #endif 531 | smartDelay(DEFAULT_TIMEWAIT); 532 | } 533 | 534 | void LoRaWanClass::setDutyCycle(bool command) 535 | { 536 | if(command)sendCommand("AT+LW=DC, ON\r\n"); 537 | else sendCommand("AT+LW=DC, OFF\r\n"); 538 | #if _DEBUG_SERIAL_ 539 | loraDebugPrint(DEFAULT_DEBUGTIME); 540 | #endif 541 | smartDelay(DEFAULT_TIMEWAIT); 542 | } 543 | 544 | void LoRaWanClass::setJoinDutyCycle(bool command) 545 | { 546 | if(command)sendCommand("AT+LW=JDC,ON\r\n"); 547 | else sendCommand("AT+LW=JDC,OFF\r\n"); 548 | #if _DEBUG_SERIAL_ 549 | loraDebugPrint(DEFAULT_DEBUGTIME); 550 | #endif 551 | smartDelay(DEFAULT_TIMEWAIT); 552 | } 553 | 554 | void LoRaWanClass::setReceiveWindowDelay(_window_delay_t command, unsigned short _delay) 555 | { 556 | char cmd[32]; 557 | 558 | memset(cmd, 0, 32); 559 | if(command == RECEIVE_DELAY1) sprintf(cmd, "AT+DELAY=RX1,%d\r\n", _delay); 560 | else if(command == RECEIVE_DELAY2) sprintf(cmd, "AT+DELAY=RX2,%d\r\n", _delay); 561 | else if(command == JOIN_ACCEPT_DELAY1) sprintf(cmd, "AT+DELAY=JRX1,%d\r\n", _delay); 562 | else if(command == JOIN_ACCEPT_DELAY2) sprintf(cmd, "AT+DELAY=JRX2,%d\r\n", _delay); 563 | sendCommand(cmd); 564 | #if _DEBUG_SERIAL_ 565 | loraDebugPrint(DEFAULT_DEBUGTIME); 566 | #endif 567 | smartDelay(DEFAULT_TIMEWAIT); 568 | } 569 | 570 | void LoRaWanClass::setClassType(_class_type_t type) 571 | { 572 | if(type == CLASS_A)sendCommand("AT+CLASS=A\r\n"); 573 | else if(type == CLASS_C)sendCommand("AT+CLASS=C\r\n"); 574 | #if _DEBUG_SERIAL_ 575 | loraDebugPrint(DEFAULT_DEBUGTIME); 576 | #endif 577 | smartDelay(DEFAULT_TIMEWAIT); 578 | } 579 | 580 | void LoRaWanClass::setDeviceMode(_device_mode_t mode) 581 | { 582 | if(mode == LWABP)sendCommand("AT+MODE=LWABP\r\n"); 583 | else if(mode == LWOTAA)sendCommand("AT+MODE=LWOTAA\r\n"); 584 | #if _DEBUG_SERIAL_ 585 | loraDebugPrint(DEFAULT_DEBUGTIME); 586 | #endif 587 | smartDelay(DEFAULT_TIMEWAIT); 588 | } 589 | 590 | bool LoRaWanClass::setOTAAJoin(_otaa_join_cmd_t command, unsigned char timeout) 591 | { 592 | char *ptr; 593 | 594 | if(command == JOIN)sendCommand("AT+JOIN\r\n"); 595 | else if(command == FORCE)sendCommand("AT+JOIN=FORCE\r\n"); 596 | 597 | #if _DEBUG_SERIAL_ 598 | loraDebugPrint(DEFAULT_DEBUGTIME); 599 | #endif 600 | smartDelay(DEFAULT_TIMEWAIT); 601 | 602 | memset(_buffer, 0, BEFFER_LENGTH_MAX); 603 | readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout); 604 | #if _DEBUG_SERIAL_ 605 | SerialUSB.print(_buffer); 606 | #endif 607 | 608 | ptr = strstr(_buffer, "+JOIN: Join failed"); 609 | if(ptr)return false; 610 | ptr = strstr(_buffer, "+JOIN: LoRaWAN modem is busy"); 611 | if(ptr)return false; 612 | 613 | return true; 614 | } 615 | 616 | void LoRaWanClass::setDeviceLowPower(void) 617 | { 618 | sendCommand("AT+LOWPOWER\r\n"); 619 | #if _DEBUG_SERIAL_ 620 | loraDebugPrint(DEFAULT_DEBUGTIME); 621 | #endif 622 | smartDelay(DEFAULT_TIMEWAIT); 623 | } 624 | 625 | void LoRaWanClass::sendDevicePing(void) 626 | { 627 | sendCommand("AT\r\n"); 628 | #if _DEBUG_SERIAL_ 629 | loraDebugPrint(DEFAULT_DEBUGTIME); 630 | #endif 631 | smartDelay(DEFAULT_TIMEWAIT); 632 | } 633 | 634 | void LoRaWanClass::setDeviceReset(void) 635 | { 636 | sendCommand("AT+RESET\r\n"); 637 | #if _DEBUG_SERIAL_ 638 | loraDebugPrint(DEFAULT_DEBUGTIME); 639 | #endif 640 | smartDelay(DEFAULT_TIMEWAIT); 641 | } 642 | 643 | void LoRaWanClass::setDeviceDefault(void) 644 | { 645 | sendCommand("AT+FDEFAULT=RISINGHF\r\n"); 646 | #if _DEBUG_SERIAL_ 647 | loraDebugPrint(DEFAULT_DEBUGTIME); 648 | #endif 649 | smartDelay(DEFAULT_TIMEWAIT); 650 | } 651 | 652 | void LoRaWanClass::initP2PMode(unsigned short frequency, _spreading_factor_t spreadingFactor, _band_width_t bandwidth, 653 | unsigned char txPreamble, unsigned char rxPreamble, short power) 654 | { 655 | char cmd[64] = {0,}; 656 | sprintf(cmd, "AT+TEST=RFCFG,%d,%d,%d,%d,%d,%d\r\n", frequency, spreadingFactor, bandwidth, txPreamble, rxPreamble, power); 657 | 658 | sendCommand("AT+MODE=TEST\r\n"); 659 | smartDelay(DEFAULT_TIMEWAIT); 660 | sendCommand(cmd); 661 | smartDelay(DEFAULT_TIMEWAIT); 662 | sendCommand("AT+TEST=RXLRPKT\r\n"); 663 | smartDelay(DEFAULT_TIMEWAIT); 664 | } 665 | 666 | bool LoRaWanClass::transferPacketP2PMode(char *buffer, unsigned char timeout) 667 | { 668 | unsigned char length = strlen(buffer); 669 | 670 | sendCommand("AT+TEST=TXLRSTR,\""); 671 | for(int i = 0; i < length; i ++)SerialLoRa.write(buffer[i]); 672 | sendCommand("\"\r\n"); 673 | 674 | // memset(_buffer, 0, BEFFER_LENGTH_MAX); 675 | // readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout); 676 | 677 | // if(strstr(_buffer, "+TEST: TX DONE"))return true; 678 | // return false; 679 | 680 | return waitForResponse("+TEST: TX DONE", timeout); 681 | } 682 | 683 | bool LoRaWanClass::transferPacketP2PMode(unsigned char *buffer, unsigned char length, unsigned char timeout) 684 | { 685 | char temp[3] = {0}; 686 | 687 | sendCommand("AT+TEST=TXLRPKT,\""); 688 | for(int i = 0; i < length; i ++) 689 | { 690 | sprintf(temp,"%02x", buffer[i]); 691 | SerialLoRa.write(temp); 692 | } 693 | sendCommand("\"\r\n"); 694 | 695 | // memset(_buffer, 0, BEFFER_LENGTH_MAX); 696 | // readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout); 697 | 698 | // if(strstr(_buffer, "+TEST: TX DONE"))return true; 699 | // return false; 700 | 701 | return waitForResponse("+TEST: TX DONE", timeout); 702 | } 703 | 704 | short LoRaWanClass::receivePacketP2PMode(unsigned char *buffer, int length, short *rssi, unsigned char timeout) 705 | { 706 | char *ptr; 707 | short number; 708 | 709 | sendCommandAndWaitForResponse("AT+TEST=RXLRPKT\r\n", "+TEST: RXLRPKT", 2); 710 | 711 | while(SerialLoRa.available())SerialLoRa.read(); 712 | memset(_buffer, 0, BEFFER_LENGTH_MAX); 713 | readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout); 714 | 715 | ptr = strstr(_buffer, "LEN"); 716 | if(ptr)number = atoi(ptr + 4); 717 | else number = 0; 718 | 719 | if(number <= 0)return 0; 720 | 721 | ptr = strstr(_buffer, "RSSI:"); 722 | if(ptr)*rssi = atoi(ptr + 5); 723 | else *rssi = -255; 724 | 725 | ptr = strstr(_buffer, "RX \""); 726 | if(ptr) 727 | { 728 | ptr += 4; 729 | 730 | uint8_t bitStep = 0; 731 | if(*(ptr + 2) == ' ')bitStep = 3; // Firmware version 2.0.10 732 | else bitStep = 2; // Firmware version 2.1.15 733 | 734 | for(short i = 0; i < number; i ++) 735 | { 736 | char temp[2] = {0}; 737 | unsigned char tmp, result = 0; 738 | 739 | temp[0] = *(ptr + i * bitStep); 740 | temp[1] = *(ptr + i * bitStep + 1); 741 | 742 | for(unsigned char j = 0; j < 2; j ++) 743 | { 744 | if((temp[j] >= '0') && (temp[j] <= '9')) 745 | tmp = temp[j] - '0'; 746 | else if((temp[j] >= 'A') && (temp[j] <= 'F')) 747 | tmp = temp[j] - 'A' + 10; 748 | else if((temp[j] >= 'a') && (temp[j] <= 'f')) 749 | tmp = temp[j] - 'a' + 10; 750 | 751 | result = result * 16 + tmp; 752 | } 753 | 754 | if(i < length)buffer[i] = result; 755 | } 756 | } 757 | 758 | memset(_buffer, 0, BEFFER_LENGTH_MAX); 759 | 760 | return number; 761 | } 762 | 763 | void LoRaWanClass::loraDebug(void) 764 | { 765 | if(SerialUSB.available())SerialLoRa.write(SerialUSB.read()); 766 | if(SerialLoRa.available())SerialUSB.write(SerialLoRa.read()); 767 | } 768 | 769 | #if _DEBUG_SERIAL_ 770 | void LoRaWanClass::loraDebugPrint(unsigned char timeout) 771 | { 772 | unsigned long timerStart, timerEnd; 773 | 774 | timerStart = millis(); 775 | 776 | while(1) 777 | { 778 | while(SerialLoRa.available()) SerialUSB.write(SerialLoRa.read()); 779 | 780 | timerEnd = millis(); 781 | if(timerEnd - timerStart > 1000 * timeout)break; 782 | } 783 | } 784 | #endif 785 | 786 | void LoRaWanClass::sendCommand(char *command) 787 | { 788 | SerialLoRa.print(command); 789 | } 790 | 791 | short LoRaWanClass::readBuffer(char *buffer, int length, unsigned char timeout) 792 | { 793 | short i = 0; 794 | unsigned long timerStart, timerEnd; 795 | 796 | timerStart = millis(); 797 | 798 | while(1) 799 | { 800 | if(i < length) 801 | { 802 | while(SerialLoRa.available()) 803 | { 804 | char c = SerialLoRa.read(); 805 | buffer[i ++] = c; 806 | } 807 | } 808 | 809 | timerEnd = millis(); 810 | if(timerEnd - timerStart > 1000 * timeout)break; 811 | } 812 | 813 | return i; 814 | } 815 | 816 | bool LoRaWanClass::waitForResponse(char* response, unsigned char timeout) 817 | { 818 | short len = strlen(response); 819 | short sum = 0; 820 | unsigned long timerStart,timerEnd; 821 | 822 | timerStart = millis(); 823 | 824 | while(1) 825 | { 826 | if(SerialLoRa.available()) 827 | { 828 | char c = SerialLoRa.read(); 829 | 830 | sum = (c == response[sum]) ? sum + 1 : 0; 831 | if(sum == len)break; 832 | } 833 | 834 | timerEnd = millis(); 835 | if(timerEnd - timerStart > 1000 * timeout)return false; 836 | } 837 | 838 | return true; 839 | } 840 | 841 | bool LoRaWanClass::sendCommandAndWaitForResponse(char* command, char *response, unsigned char timeout) 842 | { 843 | sendCommand(command); 844 | 845 | return waitForResponse(response, timeout); 846 | } 847 | 848 | 849 | LoRaWanClass lora; 850 | -------------------------------------------------------------------------------- /networktester/src/LoRaWan.h: -------------------------------------------------------------------------------- 1 | /* 2 | LoRaWAN.h 3 | 2013 Copyright (c) Seeed Technology Inc. All right reserved. 4 | 5 | Author: Wayne Weng 6 | Date: 2016-10-17 7 | 8 | add rgb backlight fucnction @ 2013-10-15 9 | 10 | The MIT License (MIT) 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy 13 | of this software and associated documentation files (the "Software"), to deal 14 | in the Software without restriction, including without limitation the rights 15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the Software is 17 | furnished to do so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be included in 20 | all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | THE SOFTWARE.1 USA 29 | */ 30 | 31 | #ifndef _LORAWAN_H_ 32 | #define _LORAWAN_H_ 33 | 34 | 35 | #include 36 | 37 | #define SerialUSB Serial 38 | #define SerialLoRa Serial1 //used with GPS module and changed UART port for LoRaWAN module 39 | //#define SerialLoRa Serial2 //used this if unchanged UART port for LoRaWAN module and without GPS module 40 | 41 | #define _DEBUG_SERIAL_ 1 42 | #define DEFAULT_TIMEOUT 5 // second 43 | #define DEFAULT_TIMEWAIT 100 // millisecond 44 | #define DEFAULT_DEBUGTIME 1 // second 45 | 46 | #define BEFFER_LENGTH_MAX 256 47 | 48 | 49 | enum _class_type_t { CLASS_A = 0, CLASS_C }; 50 | enum _physical_type_t { EU434 = 0, EU868, US915, US915HYBRID, AU915, AU915OLD, CN470, CN779, AS923, KR920, IN865 }; 51 | enum _device_mode_t { LWABP = 0, LWOTAA, TEST }; 52 | enum _otaa_join_cmd_t { JOIN = 0, FORCE }; 53 | enum _window_delay_t { RECEIVE_DELAY1 = 0, RECEIVE_DELAY2, JOIN_ACCEPT_DELAY1, JOIN_ACCEPT_DELAY2 }; 54 | enum _band_width_t { BW125 = 125, BW250 = 250, BW500 = 500 }; 55 | enum _spreading_factor_t { SF12 = 12, SF11 = 11, SF10 = 10, SF9 = 9, SF8 = 8, SF7 = 7 }; 56 | enum _data_rate_t { DR0 = 0, DR1, DR2, DR3, DR4, DR5, DR6, DR7, DR8, DR9, DR10, DR11, DR12, DR13, DR14, DR15 }; 57 | 58 | 59 | /***************************************************************** 60 | Type DataRate Configuration BitRate| TxPower Configuration 61 | EU434 0 SF12/125 kHz 250 | 0 10dBm 62 | 1 SF11/125 kHz 440 | 1 7 dBm 63 | 2 SF10/125 kHz 980 | 2 4 dBm 64 | 3 SF9 /125 kHz 1760 | 3 1 dBm 65 | 4 SF8 /125 kHz 3125 | 4 -2dBm 66 | 5 SF7 /125 kHz 5470 | 5 -5dBm 67 | 6 SF7 /250 kHz 11000 | 6:15 RFU 68 | 7 FSK:50 kbps 50000 | 69 | 8:15 RFU | 70 | ****************************************************************** 71 | Type DataRate Configuration BitRate| TxPower Configuration 72 | EU868 0 SF12/125 kHz 250 | 0 20dBm 73 | 1 SF11/125 kHz 440 | 1 14dBm 74 | 2 SF10/125 kHz 980 | 2 11dBm 75 | 3 SF9 /125 kHz 1760 | 3 8 dBm 76 | 4 SF8 /125 kHz 3125 | 4 5 dBm 77 | 5 SF7 /125 kHz 5470 | 5 2 dBm 78 | 6 SF7 /250 kHz 11000 | 6:15 RFU 79 | 7 FSK:50 kbps 50000 | 80 | 8:15 RFU | 81 | ****************************************************************** 82 | Type DataRate Configuration BitRate| TxPower Configuration 83 | US915 0 SF10/125 kHz 980 | 0 30dBm 84 | 1 SF9 /125 kHz 1760 | 1 28dBm 85 | 2 SF8 /125 kHz 3125 | 2 26dBm 86 | 3 SF7 /125 kHz 5470 | 3 24dBm 87 | 4 SF8 /500 kHz 12500 | 4 22dBm 88 | 5:7 RFU | 5 20dBm 89 | 8 SF12/500 kHz 980 | 6 18dBm 90 | 9 SF11/500 kHz 1760 | 7 16dBm 91 | 10 SF10/500 kHz 3900 | 8 14dBm 92 | 11 SF9 /500 kHz 7000 | 9 12dBm 93 | 12 SF8 /500 kHz 12500 | 10 10dBm 94 | 13 SF7 /500 kHz 21900 | 11:15 RFU 95 | 14:15 RFU | 96 | *******************************************************************/ 97 | 98 | 99 | class LoRaWanClass 100 | { 101 | public: 102 | 103 | bool dutycycle = false; 104 | 105 | LoRaWanClass(void); 106 | 107 | /** 108 | * \brief Initialize the conmunication interface 109 | * 110 | * \return Return null 111 | */ 112 | void init(void); 113 | 114 | /** 115 | * \brief Read the version from device 116 | * 117 | * \param [in] *buffer The output data cache 118 | * \param [in] length The length of data cache 119 | * \param [in] timeout The over time of read 120 | * 121 | * \return Return null. 122 | */ 123 | void getVersion(char *buffer, short length, unsigned char timeout = DEFAULT_TIMEOUT); 124 | 125 | /** 126 | * \brief Read the ID from device 127 | * 128 | * \param [in] *buffer The output data cache 129 | * \param [in] length The length of data cache 130 | * \param [in] timeout The over time of read 131 | * 132 | * \return Return null. 133 | */ 134 | void getId(char *buffer, short length, unsigned char timeout = DEFAULT_TIMEOUT); 135 | 136 | /** 137 | * \brief Set the ID 138 | * 139 | * \param [in] *DevAddr The end-device address 140 | * \param [in] *DevEUI The end-device identifier 141 | * \param [in] *AppEUI The application identifier 142 | * 143 | * \return Return null. 144 | */ 145 | void setId(char *DevAddr, char *DevEUI, char *AppEUI); 146 | 147 | /** 148 | * \brief Set the key 149 | * 150 | * \param [in] *NwkSKey The network session key 151 | * \param [in] *AppSKey The application session key 152 | * \param [in] *AppKey The Application key 153 | * 154 | * \return Return null. 155 | */ 156 | void setKey(char *NwkSKey, char *AppSKey, char *AppKey); 157 | 158 | /** 159 | * \brief Set the data rate 160 | * 161 | * \param [in] dataRate The date rate of encoding 162 | * \param [in] physicalType The type of ISM 163 | * 164 | * \return Return null. 165 | */ 166 | void setDataRate(_data_rate_t dataRate = DR0, _physical_type_t physicalType = EU434); 167 | 168 | /** 169 | * \brief ON/OFF adaptive data rate mode 170 | * 171 | * \param [in] command The date rate of encoding 172 | * 173 | * \return Return null. 174 | */ 175 | void setAdaptiveDataRate(bool command); 176 | 177 | /** 178 | * \brief Set the output power 179 | * 180 | * \param [in] power The output power value 181 | * 182 | * \return Return null. 183 | */ 184 | void setPower(short power); 185 | 186 | /** 187 | * \brief Set the port number 188 | * 189 | * \param [in] port The port number, range from 1 to 255 190 | * 191 | * \return Return null. 192 | */ 193 | void setPort(int port); 194 | 195 | /** 196 | * \brief Set the channel parameter 197 | * 198 | * \param [in] channel The channel number, range from 0 to 71 199 | * \param [in] frequency The frequency value 200 | * 201 | * \return Return null. 202 | */ 203 | void setChannel(unsigned char channel, float frequency); 204 | /** 205 | * \brief Set the channel parameter 206 | * 207 | * \param [in] channel The channel number, range from 0 to 71 208 | * \param [in] frequency The frequency value. Set frequecy zero to disable one channel 209 | * \param [in] dataRata The date rate of channel 210 | * 211 | * \return Return null. 212 | */ 213 | void setChannel(unsigned char channel, float frequency, _data_rate_t dataRata); 214 | /** 215 | * \brief Set the channel parameter 216 | * 217 | * \param [in] channel The channel number, range from 0 to 71 218 | * \param [in] frequency The frequency value 219 | * \param [in] dataRataMin The minimum date rate of channel 220 | * \param [in] dataRataMax The maximum date rate of channel 221 | * 222 | * \return Return null. 223 | */ 224 | void setChannel(unsigned char channel, float frequency, _data_rate_t dataRataMin, _data_rate_t dataRataMax); 225 | 226 | /** 227 | * \brief Transfer the data 228 | * 229 | * \param [in] *buffer The transfer data cache 230 | * \param [in] timeout The over time of transfer 231 | * 232 | * \return Return bool. Ture : transfer done, false : transfer failed 233 | */ 234 | bool transferPacket(char *buffer, unsigned char timeout = DEFAULT_TIMEOUT); 235 | /** 236 | * \brief Transfer the data 237 | * 238 | * \param [in] *buffer The transfer data cache 239 | * \param [in] length The length of data cache 240 | * \param [in] timeout The over time of transfer 241 | * 242 | * \return Return bool. Ture : transfer done, false : transfer failed 243 | */ 244 | bool transferPacket(unsigned char *buffer, int length, unsigned char timeout = DEFAULT_TIMEOUT); 245 | /** 246 | * \brief Transfer the packet data 247 | * 248 | * \param [in] *buffer The transfer data cache 249 | * \param [in] timeout The over time of transfer 250 | * 251 | * \return Return bool. Ture : Confirmed ACK, false : Confirmed NOT ACK 252 | */ 253 | bool transferPacketWithConfirmed(char *buffer, unsigned char timeout = DEFAULT_TIMEOUT); 254 | /** 255 | * \brief Transfer the data 256 | * 257 | * \param [in] *buffer The transfer data cache 258 | * \param [in] length The length of data cache 259 | * \param [in] timeout The over time of transfer 260 | * 261 | * \return Return bool. Ture : Confirmed ACK, false : Confirmed NOT ACK 262 | */ 263 | bool transferPacketWithConfirmed(unsigned char *buffer, int length, unsigned char timeout = DEFAULT_TIMEOUT); 264 | 265 | bool transferPacketLinkCheckReq(unsigned char timeout = DEFAULT_TIMEOUT); 266 | 267 | /** 268 | * \brief Receive the data 269 | * 270 | * \param [in] *buffer The receive data cache 271 | * \param [in] length The length of data cache 272 | * \param [in] *rssi The RSSI cache 273 | * 274 | * \return Return Receive data number 275 | */ 276 | short receivePacket(char *buffer, int length, short *rssi, float *snr, short *gwcnt); 277 | 278 | /** 279 | * \brief Transfer the proprietary data 280 | * 281 | * \param [in] *buffer The transfer data cache 282 | * \param [in] timeout The over time of transfer 283 | * 284 | * \return Return bool. Ture : transfer done, false : transfer failed 285 | */ 286 | bool transferProprietaryPacket(char *buffer, unsigned char timeout = DEFAULT_TIMEOUT); 287 | /** 288 | * \brief Transfer the proprietary data 289 | * 290 | * \param [in] *buffer The transfer data cache 291 | * \param [in] length The length of data cache 292 | * \param [in] timeout The over time of transfer 293 | * 294 | * \return Return bool. Ture : transfer done, false : transfer failed 295 | */ 296 | bool transferProprietaryPacket(unsigned char *buffer, unsigned char length, unsigned char timeout = DEFAULT_TIMEOUT); 297 | 298 | /** 299 | * \brief Set device mode 300 | * 301 | * \param [in] mode The mode of device 302 | * 303 | * \return Return null 304 | */ 305 | void setDeviceMode(_device_mode_t mode); 306 | 307 | /** 308 | * \brief Set device join a network 309 | * 310 | * \param [in] command The type of join 311 | * \param [in] timeout The over time of join 312 | * 313 | * \return Return bool. True : join OK, false : join NOT OK 314 | */ 315 | bool setOTAAJoin(_otaa_join_cmd_t command, unsigned char timeout = DEFAULT_TIMEOUT); 316 | 317 | /** 318 | * \brief Set message unconfirmed repeat time 319 | * 320 | * \param [in] time The repeat time, range from 1 to 15 321 | * 322 | * \return Return null 323 | */ 324 | void setUnconfirmedMessageRepeatTime(unsigned char time); 325 | 326 | /** 327 | * \brief Set message retry times time 328 | * 329 | * \param [in] time The retry time, range from 0 to 254 330 | * 331 | * \return Return null 332 | */ 333 | void setConfirmedMessageRetryTime(unsigned char time); 334 | 335 | /** 336 | * \brief ON/OFF receice window 1 337 | * 338 | * \param [in] command The true : ON, false OFF 339 | * 340 | * \return Return null 341 | */ 342 | void setReceiveWindowFirst(bool command); 343 | /** 344 | * \brief Set receice window 1 channel mapping 345 | * 346 | * \param [in] channel The channel number, range from 0 to 71 347 | * \param [in] frequency The frequency value of channel 348 | * 349 | * \return Return null 350 | */ 351 | void setReceiveWindowFirst(unsigned char channel, float frequency); 352 | 353 | /** 354 | * \brief Set receice window 2 channel mapping 355 | * 356 | * \param [in] frequency The frequency value of channel 357 | * \param [in] dataRate The date rate value 358 | * 359 | * \return Return null 360 | */ 361 | void setReceiveWindowSecond(float frequency, _data_rate_t dataRate); 362 | 363 | /** 364 | * \brief Set receice window 2 channel mapping 365 | * 366 | * \param [in] frequency The frequency value of channel 367 | * \param [in] spreadingFactor The spreading factor value 368 | * \param [in] bandwidth The band width value 369 | * 370 | * \return Return null 371 | */ 372 | void setReceiveWindowSecond(float frequency, _spreading_factor_t spreadingFactor, _band_width_t bandwidth); 373 | 374 | /** 375 | * \brief ON/OFF duty cycle limitation 376 | * 377 | * \param [in] command The true : ON, false OFF 378 | * 379 | * \return Return null 380 | */ 381 | void setDutyCycle(bool command); 382 | 383 | /** 384 | * \brief ON/OFF join duty cycle limitation 385 | * 386 | * \param [in] command The true : ON, false OFF 387 | * 388 | * \return Return null 389 | */ 390 | void setJoinDutyCycle(bool command); 391 | 392 | /** 393 | * \brief Set receice window delay 394 | * 395 | * \param [in] command The delay type 396 | * \param [in] _delay The delay value(millisecond) 397 | * 398 | * \return Return null 399 | */ 400 | void setReceiveWindowDelay(_window_delay_t command, unsigned short _delay); 401 | 402 | /** 403 | * \brief Set LoRaWAN class type 404 | * 405 | * \param [in] type The class type 406 | * 407 | * \return Return null 408 | */ 409 | void setClassType(_class_type_t type); 410 | 411 | /** 412 | * \brief Set device into low power mode 413 | * 414 | * \return Return null 415 | */ 416 | void setDeviceLowPower(void); 417 | 418 | /** 419 | * \brief Send Ping command 420 | * 421 | * \return Return null 422 | */ 423 | void sendDevicePing(void); 424 | 425 | /** 426 | * \brief Reset device 427 | * 428 | * \return Return null 429 | */ 430 | void setDeviceReset(void); 431 | 432 | /** 433 | * \brief Setup device default 434 | * 435 | * \return Return null 436 | */ 437 | void setDeviceDefault(void); 438 | 439 | /** 440 | * \brief Initialize device into P2P mode 441 | * 442 | * \param [in] frequency The ISM frequency value 443 | * \param [in] spreadingFactor The spreading factor value 444 | * \param [in] bandwidth The band width value 445 | * \param [in] txPreamble The transfer packet preamble number 446 | * \param [in] rxPreamble The receive packet preamble number 447 | * \param [in] power The output power 448 | * 449 | * \return Return null 450 | */ 451 | void initP2PMode(unsigned short frequency = 433, _spreading_factor_t spreadingFactor = SF12, _band_width_t bandwidth = BW125, 452 | unsigned char txPreamble = 8, unsigned char rxPreamble = 8, short power = 20); 453 | 454 | /** 455 | * \brief Transfer the data 456 | * 457 | * \param [in] *buffer The transfer data cache 458 | * 459 | * \return Return bool. Ture : transfer done, false : transfer failed 460 | */ 461 | bool transferPacketP2PMode(char *buffer, unsigned char timeout = DEFAULT_TIMEOUT); 462 | /** 463 | * \brief Transfer the data 464 | * 465 | * \param [in] *buffer The transfer data cache 466 | * \param [in] length The length of data cache 467 | * 468 | * \return Return bool. Ture : transfer done, false : transfer failed 469 | */ 470 | bool transferPacketP2PMode(unsigned char *buffer, unsigned char length, unsigned char timeout = DEFAULT_TIMEOUT); 471 | /** 472 | * \brief Receive the data 473 | * 474 | * \param [in] *buffer The receive data cache 475 | * \param [in] length The length of data cache 476 | * \param [in] *rssi The RSSI cache 477 | * \param [in] timeout The over time of receive 478 | * 479 | * \return Return Receive data number 480 | */ 481 | short receivePacketP2PMode(unsigned char *buffer, int length, short *rssi, unsigned char timeout = DEFAULT_TIMEOUT); 482 | 483 | /** 484 | * \brief LoRaWAN raw data 485 | * 486 | * \return Return null 487 | */ 488 | void loraDebug(void); 489 | #if _DEBUG_SERIAL_ 490 | void loraDebugPrint(unsigned char timeout); 491 | #endif 492 | 493 | 494 | 495 | private: 496 | void sendCommand(char *command); 497 | short readBuffer(char* buffer, int length, unsigned char timeout = DEFAULT_TIMEOUT); 498 | bool waitForResponse(char* response, unsigned char timeout = DEFAULT_TIMEOUT); 499 | bool sendCommandAndWaitForResponse(char* command, char *response, unsigned char timeout = DEFAULT_TIMEOUT); 500 | 501 | char _buffer[256]; 502 | 503 | }; 504 | 505 | 506 | extern LoRaWanClass lora; 507 | 508 | 509 | #endif 510 | --------------------------------------------------------------------------------