diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b44111..c8d6482 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Single Channel LoRaWAN Gateway -Version 4.0.7, July 22, 2017 +Version 5.0.2, November 18, 2017 Author: M. Westenberg (mw12554@hotmail.com) Copyright: M. Westenberg (mw12554@hotmail.com) @@ -16,6 +16,27 @@ Maintained by Maarten Westenberg (mw12554@hotmail.com) # Release Notes +New Features in version 5.0.5 (Feb 2, 2018) +- Change timer functions to now() and secons instead of millis() as the latter one overflows once +every 50 days. +- Add more debug information +- Simplified and enhanced the State Machine function + +New features in version 5.0.4 (January 1, 2018) +- Cleanup of the State machine +- Separate file for oLED work, support for 1.3" SH3006 chips based oLED. +- Still not supported: Multi Frequency works, but with loss of #packages, + and some packages are recognizeg at the wrong frequency (but since they are so close that could happen). +- In-line documenattion cleaned up + +New features in version 5.0.1 (November 18, 2017) +- Changed the state machine to run in user space only +- No Watchdog Resets anymore +- For each SF, percentage of such packages received of total packages +- OTAA and downlink work (again) although not always +- Nober of packages per hour displayed in webserver +- All Serial communication only when DUSB==1 is defined at compile time + New features in version 4.0.9 (August 11, 2017) - This release contains updates for memory leaks in several Gateway files diff --git a/ESP-sc-gway/ESP-sc-gway.h b/ESP-sc-gway/ESP-sc-gway.h index 1e13fbb..877ae7b 100644 --- a/ESP-sc-gway/ESP-sc-gway.h +++ b/ESP-sc-gway/ESP-sc-gway.h @@ -1,7 +1,7 @@ // 1-channel LoRa Gateway for ESP8266 // Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266 -// Version 5.0.1 -// Date: 2017-11-15 +// Version 5.0.6 +// Date: 2018-02-12 // // Based on work done by Thomas Telkamp for Raspberry PI 1ch gateway and many others. // Contibutions of Dorijan Morelj and Andreas Spies for OLED support. @@ -19,7 +19,7 @@ // // ---------------------------------------------------------------------------------------- -#define VERSION "V.5.0.2.H; 171118a nOLED, 15/10" +#define VERSION "V.5.0.6.H; 170212a" // This value of DEBUG determines whether some parts of code get compiled. // Also this is the initial value of debug parameter. @@ -33,6 +33,11 @@ // If set to 2 it will also print interrupt messages (not recommended) #define DUSB 1 +// Define whether we should do a formatting of SPIFFS when starting the gateway +// This is usually a good idea if the webserver is interrupted halfway a writing +// operation. +#define SPIFF_FORMAT 0 + // The spreading factor is the most important parameter to set for a single channel // gateway. It specifies the speed/datarate in which the gateway and node communicate. // As the name says, in principle the single channel gateway listens to one channel/frequency @@ -72,7 +77,8 @@ // If your pin definitions are different, update the loraModem.h file to reflect these settings. // 1: HALLARD // 2: COMRESULT pin out -// 3: Other, define your own in loraModem.h +// 3: ESP32 pin out +// 4: Other, define your own in loraModem.h #define _PIN_OUT 1 // Gather statistics on sensor and Wifi status @@ -102,8 +108,9 @@ // Define the name of the accesspoint if the gateway is in accesspoint mode (is // getting WiFi SSID and password using WiFiManager) +// NOTE: Change the password for your OWN environment to be secure #define AP_NAME "ESP8266-Gway-Things4U" -#define AP_PASSWD "MyPw01!" +#define AP_PASSWD "ttnAutoPw" // Defines whether the gateway will also report sensor/status value on MQTT @@ -120,11 +127,13 @@ // Will we use Mutex or not? // +SPI is input for SPI, SPO is output for SPI -#define MUTEX 1 -#define MUTEX_SPI 0 -#define MUTEX_SPO 0 -// Protect the interrupt module -#define MUTEX_INT 0 +#define MUTEX 0 + +// Define if OLED Display is connected to i2c +// OLED==1; 0.9 Oled Screen based on SSD1306 +// OLED==2; 1"3 Oled screens for Wemos, 128x64 SH1106 +#define OLED 2 // Make define 1 on line if you have an OLED display connected + // Define whether we want to manage the gateway over UDP (next to management // thru webinterface). @@ -146,7 +155,7 @@ #define _LOCUDPPORT 1700 // UDP port of gateway! Often 1700 or 1701 is used for upstream comms // Timing -#define _MSG_INTERVAL 15 +#define _MSG_INTERVAL 15 // Reset timer in seconds #define _PULL_INTERVAL 55 // PULL_DATA messages to server to get downstream in milliseconds #define _STAT_INTERVAL 120 // Send a 'stat' message to server #define _NTP_INTERVAL 3600 // How often do we want time NTP synchronization @@ -163,25 +172,25 @@ // Port is UDP port in this program // // Default for testing: Switch off -//#define _THINGPORT 1700 // dash.westenberg.org:8057 -//#define _THINGSERVER "yourServer.com" // Server URL of the LoRa-udp.js handler +#define _THINGPORT 57084 // dash.westenberg.org:8057 +#define _THINGSERVER "westenberg.org" // Server URL of the LoRa-udp.js handler // Gateway Ident definitions -#define _DESCRIPTION "My ESP Gateway" -#define _EMAIL "whoami@hotmail.com" +#define _DESCRIPTION "ESP Gateway" +#define _EMAIL "mw12554@hotmail.com" #define _PLATFORM "ESP8266" -#define _LAT 52.00 -#define _LON 5.900 -#define _ALT 00 +#define _LAT 52 +#define _LON 5.9 +#define _ALT 1 // ntp #define NTP_TIMESERVER "nl.pool.ntp.org" // Country and region specific #define NTP_TIMEZONES 1 // How far is our Timezone from UTC (excl daylight saving/summer time) -#define SECS_PER_HOUR 3600 +#define SECS_IN_HOUR 3600 #define NTP_INTR 0 // Do NTP processing with interrupts or in loop(); #if GATEWAYNODE==1 -#define _DEVADDR { 0x26, 0x00, 0x00 0x00 } +#define _DEVADDR { 0x26, 0x01, 0x00, 0x00 } #define _APPSKEY { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } #define _NWKSKEY { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } #define _SENSOR_INTERVAL 300 @@ -194,12 +203,6 @@ // Serial Port speed #define _BAUDRATE 115200 // Works for debug messages to serial momitor -// if OLED Display is connected to i2c -#define OLED 0 // Make define 1 on line if you have an OLED display connected -#define OLED_SCL 5 // GPIO5 / D1 -#define OLED_SDA 4 // GPIO4 / D2 -#define OLED_ADDR 0x3C // Default 0x3C for 0.9", for 1.3" it is 0x78 - // Wifi definitions // WPA is an array with SSID and password records. Set WPA size to number of entries in array // When using the WiFiManager, we will overwrite the first entry with the @@ -218,8 +221,8 @@ struct wpas { // wpas wpa[] = { { "" , "" }, // Reserved for WiFi Manager - { "aap", "aapPasswd" }, - { "ape", "apePasswd" } + { "aap", "noot" }, + { "mies", "teun" } }; // For asserting and testing the following defines are used. diff --git a/ESP-sc-gway/ESP-sc-gway.ino b/ESP-sc-gway/ESP-sc-gway.ino index 3e24d35..ba5bb02 100644 --- a/ESP-sc-gway/ESP-sc-gway.ino +++ b/ESP-sc-gway/ESP-sc-gway.ino @@ -1,7 +1,7 @@ // 1-channel LoRa Gateway for ESP8266 // Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266 -// Version 5.0.1 -// Date: 2017-11-15 +// Version 5.0.6 +// Date: 2018-02-12 // Author: Maarten Westenberg (mw12554@hotmail.com) // // based on work done by Thomas Telkamp for Raspberry PI 1-ch gateway @@ -62,8 +62,10 @@ extern "C" { #include // See lib directory #endif +// Local include files #include "loraModem.h" #include "loraFiles.h" +#include "oLED.h" #if WIFIMANAGER>0 @@ -83,10 +85,7 @@ extern "C" { #include "AES-128_V10.h" #endif -#if OLED==1 -#include "SSD1306.h" -SSD1306 display(OLED_ADDR, OLED_SDA, OLED_SCL);// i2c ADDR & SDA, SCL on wemos -#endif + int debug=1; // Debug level! 0 is no msgs, 1 normal, 2 extensive @@ -102,14 +101,13 @@ byte currentMode = 0x81; //char b64[256]; bool sx1272 = true; // Actually we use sx1276/RFM95 -uint32_t cp_nb_rx_rcv; -uint32_t cp_nb_rx_ok; -uint32_t cp_nb_rx_bad; -uint32_t cp_nb_rx_nocrc; +uint32_t cp_nb_rx_rcv; // Number of messages received by gateway +uint32_t cp_nb_rx_ok; // Number of messages received OK +uint32_t cp_nb_rx_bad; // Number of messages received bad +uint32_t cp_nb_rx_nocrc; // Number of messages without CRC uint32_t cp_up_pkt_fwd; uint8_t MAC_array[6]; -//char MAC_char[19]; // ---------------------------------------------------------------------------- // @@ -138,7 +136,12 @@ IPAddress ttnServer; // IP Address of thethingsnetwork server IPAddress thingServer; WiFiUDP Udp; -uint32_t stattime = 0; // last time we sent a stat message to server + +time_t startTime = 0; // The time in seconds since 1970 that the server started + // be aware that UTP time has to succeed for meaningful values. + // We use this variable since millis() is reset every 50 days... + +uint32_t statTime = 0; // last time we sent a stat message to server uint32_t pulltime = 0; // last time we sent a pull_data request to server uint32_t lastTmst = 0; #if A_SERVER==1 @@ -322,7 +325,7 @@ int sendNtpRequest(IPAddress timeServerIP) { if (!sendUdp( (IPAddress) timeServerIP, (int) 123, packetBuffer, NTP_PACKET_SIZE)) { gwayConfig.ntpErr++; - gwayConfig.ntpErrTime = millis(); + gwayConfig.ntpErrTime = now(); return(0); } return(1); @@ -364,9 +367,9 @@ time_t getNtpTime() secs = packetBuffer[40] << 24; secs |= packetBuffer[41] << 16; secs |= packetBuffer[42] << 8; - secs |= packetBuffer[43];\ + secs |= packetBuffer[43]; // UTC is 1 TimeZone correction when no daylight saving time - return(secs - 2208988800UL + NTP_TIMEZONES * SECS_PER_HOUR); + return(secs - 2208988800UL + NTP_TIMEZONES * SECS_IN_HOUR); } Udp.flush(); } @@ -378,8 +381,12 @@ time_t getNtpTime() // If we are here, we could not read the time from internet // So increase the counter gwayConfig.ntpErr++; - gwayConfig.ntpErrTime = millis(); - if (debug>0) Serial.println(F("getNtpTime:: read failed")); + gwayConfig.ntpErrTime = now(); +#if DUSB>=1 + if (debug>0) { + Serial.println(F("getNtpTime:: read failed")); + } +#endif return(0); // return 0 if unable to get the time } @@ -477,7 +484,7 @@ int WlanWriteWpa( char* ssid, char *pass) { // It is a matter of returning to the main loop() asap and make sure in next loop // the reconnect is done first thing. // Parameters: -// int maxTtry: Number of reties we do: +// int maxTry: Number of reties we do: // 0: Try forever. Which is normally what we want except for Setup maybe // 1: Try once and if unsuccessful return(0); // x: Try x times @@ -539,9 +546,16 @@ int WlanConnect(int maxTry) { } #if DUSB>=1 - if (i>0) { - Serial.print(F("WLAN reconnected")); - Serial.println(); + if (i==0) { + if (debug>=3) { + Serial.print(F("WLAN connected")); + Serial.println(); + } + return(1); + } + else { + Serial.print(F("WLAN retry=")); + Serial.println(i); } #endif @@ -643,7 +657,7 @@ int readUdp(int packetSize) } #endif gwayConfig.ntpErr++; - gwayConfig.ntpErrTime = millis(); + gwayConfig.ntpErrTime = now(); return(0); } @@ -1093,16 +1107,12 @@ void setup() { delay(100); Serial.flush(); delay(500); -#if MUTEX_SPI==1 - CreateMutux(&inSPI); -#endif -#if MUTEX_SPO==1 - CreateMutux(&inSPO); -#endif -#if MUTEX_INT==1 - CreateMutux(&inIntr); -#endif + if (SPIFFS.begin()) Serial.println(F("SPIFFS loaded success")); + +#if SPIFF_FORMAT>=1 + SPIFFS.format(); // Normally disabled. Enable only when SPIFFS corrupt +#endif Serial.print(F("Assert=")); #if defined CFG_noassert @@ -1111,14 +1121,8 @@ void setup() { Serial.println(F("Do Asserts")); #endif -#if OLED==1 - // Initialising the UI will init the display too. - display.init(); - display.flipScreenVertically(); - display.setFont(ArialMT_Plain_24); - display.setTextAlignment(TEXT_ALIGN_LEFT); - display.drawString(0, 24, "STARTING"); - display.display(); +#if OLED>=1 + init_oLED(); #endif delay(500); @@ -1227,9 +1231,14 @@ void setup() { #endif // Set the NTP Time + // As long as the time has not been set we try to set the time. #if NTP_INTR==1 setupTime(); // Set NTP time host and interval #else + // If not using the standard libraries, do a manual setting + // of the time. This meyhod works more reliable than the + // interrupt driven method. + //setTime((time_t)getNtpTime()); while (timeStatus() == timeNotSet) { Serial.println(F("setupTime:: Time not set (yet)")); @@ -1238,12 +1247,14 @@ void setup() { newTime = (time_t)getNtpTime(); if (newTime != 0) setTime(newTime); } + // When we are here we succeeded in getting the time + startTime = now(); // Time in seconds Serial.print("Time: "); printTime(); Serial.println(); writeGwayCfg(CONFIGFILE ); Serial.println(F("Gateway configuration saved")); -#endif +#endif //NTP_INTR #if A_SERVER==1 // Setup the webserver @@ -1264,6 +1275,7 @@ void setup() { _state = S_RX; rxLoraModem(); } + LoraUp.payLoad[0]= 0; LoraUp.payLength = 0; // Init the length to 0 // init interrupt handlers, which are shared for GPIO15 / D8, @@ -1283,12 +1295,9 @@ void setup() { writeConfig( CONFIGFILE, &gwayConfig); // Write config // activate OLED display -#if OLED==1 - // Initialising the UI will init the display too. - display.clear(); - display.setFont(ArialMT_Plain_24); - display.drawString(0, 24, "READY"); - display.display(); +#if OLED>=1 + acti_oLED(); + addr_oLED(); #endif Serial.println(F("--------------------------------------")); @@ -1313,32 +1322,36 @@ void setup() { // ---------------------------------------------------------------------------- void loop () { - uint32_t nowSeconds; + uint32_t uSeconds; // micro seconds int packetSize; - - nowTime = micros(); - nowSeconds = (uint32_t) millis() /1000; + uint32_t nowSeconds = now(); // check for event value, which means that an interrupt has arrived. // In this case we handle the interrupt ( e.g. message received) // in userspace in loop(). // - if (_event != 0x00) { - stateMachine(); // start the state machine - _event = 0; // reset value - return; // Restart loop + while (_event != 0x00) { // + stateMachine(); // start the state machine } - // After a quiet period, make sure we reinit the modem. - // XXX Still have to measure quiet period in stat[0]; - // For the moment we use msgTime - if ( (((nowTime - statr[0].tmst) / 1000000) > _MSG_INTERVAL ) && - (msgTime < statr[0].tmst)) { + // After a quiet period, make sure we reinit the modem and state machine. + // The interval is in seconds (about 10 seconds) as this re-init + // is a heavy operation. + // SO it will kick in if there are not many messages for the gatway. + // Note: Be carefull that it does not happen too often in normal operation. + // + if ( ((nowSeconds - statr[0].tmst) > _MSG_INTERVAL ) && + (msgTime < statr[0].tmst) ) + { #if DUSB>=1 - Serial.print('r'); + if (debug>=1) Serial.println("REINIT"); #endif - initLoraModem(); - if (_cad) { + initLoraModem(); // XXX 26/12/2017 + if (_hop) { + _state = S_SCAN; + hop(); + } + else if (_cad) { _state = S_SCAN; cadScanner(); } @@ -1348,7 +1361,7 @@ void loop () } writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); writeRegister(REG_IRQ_FLAGS, 0xFF); // Reset all interrupt flags - msgTime = nowTime; + msgTime = nowSeconds; } #if A_OTA==1 @@ -1373,7 +1386,7 @@ void loop () // We will not read Udp in this loop cycle then if (WlanConnect(1) < 0) { #if DUSB>=1 - Serial.print(F("loop: ERROR reconnect WLAN")); + Serial.println(F("loop: ERROR reconnect WLAN")); #endif yield(); return; // Exit loop if no WLAN connected @@ -1382,11 +1395,11 @@ void loop () // So if we are connected // Receive UDP PUSH_ACK messages from server. (*2, par. 3.3) // This is important since the TTN broker will return confirmation - // messages on UDP for every message sent by the gateway. So we have to consume them.. + // messages on UDP for every message sent by the gateway. So we have to consume them. // As we do not know when the server will respond, we test in every loop. // else { - while( (packetSize = Udp.parsePacket()) > 0) { // Length of UDP message waiting + while( (packetSize = Udp.parsePacket()) > 0) { #if DUSB>=2 Serial.println(F("loop:: readUdp calling")); #endif @@ -1400,55 +1413,19 @@ void loop () } // Now we know we succesfull received message from host else { - _event=1; // Could be done double if more messages received + _event=1; // Could be done double if more messages received } //yield(); } + //yield(); // XXX 26/12/2017 } - yield(); - - - // The next section is emergency only. If posible we hop() in the state machine. - // If hopping is enabled, and by lack of timer, we hop() - // XXX Experimental, 2.5 ms between hops max - // - - if ((_hop) && (((long)(nowTime - hopTime)) > 7500)) { - - if ((_state == S_SCAN) && (sf==SF12)) { -#if DUSB>=1 - if (debug>=1) Serial.println(F("loop:: hop")); -#endif - hop(); - } - - // XXX section below does not work without further work. It is the section with the MOST - // influence on the HOP mode of operation (which is somewhat unexpected) - // If we keep staying in another state, reset - else if (((long)(nowTime - hopTime)) > 100000) { - - _state= S_RX; - rxLoraModem(); - - hop(); - - if (_cad) { - _state= S_SCAN; - cadScanner(); - } - } - else if (debug>=3) { Serial.print(F(" state=")); Serial.println(_state); } - inHop = false; // Reset re-entrane protection of HOP - yield(); - } - - + yield(); // XXX 26/12/2017 // stat PUSH_DATA message (*2, par. 4) // - if ((nowSeconds - stattime) >= _STAT_INTERVAL) { // Wake up every xx seconds + if ((nowSeconds - statTime) >= _STAT_INTERVAL) { // Wake up every xx seconds #if DUSB>=1 if (debug>=2) { Serial.print(F("STAT <")); @@ -1481,7 +1458,7 @@ void loop () } } #endif - stattime = nowSeconds; + statTime = nowSeconds; } yield(); @@ -1489,7 +1466,7 @@ void loop () // send PULL_DATA message (*2, par. 4) // - nowSeconds = (uint32_t) millis() /1000; + nowSeconds = now(); if ((nowSeconds - pulltime) >= _PULL_INTERVAL) { // Wake up every xx seconds #if DUSB>=1 if (debug>=1) { @@ -1525,8 +1502,8 @@ void loop () #if NTP_INTR==0 // Set the time in a manual way. Do not use setSyncProvider // as this function may collide with SPI and other interrupts - yield(); - nowSeconds = (uint32_t) millis() /1000; + yield(); // 26/12/2017 + nowSeconds = now(); if (nowSeconds - ntptimer >= _NTP_INTERVAL) { yield(); time_t newTime; @@ -1535,4 +1512,6 @@ void loop () ntptimer = nowSeconds; } #endif -} + + +}//loop diff --git a/ESP-sc-gway/_gatewayMgt.ino b/ESP-sc-gway/_gatewayMgt.ino index e81ba8d..49588fe 100644 --- a/ESP-sc-gway/_gatewayMgt.ino +++ b/ESP-sc-gway/_gatewayMgt.ino @@ -1,7 +1,7 @@ // 1-channel LoRa Gateway for ESP8266 // Copyright (c) 2016, 2017 Maarten Westenberg -// Version 5.0.1 -// Date: 2017-11-15 +// Version 5.0.6 +// Date: 2018-02-12 // // Based on work done by Thomas Telkamp for Raspberry PI 1ch gateway // and many others. diff --git a/ESP-sc-gway/_loraFiles.ino b/ESP-sc-gway/_loraFiles.ino index 295e6f0..87f083e 100644 --- a/ESP-sc-gway/_loraFiles.ino +++ b/ESP-sc-gway/_loraFiles.ino @@ -1,7 +1,7 @@ // 1-channel LoRa Gateway for ESP8266 // Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266 -// Version 5.0.1 -// Date: 2017-11-15 +// Version 5.0.6 +// Date: 2018-02-12 // // based on work done by Thomas Telkamp for Raspberry PI 1ch gateway // and many others. @@ -35,13 +35,14 @@ // ---------------------------------------------------------------------------- int readConfig(const char *fn, struct espGwayConfig *c) { - Serial.println(F("readConfig:: Starting")); + Serial.print(F("readConfig:: Starting ")); if (!SPIFFS.exists(fn)) { Serial.print(F("ERROR:: readConfig, file does not exist ")); Serial.println(fn); return(-1); } + File f = SPIFFS.open(fn, "r"); if (!f) { Serial.println(F("ERROR:: SPIFFS open failed")); @@ -50,6 +51,12 @@ int readConfig(const char *fn, struct espGwayConfig *c) { while (f.available()) { +#if DUSB>=1 + if (debug>=0) { + Serial.print('.'); + } +#endif + String id =f.readStringUntil('='); String val=f.readStringUntil('\n'); @@ -127,6 +134,12 @@ int readConfig(const char *fn, struct espGwayConfig *c) { } } f.close(); +#if DUSB>=1 + if (debug>=0) { + Serial.println('#'); + } +#endif + Serial.println(); return(1); } diff --git a/ESP-sc-gway/_loraModem.ino b/ESP-sc-gway/_loraModem.ino index 833a75c..6be2d4b 100644 --- a/ESP-sc-gway/_loraModem.ino +++ b/ESP-sc-gway/_loraModem.ino @@ -1,7 +1,7 @@ // 1-channel LoRa Gateway for ESP8266 // Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266 -// Version 5.0.1 -// Date: 2017-11-15 +// Version 5.0.6 +// Date: 2018-02-12 // // based on work done by Thomas Telkamp for Raspberry PI 1ch gateway // and many others. @@ -17,27 +17,6 @@ // and transmit packages/messages. // ======================================================================================== // -// STATE MACHINE -// The program uses the following state machine (in _state), all states -// are done in interrupt routine, only the follow-up of S-RXDONE is done -// in the main loop() program. This is because otherwise the interrupt processing -// would take too long to finish -// -// S-INIT=0, The commands in this state are executed only once -// - Goto S_SCAN -// S-SCAN, CadScanner() part -// - upon CDDONE (int0) got S_CAD -// S-CAD, -// - Upon CDDECT (int1) goto S_RX, -// - Upon CDDONE (int0) goto S_SCAN -// S-RX, Received CDDECT so message detected, RX cycle started. -// - Upon RXDONE (int0) read ok goto S_RX or S_SCAN, -// - upon RXTOUT (int1) goto S_SCAN -// S-RXDONE, Read the buffer -// - Wait for reading in loop() -// - Upon message send to server goto S_SCAN -// S-TX Transmitting a message -// - Upon TX goto S_SCAN // // // SPI AND INTERRUPTS @@ -122,36 +101,13 @@ SPISettings readSettings(SPISPEED, MSBFIRST, SPI_MODE0); uint8_t readRegister(uint8_t addr) { - //noInterrupts(); // XXX -#if MUTEX_SPI==1 - if(!GetMutex(&mutexSPI)) { -#if DUSB>=1 - if (debug>=0) { - gwayConfig.reents++; - Serial.print(F("readRegister:: read reentry")); - printTime(); - Serial.println(); - if (debug>=2) Serial.flush(); - delayMicroseconds(50); - initLoraModem(); - } -#endif - return 0; - } -#endif - SPI.beginTransaction(readSettings); - + SPI.beginTransaction(readSettings); digitalWrite(pins.ss, LOW); // Select Receiver SPI.transfer(addr & 0x7F); uint8_t res = (uint8_t) SPI.transfer(0x00); digitalWrite(pins.ss, HIGH); // Unselect Receiver SPI.endTransaction(); - -#if MUTEX_SPI==1 - ReleaseMutex(&mutexSPI); -#endif - //interrupts(); // XXX return((uint8_t) res); } @@ -171,24 +127,6 @@ SPISettings writeSettings(SPISPEED, MSBFIRST, SPI_MODE0); void writeRegister(uint8_t addr, uint8_t value) { - //noInterrupts(); // XXX -#if MUTEX_SPO==1 - if(!GetMutex(&mutexSPI)) { -#if DUSB>=1 - if (debug>=0) { - gwayConfig.reents++; - Serial.print(F("writeRegister:: write reentry")); - printTime(); - Serial.println(); - delayMicroseconds(50); - initLoraModem(); - if (debug>=2) Serial.flush(); - } -#endif - return; - } -#endif - SPI.beginTransaction(writeSettings); digitalWrite(pins.ss, LOW); // Select Receiver @@ -199,11 +137,6 @@ void writeRegister(uint8_t addr, uint8_t value) digitalWrite(pins.ss, HIGH); // Unselect Receiver SPI.endTransaction(); - -#if MUTEX_SPO==1 - ReleaseMutex(&mutexSPI); -#endif - //interrupts(); } @@ -220,22 +153,6 @@ void writeRegister(uint8_t addr, uint8_t value) void writeBuffer(uint8_t addr, uint8_t *buf, uint8_t len) { //noInterrupts(); // XXX -#if MUTEX_SPO==1 - if(!GetMutex(&mutexSPI)) { -#if DUSB>=1 - if (debug>=0) { - gwayConfig.reents++; - Serial.print(F("writeBuffer:: write reentry")); - printTime(); - Serial.println(); - delayMicroseconds(50); - initLoraModem(); - if (debug>=2) Serial.flush(); - } -#endif - return; - } -#endif SPI.beginTransaction(writeSettings); digitalWrite(pins.ss, LOW); // Select Receiver @@ -247,11 +164,6 @@ void writeBuffer(uint8_t addr, uint8_t *buf, uint8_t len) digitalWrite(pins.ss, HIGH); // Unselect Receiver SPI.endTransaction(); - -#if MUTEX_SPI==1 - ReleaseMutex(&mutexSPI); -#endif - //interrupts(); } // ---------------------------------------------------------------------------- @@ -283,9 +195,16 @@ void setRate(uint8_t sf, uint8_t crc) mc2= ((sf<<4) | crc) % 0xFF; // SX1276_MC1_BW_250 0x80 | SX1276_MC1_CR_4_5 0x02 | SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01 if (sf == SF11 || sf == SF12) { mc1= 0x0B; } - } + } + + // For sx1276 chips is the CRC ON is else { - mc1= 0x72; // SX1276_MC1_BW_125==0x70 | SX1276_MC1_CR_4_5==0x02 + if (sf==SF8) { + mc1= 0x78; // SX1276_MC1_BW_125==0x70 | SX1276_MC1_CR_4_8==0x08 + } + else { + mc1= 0x72; // SX1276_MC1_BW_125==0x70 | SX1276_MC1_CR_4_5==0x02 + } mc2= ((sf<<4) | crc) & 0xFF; // crc is 0x00 or 0x04==SX1276_MC2_RX_PAYLOAD_CRCON mc3= 0x04; // 0x04; SX1276_MC3_AGCAUTO if (sf == SF11 || sf == SF12) { mc3|= 0x08; } // 0x08 | 0x04 @@ -387,24 +306,30 @@ void opmode(uint8_t mode) // receiver frequency is determined by ifreq index like so: freqs[ifreq] // ---------------------------------------------------------------------------- void hop() { - // If we are already in a hop function, do not proceed - if (!inHop) { - - inHop=true; - opmode(OPMODE_STANDBY); - ifreq = (ifreq + 1)% NUM_HOPS ; + + ifreq = (ifreq + 1) % NUM_HOPS ; // Increment the freq round robin setFreq(freqs[ifreq]); - hopTime = micros(); // record HOP moment - opmode(OPMODE_CAD); - inHop=false; - } -#if DUSB>=2 - else { - if (debug >= 3) { - Serial.println(F("Hop:: Re-entrance try")); + + sf = SF7; // Set the sf to SF7 + setRate(sf, 0x40); + + // Be aware that micros() has increased significantly from calling + // the hop function until printed below + // +#if DUSB>=1 + if (debug>=2) { + Serial.print(F("hop:: freq=")); + Serial.print(ifreq); + Serial.print(F(", sf=")); + Serial.print(sf); + Serial.print(F(", tim=")); + Serial.print(micros() - hopTime); + Serial.println(); } - } #endif + // Remember the last time we hop + hopTime = micros(); // At what time did we hop + } @@ -422,57 +347,108 @@ uint8_t receivePkt(uint8_t *payload) cp_nb_rx_rcv++; // Receive statistics counter + uint8_t crcUsed = readRegister(REG_HOP_CHANNEL); + if (crcUsed & 0x40) { +#if DUSB>=1 + if (debug>=2) Serial.println(F("receivePkt:: CRC used")); +#endif + } + // Check for payload IRQ_LORA_CRCERR_MASK=0x20 set - if((irqflags & IRQ_LORA_CRCERR_MASK) == IRQ_LORA_CRCERR_MASK) + if (irqflags & IRQ_LORA_CRCERR_MASK) { -#if DUSB>=2 - Serial.println(F("CRC")); +#if DUSB>=1 + if (debug>=0) Serial.println(F("CRC")); #endif // Reset CRC flag 0x20 - writeRegister(REG_IRQ_FLAGS, (uint8_t)(IRQ_LORA_CRCERR_MASK || IRQ_LORA_RXDONE_MASK)); // 0x12; clear CRC (== 0x20) flag + writeRegister(REG_IRQ_FLAGS, (uint8_t)(IRQ_LORA_CRCERR_MASK | IRQ_LORA_RXDONE_MASK)); // 0x12; clear CRC (== 0x20) flag return 0; } + + // Is header OK? + else if ((irqflags & IRQ_LORA_HEADER_MASK) == false) + { +#if DUSB>=1 + if (debug>=0) Serial.println(F("HEADER")); +#endif + // Reset VALID-HEADER flag 0x10 + writeRegister(REG_IRQ_FLAGS, (uint8_t)(IRQ_LORA_HEADER_MASK | IRQ_LORA_RXDONE_MASK)); // 0x12; clear HEADER (== 0x10) flag + return 0; + } + + // If there are no error messages, read the buffer from the FIFO + // This means "Set FifoAddrPtr to FifoRxBaseAddr" else { - cp_nb_rx_ok++; // Receive OK statistics counter + cp_nb_rx_ok++; // Receive OK statistics counter - uint8_t currentAddr = readRegister(REG_FIFO_RX_CURRENT_ADDR); // 0x10 - uint8_t receivedCount = readRegister(REG_RX_NB_BYTES); // 0x13; How many bytes were read -#if DUSB>=2 + if (readRegister(REG_FIFO_RX_CURRENT_ADDR) != readRegister(REG_FIFO_RX_BASE_AD)) { + if (debug>=1) { + Serial.print(F("RX BASE <")); + Serial.print(readRegister(REG_FIFO_RX_BASE_AD)); + Serial.print(F("> != RX CURRENT <")); + Serial.print(readRegister(REG_FIFO_RX_CURRENT_ADDR)); + Serial.print(F(">")); + Serial.println(); + } + } + + //uint8_t currentAddr = readRegister(REG_FIFO_RX_CURRENT_ADDR); // 0x10 + uint8_t currentAddr = readRegister(REG_FIFO_RX_BASE_AD); // 0x0F + uint8_t receivedCount = readRegister(REG_RX_NB_BYTES); // 0x13; How many bytes were read +#if DUSB>=1 if ((debug>=0) && (currentAddr > 64)) { Serial.print(F("receivePkt:: Rx addr>64")); Serial.println(currentAddr); - if (debug>=2) Serial.flush(); } #endif writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) currentAddr); // 0x0D - if (receivedCount > 64) { -#if DUSB>=2 - Serial.print(F("receivePkt:: receivedCount=")); - Serial.println(receivedCount); - if (debug>=2) Serial.flush(); + if (receivedCount > PAYLOAD_LENGTH) { +#if DUSB>=1 + if (debug>=0) { + Serial.print(F("receivePkt:: receivedCount=")); + Serial.println(receivedCount); + } #endif - receivedCount=64; + receivedCount=PAYLOAD_LENGTH; } -#if DUSB>=2 - else if (debug>=2) { - Serial.print(F("ReceivePkt:: addr=")); + + for(int i=0; i < receivedCount; i++) + { + payload[i] = readRegister(REG_FIFO); // 0x00, FIFO will auto shift register + } + + writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset ALL interrupts +#if DUSB>=1 + if (debug>=0) { + Serial.print(F("receivePkt:: freq=")); + Serial.print(ifreq); + Serial.print(F(", sf=")); + Serial.print(sf); + Serial.print(F(", lora=")); + if (payload[4]<0x10) Serial.print('0'); Serial.print(payload[4], HEX); + if (payload[3]<0x10) Serial.print('0'); Serial.print(payload[3], HEX); + if (payload[2]<0x10) Serial.print('0'); Serial.print(payload[2], HEX); + if (payload[1]<0x10) Serial.print('0'); Serial.print(payload[1], HEX); + Serial.print(F(", flags=")); + Serial.print(irqflags,HEX); + Serial.print(F(", addr=")); Serial.print(currentAddr); Serial.print(F(", len=")); Serial.println(receivedCount); if (debug>=2) Serial.flush(); } #endif - for(int i = 0; i < receivedCount; i++) - { - payload[i] = readRegister(REG_FIFO); // 0x00 - } - - writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset ALL interrupts return(receivedCount); } - - writeRegister(REG_IRQ_FLAGS, (uint8_t) IRQ_LORA_RXDONE_MASK); // 0x12; Clear RxDone IRQ_LORA_RXDONE_MASK + + + + writeRegister(REG_IRQ_FLAGS, (uint8_t) ( + IRQ_LORA_RXDONE_MASK | + IRQ_LORA_RXTOUT_MASK | + IRQ_LORA_HEADER_MASK | + IRQ_LORA_CRCERR_MASK)); // 0x12; Clear RxDone IRQ_LORA_RXDONE_MASK return 0; } @@ -498,10 +474,7 @@ bool sendPkt(uint8_t *payLoad, uint8_t payLength) writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_TX_BASE_AD)); // 0x0D, 0x0E writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) payLength); // 0x22 - //for(int i = 0; i < payLength; i++) - //{ - // writeRegister(REG_FIFO, (uint8_t) payLoad[i]); // 0x00 - //} + payLoad[payLength] = 0x00; writeBuffer(REG_FIFO, (uint8_t *) payLoad, payLength); return true; } @@ -526,7 +499,7 @@ bool sendPkt(uint8_t *payLoad, uint8_t payLength) void loraWait(uint32_t tmst) { - uint32_t startTime = micros(); // Start of the loraWait function + uint32_t startMics = micros(); // Start of the loraWait function tmst += txDelay; uint32_t waitTime = tmst - micros(); @@ -542,11 +515,11 @@ void loraWait(uint32_t tmst) if (debug >=1) { Serial.print(F("start: ")); - Serial.print(startTime); + Serial.print(startMics); Serial.print(F(", end: ")); Serial.print(tmst); Serial.print(F(", waited: ")); - Serial.print(tmst - startTime); + Serial.print(tmst - startMics); Serial.print(F(", delay=")); Serial.print(txDelay); Serial.println(); @@ -624,7 +597,11 @@ void txLoraModem(uint8_t *payLoad, uint8_t payLength, uint32_t tmst, uint8_t sfT writeRegister(REG_INVERTIQ, (uint8_t) iiq); // 0x33, (0x27 or 0x40) // 8. set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP (or lesss for 1ch gateway) - writeRegister(REG_DIO_MAPPING_1, (uint8_t)(MAP_DIO0_LORA_TXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP)); + writeRegister(REG_DIO_MAPPING_1, (uint8_t)( + MAP_DIO0_LORA_TXDONE | + MAP_DIO1_LORA_NOP | + MAP_DIO2_LORA_NOP | + MAP_DIO3_LORA_CRC)); // 9. clear all radio IRQ flags writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); @@ -642,16 +619,15 @@ void txLoraModem(uint8_t *payLoad, uint8_t payLength, uint32_t tmst, uint8_t sfT loraWait(tmst); //Set the base addres of the transmit buffer in FIFO - writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_TX_BASE_AD)); // set 0x0D to 0x0F (contains 0x80); + writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_TX_BASE_AD)); // set 0x0D to 0x0F (contains 0x80); //For TX we have to set the PAYLOAD_LENGTH - writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) payLength); // set 0x22, max 0x40==64Byte long + writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) payLength); // set 0x22, max 0x40==64Byte long //For TX we have to set the MAX_PAYLOAD_LENGTH - writeRegister(REG_MAX_PAYLOAD_LENGTH, (uint8_t) MAX_PAYLOAD_LENGTH); // set 0x22, max 0x40==64Byte long + writeRegister(REG_MAX_PAYLOAD_LENGTH, (uint8_t) MAX_PAYLOAD_LENGTH); // set 0x22, max 0x40==64Byte long // Reset the IRQ register - //writeRegister(REG_IRQ_FLAGS, 0xFF); // set 0x12 to 0xFF writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Clear the mask writeRegister(REG_IRQ_FLAGS, (uint8_t) IRQ_LORA_TXDONE_MASK);// set 0x12 to 0x08 @@ -703,42 +679,42 @@ void rxLoraModem() //writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) PAYLOAD_LENGTH); // set 0x22, 0x40==64Byte long // Set CRC Protection or MAX payload protection - //writeRegister(REG_MAX_PAYLOAD_LENGTH, (uint8_t) MAX_PAYLOAD_LENGTH); // set 0x23 to 0x80==128 + //writeRegister(REG_MAX_PAYLOAD_LENGTH, (uint8_t) MAX_PAYLOAD_LENGTH); // set 0x23 to 0x80==128 //Set the start address for the FiFO (Which should be 0) - writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_RX_BASE_AD)); // set 0x0D to 0x0F (contains 0x00); + writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_RX_BASE_AD)); // set 0x0D to 0x0F (contains 0x00); - // Low Noise Amplifier used in receiver - writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN); // 0x0C, 0x23 + // Low Noise Amplifier used in receiver + writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN); // 0x0C, 0x23 // Accept no interrupts except RXDONE, RXTOUT en RXCRC - writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(IRQ_LORA_RXDONE_MASK | IRQ_LORA_RXTOUT_MASK | IRQ_LORA_CRCERR_MASK)); + writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~( + IRQ_LORA_RXDONE_MASK | + IRQ_LORA_RXTOUT_MASK | + IRQ_LORA_HEADER_MASK | + IRQ_LORA_CRCERR_MASK)); // set frequency hopping if (_hop) { -#if DUSB>=1 - if (debug >= 1) { - Serial.print(F("rxLoraModem:: Hop, channel=")); - Serial.println(ifreq); - } -#endif - writeRegister(REG_HOP_PERIOD, 0x01); // 0x24, 0x01 was 0xFF - // Set RXDONE interrupt to dio0 - writeRegister(REG_DIO_MAPPING_1, (uint8_t)(MAP_DIO0_LORA_RXDONE | MAP_DIO1_LORA_RXTOUT | MAP_DIO1_LORA_FCC)); + //writeRegister(REG_HOP_PERIOD, 0x01); // 0x24, 0x01 was 0xFF + writeRegister(REG_HOP_PERIOD,0x00); // 0x24, 0x00 was 0xFF } else { writeRegister(REG_HOP_PERIOD,0x00); // 0x24, 0x00 was 0xFF - // Set RXDONE interrupt to dio0 - writeRegister(REG_DIO_MAPPING_1, (uint8_t)(MAP_DIO0_LORA_RXDONE | MAP_DIO1_LORA_RXTOUT)); } - + // Set RXDONE interrupt to dio0 + writeRegister(REG_DIO_MAPPING_1, (uint8_t)( + MAP_DIO0_LORA_RXDONE | + MAP_DIO1_LORA_RXTOUT | + MAP_DIO2_LORA_NOP | + MAP_DIO3_LORA_CRC)); // Set the opmode to either single or continuous receive. The first is used when // every message can come on a different SF, the second when we have fixed SF if (_cad) { // cad Scanner setup, set _state to S_RX // Set Single Receive Mode, goes in STANDBY mode after receipt - _state = S_RX; + _state= S_RX; opmode(OPMODE_RX_SINGLE); // 0x80 | 0x06 (listen one message) } else { @@ -771,20 +747,11 @@ void cadScanner() // 2. Put the radio in sleep mode opmode(OPMODE_STANDBY); // Was old value - //opmode(OPMODE_SLEEP); // set 0x01 to 0x00 // As we can come back from S_TX with other frequencies and SF // reset both to good values for cadScanner - // 3. Set frequency based on value in freq // XXX New, might be needed when receiving down -#if DUSB>=2 - if ((debug>=1) && (ifreq>9)) { - Serial.print(F("cadScanner:: E Freq=")); - Serial.println(ifreq); - if (debug>=2) Serial.flush(); - ifreq=0; - } -#endif + // 3. Set frequency based on value in ifreq // XXX New, might be needed when receiving down setFreq(freqs[ifreq]); // For every time we stat the scanner, we set the SF to the begin value @@ -797,11 +764,18 @@ void cadScanner() writeRegister(REG_SYNC_WORD, (uint8_t) 0x34); // set reg 0x39 to 0x34 // Set the interrupts we want top listen to - writeRegister(REG_DIO_MAPPING_1, - (uint8_t)(MAP_DIO0_LORA_CADDONE | MAP_DIO1_LORA_CADDETECT | MAP_DIO2_LORA_NOP | MAP_DIO3_LORA_NOP)); + writeRegister(REG_DIO_MAPPING_1, (uint8_t)( + MAP_DIO0_LORA_CADDONE | + MAP_DIO1_LORA_CADDETECT | + MAP_DIO2_LORA_NOP | + MAP_DIO3_LORA_CRC )); // Set the mask for interrupts (we do not want to listen to) except for - writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(IRQ_LORA_CDDONE_MASK | IRQ_LORA_CDDETD_MASK | IRQ_LORA_CRCERR_MASK)); + writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~( + IRQ_LORA_CDDONE_MASK | + IRQ_LORA_CDDETD_MASK | + IRQ_LORA_CRCERR_MASK | + IRQ_LORA_HEADER_MASK)); // Set the opMode to CAD opmode(OPMODE_CAD); @@ -812,6 +786,7 @@ void cadScanner() // If we are here. we either might have set the SF or we have a timeout in which // case the receive is started just as normal. return; + }// cadScanner @@ -844,7 +819,7 @@ void initLoraModem() setRate(sf, 0x04); // Low Noise Amplifier used in receiver - writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN); // 0x0C, 0x23 + writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN); // 0x0C, 0x23 uint8_t version = readRegister(REG_VERSION); // Read the LoRa chip version id if (version == 0x22) { @@ -863,35 +838,32 @@ void initLoraModem() sx1272 = false; } else { -#if DUSB>=2 +#if DUSB>=1 Serial.print(F("Unknown transceiver=")); Serial.println(version,HEX); #endif die(""); } // 7. set sync word - writeRegister(REG_SYNC_WORD, (uint8_t) 0x34); // set 0x39 to 0x34 LORA_MAC_PREAMBLE + writeRegister(REG_SYNC_WORD, (uint8_t) 0x34); // set 0x39 to 0x34 LORA_MAC_PREAMBLE // prevent node to node communication writeRegister(REG_INVERTIQ,0x27); // 0x33, 0x27; to reset from TX // Max Payload length is dependent on 256 byte buffer. At startup TX starts at // 0x80 and RX at 0x00. RX therefore maximized at 128 Bytes - writeRegister(REG_MAX_PAYLOAD_LENGTH,MAX_PAYLOAD_LENGTH); // set 0x23 to 0x80==128 bytes - writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH); // 0x22, 0x40==64Byte long + writeRegister(REG_MAX_PAYLOAD_LENGTH,MAX_PAYLOAD_LENGTH); // set 0x23 to 0x80==128 bytes + writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH); // 0x22, 0x40==64Byte long - writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_BASE_AD)); // set reg 0x0D to 0x0F - writeRegister(REG_HOP_PERIOD,0x00); // reg 0x24, set to 0x00 was 0xFF - if (_hop) { - writeRegister(REG_HOP_CHANNEL,0x00); // reg 0x1C, set to 0x00 - } + writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_RX_BASE_AD)); // set reg 0x0D to 0x0F + writeRegister(REG_HOP_PERIOD,0x00); // reg 0x24, set to 0x00 // 5. Config PA Ramp up time // set reg 0x0A writeRegister(REG_PARAMP, (readRegister(REG_PARAMP) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec // Set 0x4D PADAC for SX1276 ; XXX register is 0x5a for sx1272 writeRegister(REG_PADAC_SX1276, 0x84); // set 0x4D (PADAC) to 0x84 - //writeRegister(REG_PADAC, readRegister(REG_PADAC)|0x4); + //writeRegister(REG_PADAC, readRegister(REG_PADAC) | 0x4); // Reset interrupt Mask, enable all interrupts writeRegister(REG_IRQ_FLAGS_MASK, 0x00); @@ -901,507 +873,6 @@ void initLoraModem() }// initLoraModem - -// ---------------------------------------------------------------------------- -// stateMachine handler of the state machine. -// We use ONE state machine for all kind of interrupts. This assures that we take -// the correct action upon receiving an interrupt. -// -// STATE MACHINE -// The program uses the following state machine (in _state), all states -// are done in interrupt routine, only the follow-up of S-RXDONE is done -// in the main loop() program. This is because otherwise the interrupt processing -// would take too long to finish -// -// S-INIT=0, The commands in this state are executed only once -// - Goto S_SCAN -// -// S-SCAN, CadScanner() part -// - Upon CDDECT (int1) goto S_RX, -// - upon CDDONE (int0) got S_CAD, walk through all SF until CDDETD -// - Else stay in SCAN state -// -// S-CAD, -// - Upon CDDECT (int1) goto S_RX, -// - Upon CDDONE (int0) goto S_SCAN, start with SF7 recognition again -// -// S-RX, Received CDDECT so message detected, RX cycle started. -// - Upon RXDONE (int0) package read. If read ok continue, -// - upon RXTOUT (int1) goto S_SCAN -// -// S-RXDONE Buffer reading is complete -// - Wait for reading in loop() -// - Upon message send to server goto S_SCAN -// -// S-TX Transmitting a message -// - Upon TX goto S_SCAN -// -// S-TXDONE Transmission complete by loop() now again in interrupt -// - Set the Mask -// - reset the Flags -// - Goto either SCAN or RX -// -// This interrupt routine has been kept as simple and short as possible. -// If we receive an interrupt that does not below to a _state then print error. -// -// NOTE: We may clear the interrupt but leave the flag for the moment. -// The eventHandler should take care of repairing flags between interrupts. -// ---------------------------------------------------------------------------- - -void stateMachine() -{ - // Make a sort of mutex by using a volatile variable -#if MUTEX_INT==1 - if(!GetMutex(&inIntr)) { -#if DUSB>=1 - if (debug>=0) { - Serial.println(F("eInt:: Mutex")); - if (debug>=2) Serial.flush(); - } -#endif - return; - } -#endif - // Determine what interrupt flags are set - uint8_t flags = readRegister(REG_IRQ_FLAGS); - uint8_t mask = readRegister(REG_IRQ_FLAGS_MASK); - uint8_t intr = flags & ( ~ mask ); // Only react on non masked interrupts - uint8_t rssi; - - if (intr == 0x00) { -#if DUSB>=1 - // Something strange has happened: There has been an event - // and we do not have a value for interrupt. - if (debug>=1) Serial.println(F("stateMachine:: NO intr")); -#endif - - // Mayby wait a little before resetting all/ -#if MUTEX_INT==1 - ReleaseMutex(&inIntr); -#endif - //_state = S_SCAN; - writeRegister(REG_IRQ_FLAGS, 0xFF ); // Clear ALL interrupts - _event = 0; - return; - } - - // Small state machine inside the interrupt handler - // as next actions are depending on the state we are in. - switch (_state) - { - - // -------------------------------------------------------------- - // If the state is init, we are starting up. - // The initLoraModem() function is already called ini setup(); - case S_INIT: -#if DUSB>=2 - if (debug >= 1) { - Serial.println(F("S_INIT")); - } -#endif - // new state, needed to startup the radio (to S_SCAN) - writeRegister(REG_IRQ_FLAGS, 0xFF ); // Clear ALL interrupts - break; - - - // -------------------------------------------------------------- - // In S_SCAN we measure a high RSSI this means that there (probably) is a message - // coming in at that freq. But not necessarily on the current SF. - // If so find the right SF with CDDETD. - case S_SCAN: - // - // Intr=IRQ_LORA_CDDETD_MASK - // We detected a message on this frequency and SF when scanning - // We clear both CDDETD and CDDONE and swich to reading state - // - if (intr & IRQ_LORA_CDDETD_MASK) { -#if DUSB>=2 - if (debug >=3) { - Serial.println(F("SCAN:: CADDETD, ")); - } -#endif - - _state = S_RX; // Set state to receiving - opmode(OPMODE_RX_SINGLE); // set reg 0x01 to 0x06 - - // Set RXDONE interrupt to dio0, RXTOUT to dio1 - writeRegister(REG_DIO_MAPPING_1, (MAP_DIO0_LORA_RXDONE | MAP_DIO1_LORA_RXTOUT)); - - // Since new state is S_RX, accept no interrupts except RXDONE or RXTOUT - writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(IRQ_LORA_RXDONE_MASK | IRQ_LORA_RXTOUT_MASK)); - - delayMicroseconds( RSSI_WAIT_DOWN ); // Wait some microseconds less - // Starting with version 5.0.1 the waittime is dependent on the SF - // So for SF12 we wait longer (2^7 == 128 uSec) and for SF7 4 uSec. - //delayMicroseconds( (0x01 << ((uint8_t)sf - 5 )) ); - rssi = readRegister(REG_RSSI); // Read the RSSI - _rssi = rssi; // Read the RSSI in the state variable - - writeRegister(REG_IRQ_FLAGS, 0xFF ); // reset all interrupt flags - }//if - - // CDDONE - // We received a CDDONE int telling us that we received a message on this - // frequency and possibly on one of its SF. - // If so, we switch to CAD state where we will only listen to CDDETD event. - // - else if (intr & IRQ_LORA_CDDONE_MASK) { - - opmode(OPMODE_CAD); - - rssi = readRegister(REG_RSSI); // Read the RSSI - - // We choose the generic RSSI as a sorting mechanism for packages/messages - // The pRSSI (package RSSI) is caluclated upon successful reception of message - // So we expect tht this value makes little sense for the moment with CDDONE. - // Set the rssi as low as the noise floor. Lower values are not recognized then. - // - if ( rssi > RSSI_LIMIT ) { // Is set to 37 (27/08/2017) - _state = S_CAD; // XXX invoke the interrupt handler again? - } - - // Clear the CADDONE flag - //writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDONE_MASK); - writeRegister(REG_IRQ_FLAGS, 0xFF); - } - - // If not CDDETC and not CDDONE and sf==12 we have to hop - else if ((_hop) && (sf==12)) { - hop(); - sf=(sf_t)7; - } - - // If we do not switch to S_CAD, we have to hop - // Instead of waiting for an interrupt we do this on timer basis (more regular). - else { - _state=S_SCAN; - cadScanner(); - writeRegister(REG_IRQ_FLAGS, 0xFF); - } - // else keep on scanning - break; // S_SCAN - - - // -------------------------------------------------------------- - // S_CAD: In CAD mode we scan every SF for high RSSI until we have a DETECT. - // Reason is the we received a CADDONE interrupt so we know a message is received - // on the frequency but may be on another SF. - // - // If message is of the right frequency and SF, IRQ_LORA_CDDETD_MSAK interrupt - // is raised, indicating that we can start beging reading the message from SPI. - // - // DIO0 interrupt IRQ_LORA_CDDONE_MASK in state S_CAD==2 means that we might have - // a lock on the Freq but not the right SF. So we increase the SF - // - case S_CAD: - - // Intr=IRQ_LORA_CDDETD_MASK - // We have to set the sf based on a strong RSSI for this channel - // - if (intr & IRQ_LORA_CDDETD_MASK) { - - _state = S_RX; // Set state to start receiving - opmode(OPMODE_RX_SINGLE); // set reg 0x01 to 0x06, initiate READ - - // Set RXDONE interrupt to dio0, RXTOUT to dio1 - writeRegister(REG_DIO_MAPPING_1, (MAP_DIO0_LORA_RXDONE | MAP_DIO1_LORA_RXTOUT)); - - // Accept no interrupts except RXDONE or RXTOUT - writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(IRQ_LORA_RXDONE_MASK | IRQ_LORA_RXTOUT_MASK)); - - delayMicroseconds( RSSI_WAIT_DOWN ); // Wait some microseconds less - //delayMicroseconds( (0x01 << ((uint8_t)sf - 5 )) ); - rssi = readRegister(REG_RSSI); // Read the RSSI - _rssi = rssi; // Read the RSSI in the state variable - - //writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDETD_MASK | IRQ_LORA_RXDONE_MASK); - writeRegister(REG_IRQ_FLAGS, 0xFF ); // reset all CAD Detect interrupt flags - }// CDDETD - - // Intr == CADDONE - // So we scan this SF and if not high enough ... next - // - else if (intr & IRQ_LORA_CDDONE_MASK) { - // If this is not SF12, increment the SF and try again - // We expect on other SF get CDDETD - // - if (((uint8_t)sf) < SF12) { - sf = (sf_t)((uint8_t)sf+1); // XXX This would mean SF7 never used - setRate(sf, 0x04); // Set SF with CRC==on - - opmode(OPMODE_CAD); // Scanning mode - - delayMicroseconds(RSSI_WAIT ); - rssi = readRegister(REG_RSSI); // Read the RSSI - - // reset interrupt flags for CAD Done - writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDONE_MASK | IRQ_LORA_CDDETD_MASK); - //writeRegister(REG_IRQ_FLAGS, 0xFF ); // XXX this will prevent the CDDETD from being read - - } - // If we reach SF12, we should go back to SCAN state - else { - if (_hop) { hop(); } // if HOP we start at the next frequency - _state = S_SCAN; - cadScanner(); // Which will reset SF to SF7 - // Reset the interrupts - writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDONE_MASK); - } - } //CADDONE - - // - // if this interrupt is not CDECT or CDDONE then the interrupt is - // is unknown in this state. So we clear interrupt and give a warning. - else { -#if DUSB>=2 - if (debug>=1) { - Serial.println(F("CAD:: Unknown interrupt")); - } -#endif - _state = S_SCAN; - cadScanner(); - writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset all interrupts - } - break; //S_CAD - - - // -------------------------------------------------------------- - // If we receive an interrupt on dio0 state==S_RX - // it should be a RxDone interrupt - // So we should handle the received message - case S_RX: - - if (intr & IRQ_LORA_RXDONE_MASK) { - - // We have to check for CRC error which will be visible AFTER RXDONE is set. - // CRC errors might indicate tha the reception is not OK. - // Could be CRC error or message too large. - // CRC error checking requires DIO3 - if (intr & IRQ_LORA_CRCERR_MASK) { -#if DUSB>=2 - Serial.println(F("CRC err")); - if (debug>=2) Serial.flush(); -#endif - if (_cad) { - _state = S_SCAN; - cadScanner(); - } - else { - _state = S_RX; - rxLoraModem(); - } - - writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask - // Reset interrupts - writeRegister(REG_IRQ_FLAGS, (uint8_t)(IRQ_LORA_CRCERR_MASK | IRQ_LORA_RXDONE_MASK | IRQ_LORA_RXTOUT_MASK)); - break; - } - - if((LoraUp.payLength = receivePkt(LoraUp.payLoad)) <= 0) { -#if DUSB>=1 - if (debug>=0) { - Serial.println(F("sMachine:: Error S-RX")); - } -#endif - } - - // Do all register processing in this section (interrupt) - uint8_t value = readRegister(REG_PKT_SNR_VALUE); // 0x19; - if( value & 0x80 ) { // The SNR sign bit is 1 - - value = ( ( ~value + 1 ) & 0xFF ) >> 2; // Invert and divide by 4 - LoraUp.snr = -value; - } - else { - // Divide by 4 - LoraUp.snr = ( value & 0xFF ) >> 2; - } - - LoraUp.prssi = readRegister(REG_PKT_RSSI); // read register 0x1A, packet rssi - - // Correction of RSSI value based on chip used. - if (sx1272) { // Is it a sx1272 radio? - LoraUp.rssicorr = 139; - } else { // Probably SX1276 or RFM95 - LoraUp.rssicorr = 157; - } - - LoraUp.sf = readRegister(REG_MODEM_CONFIG2) >> 4; - - if (receivePacket() <= 0) { // read is not successful -#if DUSB>=1 - Serial.println(F("sMach:: Error receivePacket")); -#endif - } -#if DUSB>=2 - else if (debug>=2) { - Serial.println(F("sMach:: receivePacket OK")); - } -#endif - - // Set the modem to receiving BEFORE going back to - // user space. - if (_cad) { - _state = S_SCAN; - cadScanner(); - } - else { - _state = S_RX; - rxLoraModem(); - } - - writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); - writeRegister(REG_IRQ_FLAGS, 0xFF); // Reset the interrupt mask - } - - // Receive message timeout - else if (intr & IRQ_LORA_RXTOUT_MASK) { - - // Set the modem for next receive action. This must be done before - // scanning takes place as we cannot do it once RXDETTD is set. - - if (_cad) { - // Set the state to CAD scanning - _state = S_SCAN; - cadScanner(); // Start the scanner after RXTOUT - } - else { - _state = S_RX; // XXX 170828, why? - rxLoraModem(); - } - writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00 ); - writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset all interrupts - } - - // The interrupt received is not RXDONE nor RXTOUT - // therefore we restart the scanning sequence (catch all) - else { -#if DUSB>=2 - if (debug >=3) { - Serial.println(F("S_RX:: No RXDONE/RXTOUT, ")); - } -#endif - initLoraModem(); // Reset all communication,3 - if (_cad) { - _state = S_SCAN; - cadScanner(); - } - else { - _state = S_RX; - rxLoraModem(); - } - writeRegister(REG_IRQ_FLAGS_MASK, 0x00); // Reset all masks - writeRegister(REG_IRQ_FLAGS, 0xFF); // Reset all interrupts - } - break; // S_RX - - - // -------------------------------------------------------------- - - // - case S_TX: - - // Initiate the transmission of the buffer (in Interrupt space) - // We react on ALL interrupts if we are in TX state. - - txLoraModem( - LoraDown.payLoad, - LoraDown.payLength, - LoraDown.tmst, - LoraDown.sfTx, - LoraDown.powe, - LoraDown.fff, - LoraDown.crc, - LoraDown.iiq - ); - -#if DUSB>=2 - if (debug>=0) { - Serial.println(F("S_TX, ")); - } -#endif - _state = S_TXDONE; - writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); - writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags - - break; // S_TX - - // --------------------------------------------------- - // AFter the transmission is completed by the hardware, - // the interrupt TXDONE is raised telling us that the tranmission - // was successful. - // If we receive an interrupt on dio0 _state==S_TX it is a TxDone interrupt - // Do nothing with the interrupt, it is just an indication. - // sendPacket switch back to scanner mode after transmission finished OK - case S_TXDONE: - if (intr & IRQ_LORA_TXDONE_MASK) { -#if DUSB>=1 - Serial.println(F("TXDONE interrupt")); -#endif - // After transmission reset to receiver - if (_cad) { - // Set the state to CAD scanning - _state = S_SCAN; - cadScanner(); // Start the scanner after TX cycle - } - else { - _state = S_RX; - rxLoraModem(); - } - - writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); - writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags -#if DUSB>=1 - if (debug>=1) { - Serial.println(F("TXDONE handled")); - if (debug>=2) Serial.flush(); - } -#endif - } - else { -#if DUSB>=1 - if (debug>=0) { - Serial.println(F("TXDONE unknown interrupt")); - if (debug>=2) Serial.flush(); - } -#endif - } - break; // S_TXDONE - - - // -------------------------------------------------------------- - // If _STATE is in undefined state - // If such a thing happens, we should re-init the interface and - // make sure that we pick up next interrupt - default: -#if DUSB>=2 - if (debug >= 2) { - Serial.print("E state="); - Serial.println(_state); - } -#endif - if (_cad) { - _state = S_SCAN; - cadScanner(); - } - else - { - _state = S_RX; - rxLoraModem(); - } - writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); - writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset all interrupts - break; - } - -#if MUTEX_INT==1 - ReleaseMutex(&inIntr); -#endif - _event = 0; - return; -} - - // ---------------------------------------------------------------------------- // Interrupt_0 Handler. // Both interrupts DIO0 and DIO1 are mapped on GPIO15. Se we have to look at @@ -1412,7 +883,7 @@ void stateMachine() // ---------------------------------------------------------------------------- void ICACHE_RAM_ATTR Interrupt_0() { - _event = 1; + _event=1; } @@ -1429,7 +900,7 @@ void ICACHE_RAM_ATTR Interrupt_0() // ---------------------------------------------------------------------------- void ICACHE_RAM_ATTR Interrupt_1() { - _event = 1; + _event=1; } // ---------------------------------------------------------------------------- @@ -1437,7 +908,7 @@ void ICACHE_RAM_ATTR Interrupt_1() // ---------------------------------------------------------------------------- void ICACHE_RAM_ATTR Interrupt_2() { - _event = 1; + _event=1; } diff --git a/ESP-sc-gway/_oLED.ino b/ESP-sc-gway/_oLED.ino new file mode 100644 index 0000000..9791dc1 --- /dev/null +++ b/ESP-sc-gway/_oLED.ino @@ -0,0 +1,80 @@ +// 1-channel LoRa Gateway for ESP8266 +// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266 +// Version 5.0.6 +// Date: 2018-02-12 +// +// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway +// and many others. +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License +// which accompanies this distribution, and is available at +// https://opensource.org/licenses/mit-license.php +// +// Author: Maarten Westenberg (mw12554@hotmail.com) +// +// This file contains the state machine code enabling to receive +// and transmit packages/messages. +// ======================================================================================== +// + +#if OLED>=1 + + + +void init_oLED() +{ + // Initialising the UI will init the display too. + display.init(); + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_24); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(0, 24, "STARTING"); + display.display(); +} + +// Activate the OLED +// +void acti_oLED() +{ + // Initialising the UI will init the display too. + display.clear(); +#if OLED==1 + display.setFont(ArialMT_Plain_24); + display.drawString(0, 24, "READY"); +#elif OLED==2 + display.setFont(ArialMT_Plain_16); + display.drawString(0, 24, "READY"); +#endif + + display.display(); +} + +// Print a message on the OLED. +// Note: The whole message must fit in the buffer +// +void msg_oLED(String tim, String sf) { + display.clear(); + display.setFont(ArialMT_Plain_16); + display.setTextAlignment(TEXT_ALIGN_LEFT); + + display.drawString(0, 48, "LEN: " ); +// display.drawString(40, 48, String((int)messageLength) ); + display.display(); + yield(); +} + + +// Print the OLED address in use +// +void addr_oLED() +{ + + Serial.print(F("OLED_ADDR=0x")); + Serial.println(OLED_ADDR, HEX); + +} + + + +#endif \ No newline at end of file diff --git a/ESP-sc-gway/_otaServer.ino b/ESP-sc-gway/_otaServer.ino index 7d5454f..7aa24cc 100644 --- a/ESP-sc-gway/_otaServer.ino +++ b/ESP-sc-gway/_otaServer.ino @@ -1,7 +1,7 @@ // 1-channel LoRa Gateway for ESP8266 // Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266 -// Version 5.0.1 -// Date: 2017-11-15 +// Version 5.0.6 +// Date: 2018-02-12 // // // All rights reserved. This program and the accompanying materials diff --git a/ESP-sc-gway/_repeater.ino b/ESP-sc-gway/_repeater.ino index f7ecfd6..ab1c32f 100644 --- a/ESP-sc-gway/_repeater.ino +++ b/ESP-sc-gway/_repeater.ino @@ -1,7 +1,7 @@ // 1-channel LoRa Gateway for ESP8266 // Copyright (c) 2016, 2017 Maarten Westenberg -// Verison 5.0.1 -// Date: 2017-11-15 +// Verison 5.0.6 +// Date: 2018-02-12 // // All rights reserved. This program and the accompanying materials // are made available under the terms of the MIT License diff --git a/ESP-sc-gway/_sensor.ino b/ESP-sc-gway/_sensor.ino index 78f1979..998631d 100644 --- a/ESP-sc-gway/_sensor.ino +++ b/ESP-sc-gway/_sensor.ino @@ -1,7 +1,7 @@ // 1-channel LoRa Gateway for ESP8266 // Copyright (c) 2016, 2017 Maarten Westenberg -// Verison 5.0.1 -// Date: 2017-11-15 +// Verison 5.0.6 +// Date: 2018-02-12 // // All rights reserved. This program and the accompanying materials // are made available under the terms of the MIT License @@ -471,8 +471,6 @@ int sensorPacket() { // if (( frameCount % 10)==0) writeGwayCfg(CONFIGFILE); - //yield(); // XXX Can we remove this here? - if (buff_index > 512) { if (debug>0) Serial.println(F("sensorPacket:: ERROR buffer size too large")); return(-1); diff --git a/ESP-sc-gway/_stateMachine.ino b/ESP-sc-gway/_stateMachine.ino new file mode 100644 index 0000000..298580e --- /dev/null +++ b/ESP-sc-gway/_stateMachine.ino @@ -0,0 +1,722 @@ +// 1-channel LoRa Gateway for ESP8266 +// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266 +// Version 5.0.6 +// Date: 2018-02-12 +// +// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway +// and many others. +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License +// which accompanies this distribution, and is available at +// https://opensource.org/licenses/mit-license.php +// +// Author: Maarten Westenberg (mw12554@hotmail.com) +// +// This file contains the state machine code enabling to receive +// and transmit packages/messages. +// ======================================================================================== +// + + +// ---------------------------------------------------------------------------- +// stateMachine handler of the state machine. +// We use ONE state machine for all kind of interrupts. This assures that we take +// the correct action upon receiving an interrupt. +// +// _event is the software interrupt: If set this function is executed from loop(), +// the function should itself take care of setting or resetting the variable. +// +// STATE MACHINE +// The program uses the following state machine (in _state), all states +// are done in interrupt routine, only the follow-up of S-RXDONE is done +// in the main loop() program. This is because otherwise the interrupt processing +// would take too long to finish +// +// So _state has one of the following state values: +// +// S-INIT=0, The commands in this state are executed only once +// - Goto S_SCAN +// +// S-SCAN, CadScanner() part +// - Upon CDDECT (int1) goto S_RX, +// - upon CDDONE (int0) goto S_CAD, walk through all SF until CDDETD +// - Else stay in SCAN state +// +// S-CAD, +// - Upon CDDECT (int1) goto S_RX, +// - Upon CDDONE (int0) goto S_SCAN, start with SF7 recognition again +// +// S-RX, Received CDDECT so message detected, RX cycle started. +// - Upon RXDONE (int0) package read. If read ok continue to read message +// - upon RXTOUT (int1) error, goto S_SCAN +// +// S-TX Transmitting a message +// - Upon TXDONE goto S_SCAN +// +// S-TXDONE Transmission complete by loop() now again in interrupt +// - Set the Mask +// - reset the Flags +// - Goto either SCAN or RX +// +// This interrupt routine has been kept as simple and short as possible. +// If we receive an interrupt that does not below to a _state then print error. +// +// NOTE: We may clear the interrupt but leave the flag for the moment. +// The eventHandler should take care of repairing flags between interrupts. +// ---------------------------------------------------------------------------- + +void stateMachine() +{ + // Determine what interrupt flags are set + // + uint8_t flags = readRegister(REG_IRQ_FLAGS); + uint8_t mask = readRegister(REG_IRQ_FLAGS_MASK); + uint8_t intr = flags & ( ~ mask ); // Only react on non masked interrupts + uint8_t rssi; + + // If there is NO interrupt and if _hop we wait until this is one + // or the wait time is over. + // That means if hop we will ONLY execute the state machine below + // when having an interrupt value and therefore a _state + // + if (intr == 0x00) + { + // If we hop we have to make sure that we allow enought time to detect + // CDDONE or CDECT. But if we do not receive interrupts, we have to schedule + // another hop after EVENT_WAIT microseconds. + // The process is such that we scan on SF7 (the shortest) preamble and if + // nothing detected within a scan, we switch to another frequency. + // + if (_hop) { + + // Reset the IRQ registers. We clear the flag to accept all interrupts + // and we clear all interrupts. + writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); + writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); + + while (_event==0) + { + hop(); // next frequency, set sf to SF7 + cadScanner(); // Reset to SF7 + + // Wait for first CDDONE or CDETD interrupt to come in + // This is tricky for hopping as hopping is NOT interrupt driven. + // XXX All such timers are in seconds, or micros is used for real-time + + if ( (( micros() - hopTime ) > _STAT_INTERVAL ) || + (( micros() - hopTime ) > _PULL_INTERVAL ) ) + { + _event=0; + return; + } + yield(); // XXX 03/01/2018 + delayMicroseconds(300); // Allow CDDETD be noticed after CDDONE. XXX 150 + intr = readRegister(REG_IRQ_FLAGS) | intr; + if (intr!=0) _event=1; + } + + // We received a real interrupt, so do nothing with either _event + // or intr and let handle by state machine + + _state=S_CAD; +#if DUSB>=1 + if (debug>=1) { + Serial.print("EVENT=0x"); + Serial.print(intr,HEX); + Serial.print(F(", F=")); + Serial.print(ifreq); + Serial.print(F(", SF=")); + Serial.print(sf); + Serial.print(F(", E=")); + Serial.print(_event); + Serial.print(F(", S=")); + Serial.print(_state); + Serial.print(F(", t=")); + Serial.print( micros() - hopTime ); + Serial.println(); + } +#endif + _event=0; // If we received an interrupt, do the state machine below. + + }// hop + + // If not hopping make sure to return without doing anything + // cause we only act on interrupts in this mode ((_event!=0) && (intr!=0)) + // + else { + _event=0; + //return; // XXX Does this work as all are Freq 1 message when hopping + } + }// intr==0 + + // This is the actual state machine of the gateway + // and its next actions are depending on the state we are in. + // For hop situations we do not get interrupts, so we have to + // simulate and generate events ourselves. + // + switch (_state) + { + + // -------------------------------------------------------------- + // If the state is init, we are starting up. + // The initLoraModem() function is already called in setup(); + // + case S_INIT: +#if DUSB>=2 + if (debug >= 1) { + Serial.println(F("S_INIT")); + } +#endif + // new state, needed to startup the radio (to S_SCAN) + writeRegister(REG_IRQ_FLAGS, 0xFF ); // Clear ALL interrupts + _event=0; + break; + + + // -------------------------------------------------------------- + // In S_SCAN we measure a high RSSI this means that there (probably) is a message + // coming in at that freq. But not necessarily on the current SF. + // If so find the right SF with CDDETD. + // + case S_SCAN: + // + // Intr==IRQ_LORA_CDDETD_MASK + // We detected a message on this frequency and SF when scanning + // We clear both CDDETD and swich to reading state to read the message + // + if (intr & IRQ_LORA_CDDETD_MASK) { + + _state = S_RX; // Set state to receiving + opmode(OPMODE_RX_SINGLE); // set reg 0x01 to 0x06 + + // Set RXDONE interrupt to dio0, RXTOUT to dio1 + writeRegister(REG_DIO_MAPPING_1, ( + MAP_DIO0_LORA_RXDONE | + MAP_DIO1_LORA_RXTOUT | + MAP_DIO2_LORA_NOP | + MAP_DIO3_LORA_CRC )); + + // Since new state is S_RX, accept no interrupts except RXDONE or RXTOUT + writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~( + IRQ_LORA_RXDONE_MASK | + IRQ_LORA_RXTOUT_MASK | + IRQ_LORA_HEADER_MASK | + IRQ_LORA_CRCERR_MASK)); + + delayMicroseconds( RSSI_WAIT ); // Wait some microseconds less + // Starting with version 5.0.1 the waittime is dependent on the SF + // So for SF12 we wait longer (2^7 == 128 uSec) and for SF7 4 uSec. + //delayMicroseconds( (0x01 << ((uint8_t)sf - 5 )) ); + rssi = readRegister(REG_RSSI); // Read the RSSI + _rssi = rssi; // Read the RSSI in the state variable + + writeRegister(REG_IRQ_FLAGS, 0xFF ); // reset all interrupt flags + _event = 0; // Make 0, as soon aswe have an interrupt +#if DUSB>=1 + if (debug>=2) { + Serial.println(F("SCAN:: CDDETD")); + } +#endif + detTime = micros(); + }//if + + // CDDONE + // We received a CDDONE int telling us that we received a message on this + // frequency and possibly on one of its SF. + // If so, we switch to CAD state where we will wait for CDDETD event. + // + else if (intr & IRQ_LORA_CDDONE_MASK) { + + opmode(OPMODE_CAD); + rssi = readRegister(REG_RSSI); // Read the RSSI + + // We choose the generic RSSI as a sorting mechanism for packages/messages + // The pRSSI (package RSSI) is calculated upon successful reception of message + // So we expect that this value makes little sense for the moment with CDDONE. + // Set the rssi as low as the noise floor. Lower values are not recognized then. + // Every cycle starts with ifreq==0 and sf=SF7 + // + if ( rssi > RSSI_LIMIT ) // Is set to 35 + { +#if DUSB>=1 + if (debug>=2) { + Serial.println(F("S_SCAN:: -> CAD")); + } +#endif + _state = S_CAD; // promote next level + _event=0; // next CDDONE by interrupt XXXXX + } + + // If the RSSI is not big enough we skip the CDDONE + // and go back to scanning + else { +#if DUSB>=1 + if (debug>=2) { + Serial.print("S_SCAN:: rssi="); + Serial.println(rssi); + } +#endif + _state = S_SCAN; + _event=1; // loop() scan until CDDONE + } + + // Clear the CADDONE flag + writeRegister(REG_IRQ_FLAGS, 0xFF); + + }//SCAN CDDONE + + // So if we are here then we are in S_SCAN and the interrupt is not + // CDDECT or CDDONE. it is probably soft interrupt _event==1 + // So if _hop we change the frequency and restart the + // interrupt in order to check for CDONE on other frequencies + // if _hop we start at the next frequency, hop () sets the sf to SF7. + // If we are at the end of all frequencies, reset frequencies and sf + // and go to S_SCAN state. + // + // Note:: We should make sure that all frequencies are scanned in a row + // and when we switch to ifreq==0 we should stop for a while + // to allow system processing. + // We should make sure that we enable webserver etc every once in a while. + // We do this by changing _event to 1 in loop() only for _hop and + // use _event=0 for non hop. + // + else if (intr == 0x00) + { + //_state = S_SCAN; // Do this state again but now for other freq. + if (! _hop) _event = 0; // XXX 26/12/2017 !!! NEED + } + + // Unkown Interrupt, so we have an error + // + else { +#if DUSB>=1 + Serial.print(F("SCAN unknown intr=")); + Serial.println(intr,HEX); +#endif + _state=S_SCAN; + writeRegister(REG_IRQ_FLAGS, 0xFF); + } + + break; // S_SCAN + + + // -------------------------------------------------------------- + // S_CAD: In CAD mode we scan every SF for high RSSI until we have a DETECT. + // Reason is the we received a CADDONE interrupt so we know a message is received + // on the frequency but may be on another SF. + // + // If message is of the right frequency and SF, IRQ_LORA_CDDETD_MSAK interrupt + // is raised, indicating that we can start beging reading the message from SPI. + // + // DIO0 interrupt IRQ_LORA_CDDONE_MASK in state S_CAD==2 means that we might have + // a lock on the Freq but not the right SF. So we increase the SF + // + case S_CAD: + + // Intr=IRQ_LORA_CDDETD_MASK + // We have to set the sf based on a strong RSSI for this channel + // + if (intr & IRQ_LORA_CDDETD_MASK) { + + // Set RXDONE interrupt to dio0, RXTOUT to dio1 + writeRegister(REG_DIO_MAPPING_1, ( + MAP_DIO0_LORA_RXDONE | + MAP_DIO1_LORA_RXTOUT | + MAP_DIO2_LORA_NOP | + MAP_DIO3_LORA_CRC )); + + // Accept no interrupts except RXDONE or RXTOUT + writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~( + IRQ_LORA_RXDONE_MASK | + IRQ_LORA_RXTOUT_MASK | + IRQ_LORA_HEADER_MASK | + IRQ_LORA_CRCERR_MASK )); + + _state = S_RX; // Set state to start receiving + opmode(OPMODE_RX_SINGLE); // set reg 0x01 to 0x06, initiate READ + + delayMicroseconds( RSSI_WAIT ); // Wait some microseconds less + //delayMicroseconds( (0x01 << ((uint8_t)sf - 5 )) ); + rssi = readRegister(REG_RSSI); // Read the RSSI + _rssi = rssi; // Read the RSSI in the state variable + + //writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDETD_MASK | IRQ_LORA_RXDONE_MASK); + writeRegister(REG_IRQ_FLAGS, 0xFF ); // reset all CAD Detect interrupt flags + + if (_hop) { + _event=0; // if CDECT, state=S_RX so we wait for intr +#if DUSB>=1 + if (debug>=1) { + Serial.print(F("S_CAD:: hop CDECT freq=")); + Serial.print(ifreq); + Serial.print(F(", sf=")); + Serial.println(sf); + } +#endif + } + else{ + _event=1; // XXX was 0; + } +#if DUSB>=1 + if (debug>=2) { + Serial.println(F("CAD:: CDDETD")); + } +#endif + detTime=micros(); + }// CDDETD + + // Intr == CADDONE + // So we scan this SF and if not high enough ... next + // + else if (intr & IRQ_LORA_CDDONE_MASK) { + // If this is not SF12, increment the SF and try again + // We expect on other SF get CDDETD + // + if (((uint8_t)sf) < SF12) { + sf = (sf_t)((uint8_t)sf+1); // Increment sf + setRate(sf, 0x04); // Set SF with CRC==on + + opmode(OPMODE_CAD); // Scanning mode + + delayMicroseconds(RSSI_WAIT); + rssi = readRegister(REG_RSSI); // Read the RSSI + + // reset interrupt flags for CAD Done + writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDONE_MASK | IRQ_LORA_CDDETD_MASK); + //writeRegister(REG_IRQ_FLAGS, 0xFF ); // This will prevent the CDDETD from being read + _event=0; // XXXXX 171215 +#if DUSB>=1 + if (debug>=2) { + Serial.print(F("S_CAD:: CDONE, SF=")); + Serial.println(sf); + } +#endif + } + + // If we reach SF12, we should go back to SCAN state + // + else { + _state = S_SCAN; // As soon as we reach SF12 do something + cadScanner(); // Which will reset SF to SF7 + + //writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDONE_MASK); + writeRegister(REG_IRQ_FLAGS, 0xFF ); + _event=1; // reset soft intr, to state machine again +#if DUSB>=1 + if (debug>=2) { + Serial.print(F("CAD->SCAN:0x")); + Serial.print(ifreq); + Serial.print(F(":SF")); + Serial.print(sf); + Serial.println(); + } +#endif + } + } //CADDONE + + // if this interrupt is not CDECT or CDDONE then probably is 0x00 + // This means _event was set but there was no real interrupt (yet). + // So we clear _event and wait for next (soft) interrupt. + // We stay in the CAD state because CDDONE means something is + // coming on this frequency so we wait on CDECT. + // + else if (intr == 0x00) { + _event=0; // Stay in CAD _state until real interrupt + } + + // else we do not recognize the interrupt. We print an error + // and restart scanning. If hop we even start at ifreq==1 + // + else { +#if DUSB>=1 + if (debug>=0) { + Serial.print(F("CAD: Unknown interrupt=")); + Serial.println(intr); + } +#endif + _state = S_SCAN; + cadScanner(); // Scan and set SF7 + writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset all interrupts + _event=0; + } + break; //S_CAD + + + // -------------------------------------------------------------- + // If we receive an interrupt on dio0 state==S_RX + // it should be a RxDone interrupt + // So we should handle the received message + // + case S_RX: + + if (intr & IRQ_LORA_RXDONE_MASK) { + + // We have to check for CRC error which will be visible AFTER RXDONE is set. + // CRC errors might indicate tha the reception is not OK. + // Could be CRC error or message too large. + // CRC error checking requires DIO3 + // + if (intr & IRQ_LORA_CRCERR_MASK) { +#if DUSB>=1 + if ((debug>=1)&&(intr & IRQ_LORA_CRCERR_MASK)) Serial.println(F("CRC err")); +#endif + if (_cad) { + _state = S_SCAN; + cadScanner(); + } + else { + _state = S_RX; + rxLoraModem(); + } + + writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask + // Reset interrupts + writeRegister(REG_IRQ_FLAGS, (uint8_t)( + IRQ_LORA_RXDONE_MASK | + IRQ_LORA_RXTOUT_MASK | + IRQ_LORA_HEADER_MASK | + IRQ_LORA_CRCERR_MASK )); + + _event=0; + break; + } +unsigned long ffTime = micros(); + // There should not be an error in the message + // + LoraUp.payLoad[0]= 0x00; + if((LoraUp.payLength = receivePkt(LoraUp.payLoad)) <= 0) { +#if DUSB>=1 + if (debug>=0) { + Serial.println(F("sMachine:: Error S-RX")); + } +#endif + } +#if DUSB>=1 + if (debug>=1) { + Serial.print(F("RXDONE=")); + Serial.println(ffTime - detTime); + } +#endif + + // Do all register processing in this section + uint8_t value = readRegister(REG_PKT_SNR_VALUE); // 0x19; + if ( value & 0x80 ) { // The SNR sign bit is 1 + + value = ( ( ~value + 1 ) & 0xFF ) >> 2; // Invert and divide by 4 + LoraUp.snr = -value; + } + else { + // Divide by 4 + LoraUp.snr = ( value & 0xFF ) >> 2; + } + + LoraUp.prssi = readRegister(REG_PKT_RSSI); // read register 0x1A, packet rssi + + // Correction of RSSI value based on chip used. + if (sx1272) { // Is it a sx1272 radio? + LoraUp.rssicorr = 139; + } else { // Probably SX1276 or RFM95 + LoraUp.rssicorr = 157; + } + + LoraUp.sf = readRegister(REG_MODEM_CONFIG2) >> 4; + + // If read was successful, read the package from the LoRa bus + // + if (receivePacket() <= 0) { // read is not successful +#if DUSB>=1 + Serial.println(F("sMach:: Error receivePacket")); +#endif + } + + // Set the modem to receiving BEFORE going back to user space. + // + if (_cad) { + _state = S_SCAN; + cadScanner(); + } + else { + _state = S_RX; + rxLoraModem(); + } + + writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); + writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset the interrupt mask + _event=0; + } + + // RX TIMEOUT: We did receive message receive timeout + // + else if (intr & IRQ_LORA_RXTOUT_MASK) { + + // Make sure we reset all interrupts// + writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00 ); + writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset all interrupts + + // For the modem in cad state we reset to SF7 + // If a timeout occurs here we reset the cadscanner + // + if (_cad) { // XXX 01/01/2018 + // Set the state to CAD scanning +#if DUSB>=1 + if (debug>=2) { + Serial.print(F("RXTOUT:: f=")); + Serial.print(ifreq); + Serial.print(F(", sf=")); + Serial.print(sf); + Serial.print(F(", tim=")); + Serial.println(micros() - detTime); + } +#endif + _state = S_SCAN; + cadScanner(); // Start the scanner after RXTOUT + _event=0; + } + + // If not in cad mode we are in single channel single sf mode. + // + else { + _state = S_RX; // + rxLoraModem(); + _event=0; + } + + } + + // The interrupt received is not RXDONE nor RXTOUT + // therefore we restart the scanning sequence (catch all) + // XXX This should not be possible, It is always one of the two... + else { +#if DUSB>=1 + if (debug>=1) { + Serial.print(F("S_RX:: no RXDONE or RXTOUT but=")); + Serial.println(intr); + } +#endif + //initLoraModem(); + //_event=0; + } + + break; // S_RX + + + // -------------------------------------------------------------- + // Start te transmissoion of a message in state S-TX + // We use TXDONE as the state to read the message. + // + case S_TX: + + if (intr == 0x00) { +#if DUSB>=1 + Serial.println(F("TX:00")); + writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags + _event=0; + return; +#endif + } + + // Initiate the transmission of the buffer (in Interrupt space) + // We react on ALL interrupts if we are in TX state. + txLoraModem( + LoraDown.payLoad, + LoraDown.payLength, + LoraDown.tmst, + LoraDown.sfTx, + LoraDown.powe, + LoraDown.fff, + LoraDown.crc, + LoraDown.iiq + ); + +#if DUSB>=2 + if (debug>=0) { + Serial.println(F("S_TX, ")); + } +#endif + _state = S_TXDONE; + writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); + writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags + _event=1; + break; // S_TX + + + // --------------------------------------------------- + // AFter the transmission is completed by the hardware, + // the interrupt TXDONE is raised telling us that the tranmission + // was successful. + // If we receive an interrupt on dio0 _state==S_TX it is a TxDone interrupt + // Do nothing with the interrupt, it is just an indication. + // sendPacket switch back to scanner mode after transmission finished OK + // + case S_TXDONE: + if (intr & IRQ_LORA_TXDONE_MASK) { +#if DUSB>=1 + Serial.println(F("TXDONE interrupt")); +#endif + // After transmission reset to receiver + if (_cad) { + // Set the state to CAD scanning + _state = S_SCAN; + cadScanner(); // Start the scanner after TX cycle + } + else { + _state = S_RX; + rxLoraModem(); + } + + writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); + writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags +#if DUSB>=1 + if (debug>=1) { + Serial.println(F("TXDONE handled")); + if (debug>=2) Serial.flush(); + } +#endif + _event=0; + } + + // If a soft _event==0 interrupt and no transmission finished: + else { +#if DUSB>=1 + if (debug>=0) { + Serial.print(F("TXDONE unknown interrupt=")); + Serial.println(intr); + if (debug>=2) Serial.flush(); + } +#endif + writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags + _event=0; + } + + break; // S_TXDONE + + + // -------------------------------------------------------------- + // If _STATE is in undefined state + // If such a thing happens, we should re-init the interface and + // make sure that we pick up next interrupt + default: +#if DUSB>=1 + if (debug >= 0) { + Serial.print("E state="); + Serial.println(_state); + } +#endif + if (_cad) { + _state = S_SCAN; + cadScanner(); + } + else + { + _state = S_RX; + rxLoraModem(); + } + writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); + writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset all interrupts + _event=0; + break; + } + + return; +} diff --git a/ESP-sc-gway/_txRx.ino b/ESP-sc-gway/_txRx.ino index 5348d67..6af81e9 100644 --- a/ESP-sc-gway/_txRx.ino +++ b/ESP-sc-gway/_txRx.ino @@ -1,7 +1,7 @@ // 1-channel LoRa Gateway for ESP8266 // Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266 -// Version 5.0.1 -// Date: 2017-11-15 +// Version 5.0.6 +// Date: 2018-02-12 // // based on work done by Thomas Telkamp for Raspberry PI 1ch gateway // and many others. @@ -148,7 +148,7 @@ int sendPacket(uint8_t *buf, uint8_t length) LoraDown.payLoad = payLoad; LoraDown.payLength = payLength; - LoraDown.tmst = tmst; + LoraDown.tmst = tmst; // Downstream in milis LoraDown.sfTx = sfTx; LoraDown.powe = powe; LoraDown.fff = fff; @@ -254,13 +254,26 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte #if STATISTICS >= 1 // Receive statistics for (int m=( MAX_STAT -1); m>0; m--) statr[m]=statr[m-1]; - statr[0].tmst = millis(); + statr[0].tmst = now(); statr[0].ch= ifreq; statr[0].prssi = prssi - rssicorr; #if RSSI==1 statr[0].rssi = _rssi - rssicorr; #endif statr[0].sf = LoraUp.sf; +#if DUSB>=2 + if (debug>=0) { + if ((message[4] != 0x26) || (message[1]==0x99)) { + Serial.print(F("addr=")); + for (int i=messageLength; i>0; i--) { + if (message[i]<16) Serial.print('0'); + Serial.print(message[i],HEX); + Serial.print(' '); + } + Serial.println(); + } + } +#endif statr[0].node = ( message[1]<<24 | message[2]<<16 | message[3]<<8 | message[4] ); #if STATISTICS >= 2 @@ -277,7 +290,7 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte #if DUSB>=1 if (debug>=1) { - Serial.print(F("pRSSI: ")); + Serial.print(F("buildPacket:: pRSSI=")); Serial.print(prssi-rssicorr); Serial.print(F(" RSSI: ")); Serial.print(_rssi - rssicorr); @@ -297,31 +310,43 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte #endif // Show received message status on OLED display -#if OLED==1 - display.clear(); - display.setFont(ArialMT_Plain_16); - display.setTextAlignment(TEXT_ALIGN_LEFT); - char timBuff[20]; - sprintf(timBuff, "%02i:%02i:%02i", hour(), minute(), second()); - display.drawString(0, 0, "Time: " ); - display.drawString(40, 0, timBuff); - display.drawString(0, 16, "RSSI: " ); - display.drawString(40, 16, String(prssi-rssicorr)); - display.drawString(70, 16, ",SNR: " ); - display.drawString(110, 16, String(SNR) ); +#if OLED>=1 + char timBuff[20]; + sprintf(timBuff, "%02i:%02i:%02i", hour(), minute(), second()); + +// char addrBuff[20; +// if (message[4] < 0x10) display.drawString( 40, 32, "0"+String(message[4], HEX)); else display.drawString( 40, 32, String(message[4], HEX)); +// if (message[3] < 0x10) display.drawString( 61, 32, "0"+String(message[3], HEX)); else display.drawString( 61, 32, String(message[3], HEX)); +// if (message[2] < 0x10) display.drawString( 82, 32, "0"+String(message[2], HEX)); else display.drawString( 82, 32, String(message[2], HEX)); +// if (message[1] < 0x10) display.drawString(103, 32, "0"+String(message[1], HEX)); else display.drawString(103, 32, String(message[1], HEX)); +// sprintf(addrBuff, "%02X,:%02X:%02X:%02X", message[4], message[3], message[2], message[1]); + + display.clear(); + display.setFont(ArialMT_Plain_16); + display.setTextAlignment(TEXT_ALIGN_LEFT); + +// msg_oLED(timBuff, prssi-rssicorr, SNR, message) + + display.drawString(0, 0, "Time: " ); + display.drawString(40, 0, timBuff); + display.drawString(0, 16, "RSSI: " ); + display.drawString(40, 16, String(prssi-rssicorr)); + display.drawString(70, 16, ",SNR: " ); + display.drawString(110, 16, String(SNR) ); - display.drawString(0, 32, "Addr: " ); + display.drawString(0, 32, "Addr: " ); - if (message[4] < 0x10) display.drawString( 40, 32, "0"+String(message[4], HEX)); else display.drawString( 40, 32, String(message[4], HEX)); - if (message[3] < 0x10) display.drawString( 61, 32, "0"+String(message[3], HEX)); else display.drawString( 61, 32, String(message[3], HEX)); - if (message[2] < 0x10) display.drawString( 82, 32, "0"+String(message[2], HEX)); else display.drawString( 82, 32, String(message[2], HEX)); - if (message[1] < 0x10) display.drawString(103, 32, "0"+String(message[1], HEX)); else display.drawString(103, 32, String(message[1], HEX)); + if (message[4] < 0x10) display.drawString( 40, 32, "0"+String(message[4], HEX)); else display.drawString( 40, 32, String(message[4], HEX)); + if (message[3] < 0x10) display.drawString( 61, 32, "0"+String(message[3], HEX)); else display.drawString( 61, 32, String(message[3], HEX)); + if (message[2] < 0x10) display.drawString( 82, 32, "0"+String(message[2], HEX)); else display.drawString( 82, 32, String(message[2], HEX)); + if (message[1] < 0x10) display.drawString(103, 32, "0"+String(message[1], HEX)); else display.drawString(103, 32, String(message[1], HEX)); - display.drawString(0, 48, "LEN: " ); - display.drawString(40, 48, String((int)messageLength) ); - display.display(); - yield(); -#endif + display.drawString(0, 48, "LEN: " ); + display.drawString(40, 48, String((int)messageLength) ); + display.display(); + yield(); + +#endif //OLED>=1 int j; @@ -336,11 +361,19 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte } #endif base64_encode(b64, (char *) message, messageLength);// max 341 - + // start composing datagram with the header + uint8_t token_h = (uint8_t)rand(); // random token + uint8_t token_l = (uint8_t)rand(); // random token + // pre-fill the data buffer with fixed fields buff_up[0] = PROTOCOL_VERSION; // 0x01 still - buff_up[3] = PKT_PUSH_DATA; // 0x00 + + buff_up[1] = token_h; + buff_up[2] = token_l; + + buff_up[3] = PKT_PUSH_DATA; // 0x00 + // READ MAC ADDRESS OF ESP8266, and insert 0xFF 0xFF in the middle buff_up[4] = MAC_array[0]; buff_up[5] = MAC_array[1]; @@ -351,11 +384,7 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte buff_up[10] = MAC_array[4]; buff_up[11] = MAC_array[5]; - // start composing datagram with the header - uint8_t token_h = (uint8_t)rand(); // random token - uint8_t token_l = (uint8_t)rand(); // random token - buff_up[1] = token_h; - buff_up[2] = token_l; + buff_index = 12; // 12-byte binary (!) header // start of JSON structure that will make payload @@ -473,7 +502,7 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte // ---------------------------------------------------------------------------- int receivePacket() { - uint8_t buff_up[TX_BUFF_SIZE]; // buffer to compose the upstream packet to backend server + uint8_t buff_up[TX_BUFF_SIZE]; // buffer to compose the upstream packet to backend server long SNR; uint8_t message[128] = { 0x00 }; // MSG size is 128 bytes for rx uint8_t messageLength = 0; diff --git a/ESP-sc-gway/_wwwServer.ino b/ESP-sc-gway/_wwwServer.ino index 2fcc090..02c832d 100644 --- a/ESP-sc-gway/_wwwServer.ino +++ b/ESP-sc-gway/_wwwServer.ino @@ -1,7 +1,7 @@ // 1-channel LoRa Gateway for ESP8266 // Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266 -// Version 5.0.1 -// Date: 2017-11-15 +// Version 5.0.6 +// Date: 2018-02-12 // // based on work done by many people and making use of several libraries. // @@ -69,16 +69,21 @@ static void printHEX(char * hexa, const char sep, String& response) // ---------------------------------------------------------------------------- // stringTime +// Print the time t into the String reponse. t is of type time_t in seconds. // Only when RTC is present we print real time values -// t contains number of milli seconds since system started that the event happened. +// t contains number of seconds since system started that the event happened. // So a value of 100 would mean that the event took place 1 minute and 40 seconds ago // ---------------------------------------------------------------------------- -static void stringTime(unsigned long t, String& response) { +static void stringTime(time_t t, String& response) { if (t==0) { response += "--"; return; } // now() gives seconds since 1970 - time_t eventTime = now() - ((millis()-t)/1000); + // as millis() does rotate every 50 days + // So we need another timing parameter + time_t eventTime = t; + + // Rest is standard byte _hour = hour(eventTime); byte _minute = minute(eventTime); byte _second = second(eventTime); @@ -139,7 +144,8 @@ static void setVariables(const char *cmd, const char *arg) { if (! _hop) { ifreq=0; freq=freqs[0]; - rxLoraModem(); + rxLoraModem(); + cadScanner(); } writeGwayCfg(CONFIGFILE); // Save configuration to file } @@ -272,7 +278,7 @@ static void openWebPage() response +="Version: "; response+=VERSION; response +="
ESP alive since "; // STARTED ON - stringTime(1, response); + stringTime(startTime, response); response +=", Uptime: "; // UPTIME uint32_t secs = millis()/1000; @@ -289,7 +295,7 @@ static void openWebPage() response += String() + _second; response +="
Current time "; // CURRENT TIME - stringTime(millis(), response); + stringTime(now(), response); response +="
"; server.sendContent(response); @@ -529,9 +535,11 @@ static void statisticsData() response +="Packages Uplink Total"; response +="" + String(cp_nb_rx_rcv) + ""; - response +="" + String((cp_nb_rx_rcv*3600)/(millis()/1000)) + ""; + response +="" + String((cp_nb_rx_rcv*3600)/(now() - startTime)) + ""; + response +="Packages Uplink OK "; response +=cp_nb_rx_ok; response+=""; + response +="Packages Downlink"; response +=cp_up_pkt_fwd; response+=""; @@ -577,8 +585,9 @@ static void sensorData() response += ""; response += "Time"; response += "Node"; - response += "Channel"; - response += "SF"; + response += "C"; + response += "Freq"; + response += "SF"; response += "pRSSI"; #if RSSI==1 if (debug > 1) { @@ -594,7 +603,7 @@ static void sensorData() response = ""; response += String() + ""; - stringTime(statr[i].tmst, response); + stringTime((statr[i].tmst), response); // XXX Change tmst not to be millis() dependent response += ""; response += String() + ""; printHEX((char *)(& (statr[i].node)),' ',response); @@ -785,7 +794,8 @@ void setupWWW() // Reset the statistics server.on("/RESET", []() { Serial.println(F("RESET")); - cp_nb_rx_rcv = 0; + startTime= now() - 1; // Reset all timers too + cp_nb_rx_rcv = 0; // Reset package statistics cp_nb_rx_ok = 0; cp_up_pkt_fwd = 0; #if STATISTICS >= 1 diff --git a/ESP-sc-gway/loraFiles.h b/ESP-sc-gway/loraFiles.h index bd4255c..41f45ae 100644 --- a/ESP-sc-gway/loraFiles.h +++ b/ESP-sc-gway/loraFiles.h @@ -1,7 +1,7 @@ // 1-channel LoRa Gateway for ESP8266 // Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266 -// Version 5.0.1 -// Date: 2017-11-15 +// Version 5.0.6 +// Date: 2018-02-12 // // based on work done by Thomas Telkamp for Raspberry PI 1ch gateway // and many others. @@ -39,7 +39,7 @@ struct espGwayConfig { uint8_t debug; // range 0 to 4 bool cad; // is CAD enabled? - bool hop; // Is HOP enabled (Note: SHould be disabled) + bool hop; // Is HOP enabled (Note: Should be disabled) bool isNode; // Is gateway node enabled bool refresh; // Is WWW browser refresh enabled diff --git a/ESP-sc-gway/loraModem.h b/ESP-sc-gway/loraModem.h index 35c50b4..efac4e7 100644 --- a/ESP-sc-gway/loraModem.h +++ b/ESP-sc-gway/loraModem.h @@ -1,7 +1,7 @@ // 1-channel LoRa Gateway for ESP8266 // Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266 -// Version 5.0.1 -// Date: 2017-11-14 +// Version 5.0.6 +// Date: 2018-02-12 // // based on work done by Thomas Telkamp for Raspberry PI 1ch gateway // and many other contributors. @@ -19,10 +19,37 @@ // // ------------------------------------------------------------------------------------ -// Our code should correct the server timing -long txDelay= 0x00; // delay time on top of server TMST -#define SPISPEED 8000000 // was 50000/50KHz < 10MHz +// ---------------------------------------- +// Used by REG_PAYLOAD_LENGTH to set receive payload length +#define PAYLOAD_LENGTH 0x40 // 64 bytes +#define MAX_PAYLOAD_LENGTH 0x80 // 128 bytes + +// In order to make the CAD behaviour dynamic we set a variable +// when the CAD functions are defined. Value of 3 is minimum frequencies a +// gateway should support to be fully LoRa compliant. +#define NUM_HOPS 2 // 3 should be the minimum + +// Do not change these setting for RSSI detection. They are used for CAD +// Given the correction factor of 157, we can get to -122dB with this rating +// +#define RSSI_LIMIT 35 // + +// How long to wait in LoRa mode before using the RSSI value. +// This period should be as short as possible, yet sufficient +// +#define RSSI_WAIT 6 // was 25 + +// How long will it take when hopping before a CDONE or CDETD value +// is measured. +// +#define EVENT_WAIT 600 // was 200 + +// Our code should correct the server Tramission delay settings +long txDelay= 0x00; // tx delay time on top of server TMST + +// SPI setting. 8MHz seems to be the max +#define SPISPEED 8000000 // Set to 8 * 10E6 // Frequencies // Set center frequency. If in doubt, choose the first one, comment all others @@ -32,7 +59,7 @@ int freqs [] = { 868100000, // Channel 0, 868.1 MHz primary 868300000, // Channel 1, 868.3 MHz mandatory 868500000, // Channel 2, 868.5 MHz mandatory - 867100000, // Channel 3, 867.1 MHz + 867100000, // Channel 3, 867.1 MHz Optional 867300000, 867500000, 867700000, @@ -60,26 +87,23 @@ volatile uint8_t _event=0; // so we need to store the current value we like to work with uint8_t _rssi; -// In order to make the CAD behaviour dynamic we set a variable -// when the CAD functions are defined. Value of 3 is minimum frequencies a -// gateway should support to be fully LoRa compliant. -#define NUM_HOPS 3 - bool _cad= (bool) _CAD; // Set to true for Channel Activity Detection, only when dio 1 connected bool _hop=false; // experimental; frequency hopping. Only use when dio2 connected bool inHop=false; unsigned long nowTime=0; -unsigned long hopTime=0; unsigned long msgTime=0; +unsigned long hopTime=0; +unsigned long detTime=0; #if _PIN_OUT==1 // Definition of the GPIO pins used by the Gateway for Hallard type boards +// struct pins { uint8_t dio0=15; // GPIO15 / D8. For the Hallard board shared between DIO0/DIO1/DIO2 uint8_t dio1=15; // GPIO15 / D8. Used for CAD, may or not be shared with DIO0 uint8_t dio2=15; // GPIO15 / D8. Used for frequency hopping, don't care uint8_t ss=16; // GPIO16 / D0. Select pin connected to GPIO16 / D0 - uint8_t rst=0; // GPIO0 / D3. Reset pin not used + uint8_t rst=0; // GPIO 0 / D3. Reset pin not used // MISO 12 / D6 // MOSI 13 / D7 // CLK 14 / D5 @@ -93,8 +117,22 @@ struct pins { uint8_t ss=15; // GPIO15 / D8. Select pin connected to GPIO15 uint8_t rst=0; // GPIO0 / D3. Reset pin not used } pins; +#elif _PIN_OUT==3 +// For ESP32 based board +// SCK == GPIO5/ PIN5 +// SS == GPIO18/PIN18 +// MISO == GPIO19/ PIN19 +// MOSI == GPIO27/ PIN27 +// RST == GPIO14/ PIN14 +struct pins { + uint8_t dio0=26; // GPIO26 / Dio0 used for one frequency and one SF + uint8_t dio1=26; // GPIO26 / Used for CAD, may or not be shared with DIO0 + uint8_t dio2=26; // GPI26 / Used for frequency hopping, don't care + uint8_t ss=18; // GPIO18 / Dx. Select pin connected to GPIO18 + uint8_t rst=14; // GPIO0 / D3. Reset pin not used +} pins; #else - // Use your own pin definitions, and uncomment #error line below + // Use your own pin definitions, and comment #error line below // MISO 12 / D6 // MOSI 13 / D7 // CLK 14 / D5 @@ -106,21 +144,18 @@ struct pins { // Ech time a message is received or sent the statistics are updated. // In case STATISTICS==1 we define the last MAX_STAT messages as statistics struct stat_t { - unsigned long tmst; // Time since 1970 in milli seconds + unsigned long tmst; // Time since 1970 in seconds unsigned long node; // 4-byte DEVaddr (the only one known to gateway) uint8_t ch; // Channel index to freqs array uint8_t sf; #if RSSI==1 - int8_t rssi; // XXX Can be < -128 + int8_t rssi; // XXX Can be < -128 #endif - int8_t prssi; // XXX Can be < -128 + int8_t prssi; // XXX Can be < -128 } stat_t; #if STATISTICS >= 1 -// History of received uplink messages from nodes -struct stat_t statr[MAX_STAT]; - // STATC contains the statistic that are gateway related and not per // message. Example: Number of messages received on SF7 or number of (re) boots // So where statr contains the statistics gathered per packet the statc @@ -139,55 +174,42 @@ struct stat_c { } stat_c; struct stat_c statc; #endif +// History of received uplink messages from nodes +struct stat_t statr[MAX_STAT]; + #else // STATISTICS==0 -struct stat_t statr[1]; // Always have at least one element to store in +struct stat_t statr[1]; // Always have at least one element to store in #endif // Define the payload structure used to separate interrupt ans SPI // processing from the loop() part -uint8_t payLoad[128]; // Payload i +uint8_t payLoad[128]; // Payload i struct LoraBuffer { - uint8_t * payLoad; - uint8_t payLength; - uint32_t tmst; - uint8_t sfTx; - uint8_t powe; - uint32_t fff; - uint8_t crc; - uint8_t iiq; + uint8_t * payLoad; + uint8_t payLength; + uint32_t tmst; // in millis() + uint8_t sfTx; + uint8_t powe; + uint32_t fff; + uint8_t crc; + uint8_t iiq; } LoraDown; -// Up buffer (from Lora to UDP) +// Up buffer (from Lora sensor to UDP) // struct LoraUp { - uint8_t payLoad[128]; - uint8_t payLength; - int prssi; - long snr; - int rssicorr; - uint8_t sf; + uint8_t payLoad[128]; + uint8_t payLength; + int prssi; + long snr; + int rssicorr; + uint8_t sf; } LoraUp; -// ---------------------------------------- -// Used by REG_PAYLOAD_LENGTH to set receive payload length -#define PAYLOAD_LENGTH 0x40 // 64 bytes -#define MAX_PAYLOAD_LENGTH 0x80 // 128 bytes - -// Do not change these setting for RSSI detection. They are used for CAD -// Given the correction factor of 157, we can get to -120dB with this rating -// -#define RSSI_LIMIT 37 // was 39 -#define RSSI_LIMIT_DOWN 34 // Was 34 - -// How long to wait in LoRa mode before using the RSSI value. -// This period should be as short as possible, yet sufficient -// -#define RSSI_WAIT 15 // was 100 works, 50 works, 40 works, 25 works -#define RSSI_WAIT_DOWN 10 // was 75 works, 35 works, 30 works, 20 works // ============================================================================ // Set all definitions for Gateway @@ -195,7 +217,7 @@ struct LoraUp { // Register definitions. These are the addresses of the TFM95, SX1276 that we // need to set in the program. -#define REG_FIFO 0x00 +#define REG_FIFO 0x00 // rw FIFO address #define REG_OPMODE 0x01 // Register 2 to 5 are unused for LoRa #define REG_FRF_MSB 0x06 @@ -204,17 +226,17 @@ struct LoraUp { #define REG_PAC 0x09 #define REG_PARAMP 0x0A #define REG_LNA 0x0C -#define REG_FIFO_ADDR_PTR 0x0D -#define REG_FIFO_TX_BASE_AD 0x0E -#define REG_FIFO_RX_BASE_AD 0x0F +#define REG_FIFO_ADDR_PTR 0x0D // rw SPI interface address pointer in FIFO data buffer +#define REG_FIFO_TX_BASE_AD 0x0E // rw write base address in FIFO data buffer for TX modulator +#define REG_FIFO_RX_BASE_AD 0x0F // rw read base address in FIFO data buffer for RX demodulator (0x00) -#define REG_FIFO_RX_CURRENT_ADDR 0x10 +#define REG_FIFO_RX_CURRENT_ADDR 0x10 // r Address of last packet received #define REG_IRQ_FLAGS_MASK 0x11 #define REG_IRQ_FLAGS 0x12 #define REG_RX_NB_BYTES 0x13 #define REG_PKT_SNR_VALUE 0x19 -#define REG_PKT_RSSI 0x1A // latest package -#define REG_RSSI 0x1B // Current RSSI, section 6.4, or 5.5.5 +#define REG_PKT_RSSI 0x1A // latest package +#define REG_RSSI 0x1B // Current RSSI, section 6.4, or 5.5.5 #define REG_HOP_CHANNEL 0x1C #define REG_MODEM_CONFIG1 0x1D #define REG_MODEM_CONFIG2 0x1E @@ -227,7 +249,7 @@ struct LoraUp { #define REG_RSSI_WIDEBAND 0x2C #define REG_INVERTIQ 0x33 -#define REG_DET_TRESH 0x37 // SF6 +#define REG_DET_TRESH 0x37 // SF6 #define REG_SYNC_WORD 0x39 #define REG_TEMP 0x3C @@ -245,7 +267,7 @@ struct LoraUp { #define SX72_MODE_SLEEP 0x80 #define SX72_MODE_STANDBY 0x81 #define SX72_MODE_FSTX 0x82 -#define SX72_MODE_TX 0x83 // 0x80 | 0x03 +#define SX72_MODE_TX 0x83 // 0x80 | 0x03 #define SX72_MODE_RX_CONTINUOS 0x85 // ---------------------------------------- @@ -266,7 +288,7 @@ struct LoraUp { // ---------------------------------------- // LOW NOISE AMPLIFIER -#define LNA_MAX_GAIN 0x23 +#define LNA_MAX_GAIN 0x23 // Max gain 0x20 | Boost 0x03 #define LNA_OFF_GAIN 0x00 #define LNA_LOW_GAIN 0x20 @@ -326,9 +348,13 @@ struct LoraUp { #define MAP_DIO2_LORA_NOP 0x0C // ----11-- bit 3 and 2 #define MAP_DIO3_LORA_CADDONE 0x00 // ------00 bit 1 and 0 +#define MAP_DIO3_LORA_HEADER 0x01 // ------01 +#define MAP_DIO3_LORA_CRC 0x02 // ------10 #define MAP_DIO3_LORA_NOP 0x03 // ------11 +// FSK specific #define MAP_DIO0_FSK_READY 0x00 // 00------ (packet sent / payload ready) + #define MAP_DIO1_FSK_NOP 0x30 // --11---- #define MAP_DIO2_FSK_TXNOP 0x04 // ----01-- #define MAP_DIO2_FSK_TIMEOUT 0x08 // ----10-- @@ -355,7 +381,7 @@ struct LoraUp { #define PKT_PULL_ACK 0x04 #define PKT_TX_ACK 0x05 -#define MGT_RESET 0x15 // Not a LoRa Gateway Spec message +#define MGT_RESET 0x15 // Not a LoRa Gateway Spec message #define MGT_SET_SF 0x16 #define MGT_SET_FREQ 0x17 diff --git a/ESP-sc-gway/oLED.h b/ESP-sc-gway/oLED.h new file mode 100644 index 0000000..54283c7 --- /dev/null +++ b/ESP-sc-gway/oLED.h @@ -0,0 +1,41 @@ +// 1-channel LoRa Gateway for ESP8266 +// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266 +// Version 5.0.6 +// Date: 2018-02-12 +// +// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway +// and many others. +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the MIT License +// which accompanies this distribution, and is available at +// https://opensource.org/licenses/mit-license.php +// +// Author: Maarten Westenberg (mw12554@hotmail.com) +// +// This file contains a number of compile-time settings and definitions for OLED support. +// +// ---------------------------------------------------------------------------------------- + +#if OLED>=1 + +#define OLED_SCL 5 // GPIO5 / D1 +#define OLED_SDA 4 // GPIO4 / D2 + +#if OLED==1 +#include "SSD1306.h" +#define OLED_ADDR 0x3C // Default 0x3C for 0.9", for 1.3" it is 0x78 +SSD1306 display(OLED_ADDR, OLED_SDA, OLED_SCL);// i2c ADDR & SDA, SCL on wemos +#endif + +#if OLED==2 +#include "SH1106.h" +//#define OLED_ADDR 0x78 // Default for 1.3" it is 0x78 +#define OLED_ADDR 0x3C // Default 0x3C for 0.9", for 1.3" it is 0x78 +SH1106 display(OLED_ADDR, OLED_SDA, OLED_SCL); // i2c ADDR & SDA, SCL on wemos +#endif + + + + +#endif \ No newline at end of file diff --git a/README.md b/README.md index 799452e..44cecc4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Single Channel LoRaWAN Gateway -Version 5.0.1, November, 2017 +Version 5.0.3, December 09, 2017 Author: M. Westenberg (mw12554@hotmail.com) Copyright: M. Westenberg (mw12554@hotmail.com) @@ -20,21 +20,25 @@ The software implements a standard LoRa gateway with the following exceptions on - This LoRa gateway is not a full gateway but it implements just a one-channel/one frequency gateway. The minimum amount of frequencies supported by a full gateway is 3, most support 9 or more frequencies. -This software started as a proof-of-concept to prove that a single low-cost RRFM95 chip which was present in almost every -LoRa node in Europe could be used as a cheap alternative to the far more expensive full gateways that were -making use of the SX1301 chip. +This software started as a proof-of-concept to prove that a single low-cost RRFM95 chip which was present +in almost every LoRa node in Europe could be used as a cheap alternative to the far more expensive full +gateways that were making use of the SX1301 chip. -- As the software of this gateway will often be used during the development phase of a project or in demo situations, -the software is flexible and can be easily configured according to environment or customer requirements. -There are two ways of interacting with the software: 1. Modifying the ESP-sc-gway.h file at compile time allows the -administrator to set almost all parameters. 2. Using the webinterface (http://) will allow -administrators to set and reset several of the parameters at runtime. +- As the software of this gateway will often be used during the development phase of a project or in +demo situations, the software is flexible and can be easily configured according to environment or +customer requirements. There are two ways of interacting with the software: +1. Modifying the ESP-sc-gway.h file at compile time allows the administrator to set almost all parameters. +2. Using the webinterface (http://) will allow administrators to set and reset several of the +parameters at runtime. + +Full documentation of the Single Channel Gateway is found at things4u.github.io, please look at the Hardware Guide +under the Gateway chapter. ## testing The single channel gateway has been tested on a gateway with the Wemos D1 Mini, using a HopeRF RFM95W transceiver. -The LoRa nodes tested are: +The LoRa nodes tested againts this gateway are: - TeensyLC with HopeRF RFM95 radio - Arduino Pro-Mini (default Armega328 model, 8MHz 3.3V and 16MHz 3.3V) @@ -72,7 +76,7 @@ coming in on the Serial monitor. There are two ways of changing the configuration of the single channel gateway: 1. Changing the ESP-sc-gway.h file at compile-time -2. Run the http:// web interface to change setting at complie time. +2. Run the http:// web interface to change settings at run time. ## Editing the ESP-sc-gway.h file @@ -89,6 +93,14 @@ The user is advised to turn off functions not used in order to save on memory us If the heap drops below 18 KBytes some functions may not behave as expected (in extreme case the program may crash). +### Setting USB + +The user can determine whether or not the USB console is used for output messages. +When setting DUSB to 0 all output by Serial is disabled +(actually the Serial statements are not included in the code). + + \#define DUSB 1 + ### Debug The user can set the initial value of the DEBUG parameter. @@ -301,7 +313,7 @@ Please note that they are NOT part of the ESP 1channel gateway and may have thei However, these libraries are not part of the single-channel Gateway software. -# Connections +# Pin Connections See http://things4u.github.io in the hardware section for building and connection instructions. @@ -310,7 +322,7 @@ See http://things4u.github.io in the hardware section for building and connectio The following things are still on my wish list to make to the single channel gateway: -- Receive downstream message with commands form the server. These can be used to configure +- Receive downstream message with commands from the server. These can be used to configure the gateway through downlink messages (such as setting the SF) - Support for ESP32 and RFM95 on 915 MHz - Use the SPIFFS for storing .css files @@ -321,4 +333,5 @@ The following things are still on my wish list to make to the single channel gat # License The source files of the gateway sketch in this repository is made available under the MIT -license. The libraries included in this repository are included for convenience only and all have their own license, and are not part of the ESP 1ch gateway code. +license. The libraries included in this repository are included for convenience only and all have their own license, +and are not part of the ESP 1ch gateway code.