瀏覽代碼

Version 6.2.4; Focus on Downstream messaging, and other improvements. Please look and CHANGELOG for all changes.

platenspeler 5 年之前
父節點
當前提交
59d0cfa2e5
共有 24 個文件被更改,包括 1390 次插入1075 次删除
  1. 22 2
      CHANGELOG.md
  2. 22 5
      README.md
  3. 2 2
      TODO.md
  4. 49 16
      platformio.ini
  5. 141 102
      src/ESP-sc-gway.ino
  6. 5 12
      src/_WiFi.ino
  7. 13 6
      src/_gatewayMgt.ino
  8. 85 134
      src/_loraFiles.ino
  9. 89 143
      src/_loraModem.ino
  10. 19 19
      src/_oLED.ino
  11. 3 3
      src/_otaServer.ino
  12. 1 1
      src/_repeater.ino
  13. 7 8
      src/_sensor.ino
  14. 59 67
      src/_stateMachine.ino
  15. 1 1
      src/_tcpTTN.ino
  16. 217 219
      src/_txRx.ino
  17. 270 120
      src/_udpSemtech.ino
  18. 96 42
      src/_utils.ino
  19. 110 66
      src/_wwwServer.ino
  20. 54 38
      src/configGway.h
  21. 93 43
      src/configNode.h
  22. 8 3
      src/loraFiles.h
  23. 18 17
      src/loraModem.h
  24. 6 6
      src/oLED.h

+ 22 - 2
CHANGELOG.md

@@ -1,6 +1,6 @@
 # Single Channel LoRaWAN Gateway
 
-Version 6.2.0, January 29, 2020 
+Version 6.2.4, February 29, 2020 
 Author: M. Westenberg (mw12554@hotmail.com)  
 Copyright: M. Westenberg (mw12554@hotmail.com)  
 
@@ -16,10 +16,30 @@ Maintained by Maarten Westenberg (mw12554@hotmail.com)
 
 # Release Notes
 
+Features release 6.2.4 (April 25, 2020)
+- Changes the date layout used in the output to be more standard: 3 characters weekday, months and day 2 chars
+- Changed the configGway.h file a lot ro define default values for parameters while at the 
+	same time allowing changing through PlatformIO
+- Updated the documentation itself and updated link in the software
+- For Monitor output use the same stringTime() function
+- Added the _PROFILER definition to read the timing (PlatformIO only)
+- Adapt the documentation and start writing hardware guide (for compile options)
+- Changed the delay for TX (Downlink messages) -. Removed to 0 for _STIRCT_1CH
+- Index of iSeen messages was in Hex -> Changed to decimal
+- Added a timing correction for sending message of aound 700000 uSec (0.7 Sec) to make downlink work
+- Changed naming of tmst (TimeStamp) functions
+- Removed function printlog(), as it was not used
+- Moved the SPIFFS file operations to a timer function in loop() as it consumer far too much time. 
+As a result regular timestamp is reduced.
+- Removed the logging functions for the same reasons. User can choose to put those on again.
+- Added Udp.flush() when NTP messages arrive during operations 
+- Changed layout of webpage. Changed sequence to first OFF and then ON
+- Changed website to include loraWait statistics
+
 Features release 6.2.3 (February 23, 2020)
 - Lots of bugs and documentation fixes
 - Added customizable #define statements through platformio.ini file (Read!!!)
-- Changed the WiFiManager code to better support both architectures: ESP8266 and ESP32\
+- Changed the WiFiManager code to better support both architectures: ESP8266 and ESP32
 
 Features release 6.2.1 (February 2, 2020)
 - PlatformIO support

+ 22 - 5
README.md

@@ -1,7 +1,7 @@
 # Single Channel LoRaWAN Gateway
 
-Version 6.2.0, 
-Data: January 29, 2020  
+Version 6.2.4, 
+Data: April 25, 2020  
 Author: M. Westenberg (mw12554@hotmail.com)  
 Copyright: M. Westenberg (mw12554@hotmail.com)  
 
@@ -16,8 +16,8 @@ Maintained by Maarten Westenberg (mw12554@hotmail.com)
 
 # Description
 
-First of all: PLEASE READ THIS FILE AND [Documentation](https://things4u.github.io/Projects/SingleChannelGateway/UserGuide/Introduction%206.html) it should contain most of the 
-information you need to get going.
+First of all: PLEASE READ THIS FILE AND [Documentation](http://things4u.github.io/Projects/SingleChannelGateway) 
+it should contain most of the information you need to get going.
 Unfortunately I do not have the time to follow up on all emails, and as most information including pin-outs 
 etc etc are contained on these pages I hope you have the time to read them and post any remaining questions.
 
@@ -47,6 +47,24 @@ 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.
 
+# PlatformIO or ArduinoIDE
+
+The source works on both environments, both the classic Arduino IDE and on PlatformIO. 
+Unfortunately there are small differences between these two envrironments. 
+At this moment the src directory contains the PlatformIO source, and therefore we will decribe how to connect to Arduino IDE.
+The applies to the libraries.
+
+## PlatformIO
+When in PlatformIO, choose <File> and then <Add folder to Workspace...> and select the new LoRa-1ch-ESP-Gateway 
+top directory.
+Then just open the ESP-sc-gway.ino file at src directory and build or upload
+
+## Arduino IDE
+
+Create a place on you filesystem wo work on the files. In thius directory creat the source directory "ESP-sc-gway" 
+and the libraries directory "libraries". When unpacking the source at github: 
+Copy the content of the "src" directory to the Aruino IDE "ESP-sc-gway" directory and copy the contents 
+of the "lib" to the Arduino IDE "libraries" directory;
 
 ## testing
 
@@ -467,7 +485,6 @@ The following things are still on my wish list to make to the single channel gat
 - Look at Class B and C support
 
 
-
 # License
 
 The source files of the gateway sketch in this repository is made available under the MIT

+ 2 - 2
TODO.md

@@ -16,9 +16,9 @@ Maintained by Maarten Westenberg (mw12554@hotmail.com)
 
 # ToDo Functions
 
-Features not in release 6.2.3
-
+Features not in release 6.2.4
 
+- Change Downstream timing to be interrupt driven.
 - Frequency: Support for eu433 frequencies (Standard)
 - Testing and timing of downlink functions (need quiet area)
 - Get HOP frequency functions to work on three frequencies

+ 49 - 16
platformio.ini

@@ -8,54 +8,87 @@
 ; Please visit documentation for the other options and examples
 ; https://docs.platformio.org/page/projectconf.html
 
+; Nr 21 has WIFIMANAGER set
 [env:Gateway_21]
 platform = espressif8266
 board = d1_mini
 board_build.mcu = esp8266
 board_build.f_cpu = 80000000L
 build_flags =
-  -D _WIFIMANAGER=1
-  -D OLED=0
+  -D _WIFIMANAGER=0
+  -D _SPIFFS_FORMAT=0
+  -D _OLED=0
+  -D _DUSB=1
+  -D _PROFILER=1
 framework = arduino
 upload_protocol = espota
 board_build.flash_mode = qio
 upload_speed = 115200
 upload_port = 192.168.2.21
 
-[env:Gateway_22]
+[env:Gateway_30]
 platform = espressif8266
 board = d1_mini
 board_build.mcu = esp8266
 board_build.f_cpu = 80000000L
 build_flags =
-  -D OLED=1
+  -D _WIFIMANAGER=0
+  -D _SPIFFS_FORMAT=0
+  -D _OLED=0
+  -D _DUSB=1
+  -D _PROFILER=1
 framework = arduino
 upload_protocol = espota
 board_build.flash_mode = qio
 upload_speed = 115200
-upload_port = 192.168.2.22
+upload_port = 192.168.2.30
 
-[env:Gateway_31]
-platform = espressif8266
-board = d1_mini
-board_build.mcu = esp8266
-board_build.f_cpu = 80000000L
+[env:Gateway_72]
+platform = espressif32
+board = heltec_wifi_lora_32
+framework = arduino
 build_flags =
-  -D _PIN_OUT=2
-  -D OLED=2
+  -D _SPIFFS_FORMAT=0
+  -D _WIFIMANAGER=0
+  -D _OLED=0
+  -D _DUSB=1
+  -D _PROFILER=1
+upload_protocol = espota
+board_build.flash_mode = qio
+upload_speed = 115200
+upload_port = 192.168.2.72
+
+[env:Gateway_138]
+platform = espressif32
+board = heltec_wifi_lora_32
 framework = arduino
+build_flags =
+  -D _WIFIMANAGER=0
+  -D _SPIFFS_FORMAT=0
+  -D _OLED=1
+  -D _DUSB=1
+  -D _PROFILER=1
+;  -D _JSONENCODE=1
+;  -D _MAXSEEN=0
 upload_protocol = espota
 board_build.flash_mode = qio
 upload_speed = 115200
-upload_port = 192.168.2.31
+upload_port = 192.168.2.138
 
-[env:Gateway_59]
+; Nr. 54 has WIFIMANAGER sometimes set
+; Also the Gateway is a T-Beam sensor for temperature and GPS (_GATEWAYNODE=1)
+[env:Gateway_54]
 platform = espressif32
 board = heltec_wifi_lora_32
 build_flags =
-  -D _WIFIMANAGER=1
+  -D _WIFIMANAGER=0
+  -D _SPIFFS_FORMAT=0
+  -D _DUSB=0
+  -D _OLED=0
+  -D _GATEWAYNODE=1
+  -D _PROFILER=1
 framework = arduino
 upload_protocol = espota
 board_build.flash_mode = qio
 upload_speed = 115200
-upload_port = 192.168.2.59
+upload_port = 192.168.2.54

+ 141 - 102
src/ESP-sc-gway.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg
 // Author: Maarten Westenberg (mw12554@hotmail.com)
 //
@@ -81,16 +81,16 @@ extern "C" {
 
 #	define ESP_getChipId()   ((uint32_t)ESP.getEfuseMac())
 
-#	if A_SERVER==1
+#	if _SERVER==1
 #		include <WebServer.h>								// Standard Webserver for ESP32
 #		include <Streaming.h>          						// http://arduiniana.org/libraries/streaming/
-		WebServer server(A_SERVERPORT); // MMM added 20Feb
-#	endif //A_SERVER
+		WebServer server(_SERVERPORT); // MMM added 20Feb
+#	endif //_SERVER
 
-#	if A_OTA==1
+#	if _OTA==1
 //#		include <ESP32httpUpdate.h>							// Not yet available
 #		include <ArduinoOTA.h>
-#	endif //A_OTA
+#	endif //_OTA
 
 
 // ----------- Specific ESP8266 stuff --------------
@@ -105,16 +105,16 @@ extern "C" {
 
 #	define ESP_getChipId()   (ESP.getChipId())
 
-#	if A_SERVER==1
+#	if _SERVER==1
 #		include <ESP8266WebServer.h>
 #		include <Streaming.h>          						// http://arduiniana.org/libraries/streaming/
-		ESP8266WebServer server(A_SERVERPORT);				// Standard IDE lib
-#	endif //A_SERVER
+		ESP8266WebServer server(_SERVERPORT);				// Standard IDE lib
+#	endif //_SERVER
 
-#	if A_OTA==1
+#	if _OTA==1
 #		include <ESP8266httpUpdate.h>
 #		include <ArduinoOTA.h>
-#	endif //A_OTA
+#	endif //_OTA
 					
 
 #else
@@ -163,6 +163,9 @@ char platform[24]	= _PLATFORM; 							// platform definition
 char email[40]		= _EMAIL;    							// used for contact email
 char description[64]= _DESCRIPTION;							// used for free form description 
 
+// JSON definitions
+StaticJsonDocument<312> jsonBuffer;							// Use of arduinoJson version 6!
+	
 // define servers
 
 IPAddress ntpServer;										// IP address of NTP_TIMESERVER
@@ -171,18 +174,20 @@ IPAddress thingServer;										// Only if we use a second (backup) server
 
 WiFiUDP Udp;
 
-time_t startTime = 0;										// The time in seconds since 1970 that the server started. We use this variable since millis() is reset every 50 days...
+time_t startTime = 0;										// The time in seconds since 1970 that the server started. 
 uint32_t eventTime = 0;										// Timing of _event to change value (or not).
 uint32_t sendTime = 0;										// Time that the last message transmitted
 uint32_t doneTime = 0;										// Time to expire when CDDONE takes too long
 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 pullTime = 0;										// last time we sent a pull_data request to server\
+uint32_t rstTime  = 0;										// When to reset the timer
+uint32_t fileTime = 0;										// Wite the configuration to file
 
 #define TX_BUFF_SIZE  1024									// Upstream buffer to send to MQTT
 #define RX_BUFF_SIZE  1024									// Downstream received from MQTT
 #define STATUS_SIZE	  512									// Should(!) be enough based on the static text .. was 1024
 
-#if A_SERVER==1
+#if _SERVER==1
 	uint32_t wwwtime = 0;
 #endif
 #if NTP_INTR==0
@@ -191,6 +196,9 @@ uint32_t pullTime = 0;										// last time we sent a pull_data request to serv
 #if _GATEWAYNODE==1
 	uint16_t frameCount=0;									// We write this to SPIFF file
 #endif
+#ifdef _PROFILER
+	uint32_t endTmst=0;
+#endif
 
 // Init the indexes of the data we display on the webpage
 // We use this for circular buffers
@@ -222,11 +230,11 @@ void setupWWW();														// _wwwServer.ino forward
 
 void mPrint(String txt);												// _utils.ino
 int getNtpTime(time_t *t);												// _utils.ino
-int mStat(uint8_t intr, String & response);								// _utils.ini
+int mStat(uint8_t intr, String & response);								// _utils.ino
 void SerialStat(uint8_t intr);											// _utils.ino
 void printHexDigit(uint8_t digit, String & response);					// _utils.ino
 int inDecodes(char * id);												// _utils.ino
-static void stringTime(time_t t, String & response);					// _urils.ino
+static void stringTime(time_t t, String & response);					// _utils.ino
 
 int initMonitor(struct moniLine *monitor);								// _loraFiles.ino
 void initConfig(struct espGwayConfig *c);								// _loraFiles.ino
@@ -250,7 +258,7 @@ void stateMachine();													// _stateMachine.ino
 
 bool connectUdp();														// _udpSemtech.ino
 int readUdp(int packetSize);											// _udpSemtech.ino
-int sendUdp(IPAddress server, int port, uint8_t *msg, int length);		// _udpSemtech.ino
+int sendUdp(IPAddress server, int port, uint8_t *msg, uint16_t length);		// _udpSemtech.ino
 void sendstat();														// _udpSemtech.ino
 void pullData();														// _udpSemtech.ino
 
@@ -283,13 +291,13 @@ void setup() {
 #	endif //_DUSB
 
 
-#	if OLED>=1
-		init_oLED();										// When done display "STARTING" on OLED
-#	endif //OLED
+#	if _OLED>=1
+		init_oLED();										// When done display "STARTING" on Oled
+#	endif //_OLED
 
 #	if _GPS==1
 		// Pins are defined in LoRaModem.h together with other pins
-		sGps.begin(9600, SERIAL_8N1, GPS_TX, GPS_RX);			// PIN 12-TX 15-RX
+		sGps.begin(9600, SERIAL_8N1, GPS_TX, GPS_RX);		// PIN 12-TX 15-RX
 #	endif //_GPS
 
 	delay(500);
@@ -301,7 +309,7 @@ void setup() {
 		}
 #		endif //_MONITOR
 	}
-	else {											// SPIFFS not found
+	else {													// SPIFFS not found
 		if (pdebug & P_MAIN) {
 			mPrint("SPIFFS.begin: not found, formatting");
 		}
@@ -317,6 +325,7 @@ void setup() {
 	SPIFFS.format();										// Normally disabled. Enable only when SPIFFS corrupt
 	delay(500);
 	initConfig(&gwayConfig);
+	gwayConfig.formatCntr++;
 	if ((debug>=1) && (pdebug & P_MAIN)) {
 		mPrint("Format SPIFFS Filesystem Done");
 	}
@@ -349,13 +358,11 @@ void setup() {
 	else {
 #		if _MONITOR>=1
 		if (debug>=0) {
-			mPrint("setup:: readGwayCfg: ERROR readCfgCfg Failed");
+			mPrint("setup:: readGwayCfg: ERROR readGwayCfg Failed");
 		}
 #		endif	
 	};							
-
 	delay(500);
-	yield();
 
 #	if _WIFIMANAGER==1
 		msg_oLED("WIFIMGR");
@@ -386,9 +393,10 @@ void setup() {
 			mPrint("setup:: Error Wifi network connect(0)");
 		}
 #		endif //_MONITOR
-		yield();
 	}
 
+	yield();
+
 #	if _MONITOR>=1
 	if ((debug>=1) & ( pdebug & P_MAIN )) {
 		mPrint("setup:: WlanConnect="+String(WiFi.SSID()) );
@@ -566,16 +574,16 @@ void setup() {
 
 	// The Over the Air updates are supported when we have a WiFi connection.
 	// The NTP time setting does not have to be precise for this function to work.
-#if A_OTA==1
+#if _OTA==1
 	setupOta(hostname);										// Uses wwwServer 
-#endif //A_OTA
+#endif //_OTA
 
 	readSeen(_SEENFILE, listSeen);							// read the seenFile records
 
-#if A_SERVER==1	
+#if _SERVER==1	
 	// Setup the webserver
 	setupWWW();
-#endif //A_SERVER
+#endif //_SERVER
 
 	delay(100);												// Wait after setup
 	
@@ -609,11 +617,11 @@ void setup() {
 	writeConfig(CONFIGFILE, &gwayConfig);					// Write config
 	writeSeen(_SEENFILE, listSeen);							// Write the last time record  is seen
 
-	// activate OLED display
-#	if OLED>=1
+	// activate Oled display
+#	if _OLED>=1
 		acti_oLED();
 		addr_oLED();
-#	endif //OLED
+#	endif // _OLED
 
 	mPrint(" --- Setup() ended, Starting loop() ---");
 
@@ -632,7 +640,7 @@ void setup() {
 // and the backend system cannot do its housekeeping, the watchdog
 // function will be executed which means effectively that the 
 // program crashes.
-// We use yield() a lot to avoid ANY watch dog activity of the program.
+// We use yield() to avoid ANY watch dog activity of the program.
 //
 // NOTE2: For ESP make sure not to do large array declarations in loop();
 // ----------------------------------------------------------------------------
@@ -641,6 +649,55 @@ void loop ()
 	int packetSize;
 	uint32_t nowSeconds = now();
 	
+	// If we are not connected, try to connect.
+	// We will not read Udp in this loop cycle if not connected to Wlan
+	if (WlanConnect(1) < 0) {
+#		if _MONITOR>=1
+		if ((debug >= 0) && (pdebug & P_MAIN)) {
+			mPrint("loop:: ERROR reconnect WLAN");
+		}
+#		endif //_MONITOR
+		yield();
+		return;												// Exit loop if no WLAN connected
+	} //WlanConnect()
+
+	yield();												// MMM 200403 to make sure UDP buf filled
+
+	// 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.
+	// As we do not know when the server will respond, we test in every loop.
+	//
+	while( (packetSize= Udp.parsePacket()) > 0) {
+#		if _MONITOR>=1
+		if ((debug>=2) && (pdebug & P_TX)){
+			mPrint("loop:: readUdp available");
+		}
+#		endif //_MONITOR
+
+		// DOWNSTREAM
+		// Packet may be PKT_PUSH_ACK (0x01), PKT_PULL_ACK (0x03) or PKT_PULL_RESP (0x04)
+		// This command is found in byte 4 (buffer[3])
+
+		if (readUdp(packetSize) < 0) {
+#			if _MONITOR>=1
+			if (debug>=0)
+				mPrint("Dwn readUdp ERROR, returning < 0");
+#			endif //_MONITOR
+			break;
+		}
+		// Now we know we succesfully received message from host
+		// If return value is 0, we received a NTP message,
+		// otherwise a UDP message with other TTN content, all ACKs are 4 bytes long
+		else {
+			//_event=1;									// Could be done double if more messages received
+			//mPrint("Dwn udp received="+String(micros())+", packetSize="+String(packetSize));
+		}
+	}
+
+	yield();
+
 	// 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().
@@ -653,8 +710,8 @@ void loop ()
 	// So it will kick in if there are not many messages for the gateway.
 	// Note: Be careful that it does not happen too often in normal operation.
 	//
-	if ( ((nowSeconds - statr[0].tmst) > _MSG_INTERVAL ) &&
-		(msgTime <= statr[0].tmst) ) 
+	if ( ((nowSeconds - statr[0].time) > _MSG_INTERVAL ) &&
+		(msgTime <= statr[0].time) ) 
 	{
 #		if _MONITOR>=1
 		if (( debug>=2 ) && ( pdebug & P_MAIN )) {
@@ -683,94 +740,47 @@ void loop ()
 		msgTime = nowSeconds;
 	}
 
-#if A_SERVER==1
-	// Handle the Web server part of this sketch. Mainly used for administration 
-	// and monitoring of the node. This function is important so it is called at the
-	// start of the loop() function.
-	yield();
-	server.handleClient();
-#endif //A_SERVER
-
-#if A_OTA==1
+#if _OTA==1
 	// Perform Over the Air (OTA) update if enabled and requested by user.
 	// It is important to put this function early in loop() as it is
 	// not called frequently but it should always run when called.
 	//
 	yield();
 	ArduinoOTA.handle();
-#endif //A_OTA
+#endif //_OTA
 
 	// If event is set, we know that we have a (soft) interrupt.
 	// After all necessary web/OTA services are scanned, we will
 	// reloop here for timing purposes. 
 	// Do as less yield() as possible.
-	// XXX 180326
 	if (_event == 1) {
 		return;
 	}
 	else yield();
 
-	
-	// If we are not connected, try to connect.
-	// We will not read Udp in this loop cycle then
-	if (WlanConnect(1) < 0) {
-#		if _MONITOR>=1
-		if (( debug >= 0 ) && ( pdebug & P_MAIN )) {
-			mPrint("loop:: ERROR reconnect WLAN");
-		}
-#		endif //_MONITOR
-		yield();
-		return;												// Exit loop if no WLAN connected
-	} //WlanConnect
-	
-	// 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.
-	// As we do not know when the server will respond, we test in every loop.
-	//
-	else {
-		while( (packetSize = Udp.parsePacket()) > 0) {
-#			if _MONITOR>=1
-			if (debug>=2) {
-				mPrint("loop:: readUdp calling");
-			}
-#			endif //_MONITOR
-			// DOWNSTREAM
-			// Packet may be PKT_PUSH_ACK (0x01), PKT_PULL_ACK (0x03) or PKT_PULL_RESP (0x04)
-			// This command is found in byte 4 (buffer[3])
-			if (readUdp(packetSize) < 0) {
-#				if _MONITOR>=1
-				if ( debug>=0 )
-					mPrint("readUdp ERROR,down returning < 0");
-#				endif //_MONITOR
-				break;
-			}
-			// Now we know we succesfully received message from host
-			// If return value is 0, we received a NTP message,
-			// otherwise a UDP message with other TTN content
-			else {
-				//_event=1;									// Could be done double if more messages received
-			}
-		}
-	}
+#if _SERVER==1
+	// Handle the Web server part of this sketch. Mainly used for administration 
+	// and monitoring of the node. This function is important so it is called at the
+	// start of the loop() function.
+	server.handleClient();
+#endif //_SERVER
+
 
-	yield();												// on 26/12/2017
 
 	// stat PUSH_DATA message (*2, par. 4)
 	//	
-
     if ((nowSeconds - statTime) >= _STAT_INTERVAL) {		// Wake up every xx seconds
+		yield();											// on 26/12/2017
         sendstat();											// Show the status message and send to server
 #		if _MONITOR>=1
-		if (( debug>=2 ) && ( pdebug & P_MAIN )) {
+		if ((debug>=2) && (pdebug & P_MAIN)) {
 			mPrint("Send Pushdata sendstat");
 		}
 #		endif //_MONITOR
 
 		// If the gateway behaves like a node, we do from time to time
 		// send a node message to the backend server.
-		// The Gateway node emessage has nothing to do with the STAT_INTERVAL
+		// The Gateway node message has nothing to do with the STAT_INTERVAL
 		// message but we schedule it in the same frequency.
 		//
 #		if _GATEWAYNODE==1
@@ -792,21 +802,44 @@ void loop ()
 #		endif//_GATEWAYNODE
 		statTime = nowSeconds;
     }
-	
-	yield();
 
-	
+
 	// send PULL_DATA message (*2, par. 4)
 	//
+	// This message will also restart the server which taken approx. 3 ms.
 	nowSeconds = now();
     if ((nowSeconds - pullTime) >= _PULL_INTERVAL) {		// Wake up every xx seconds
+
+		yield();
         pullData();											// Send PULL_DATA message to server
-		startReceiver();
+		//startReceiver();
 		pullTime = nowSeconds;
 		
 #		if _MONITOR>=1
-		if (( debug>=2) && ( pdebug & P_MAIN )) {
-			mPrint("ESP-sc-gway:: PULL_DATA message sent");
+		if ((debug>=1) && (pdebug & P_RX)) {
+			String response = "UP ESP-sc-gway:: PKT_PULL_DATA message sent: micr=";
+			printInt(micros(), response);
+			mPrint(response);
+		}
+#		endif //_MONITOR
+    }
+
+
+	// send RESET_DATA message (*2, par. 4)
+	//
+	// This message will also restart the server which taken approx. 3 ms.
+	nowSeconds = now();
+    if ((nowSeconds - rstTime) >= _RST_INTERVAL) {			// Wake up every xx seconds
+
+		yield();
+		startReceiver();
+		rstTime = nowSeconds;
+		
+#		if _MONITOR>=1
+		if ((debug>=2) && (pdebug & P_MAIN)) {
+			String response = "UP ESP-sc-gway:: RST_DATA message sent: micr=";
+			printInt(micros(), response);
+			mPrint(response);
 		}
 #		endif //_MONITOR
     }
@@ -820,7 +853,6 @@ void loop ()
 		//  as this function may collide with SPI and other interrupts
 		// Note: It can be that we do not receive a time this loop (no worries)
 		yield();
-		nowSeconds = now();
 		if (nowSeconds - ntptimer >= _NTP_INTERVAL) {
 			yield();
 			time_t newTime;
@@ -844,4 +876,11 @@ void loop ()
 		}
 #	endif//NTP_INTR
 
+#	if _MAXSEEN >= 1
+		if ((nowSeconds - fileTime) >= _FILE_INTERVAL) {
+			writeSeen(_SEENFILE, listSeen);
+			fileTime = nowSeconds;
+		}
+#	endif // _MAXSEEN
+
 }//loop

+ 5 - 12
src/_WiFi.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 // 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
@@ -235,17 +235,17 @@ int WlanConnect(int maxTry) {
 	// Value 0 is reserved for setup() first time connect
 	int i=0;
 
-	while ( (WiFi.status() != WL_CONNECTED) && (( i<= maxTry ) || (maxTry==0)) )
+	while ((WiFi.status() != WL_CONNECTED) && (( i<= maxTry ) || (maxTry==0)) )
 	{
 		// We try every SSID in wpa array until success
-		for (int j=wpa_index; (j< (sizeof(wpa)/sizeof(wpa[0]))) && (WiFi.status() != WL_CONNECTED ); j++)
+		for (unsigned int j=wpa_index; (j<(sizeof(wpa)/sizeof(wpa[0]))) && (WiFi.status() != WL_CONNECTED ); j++)
 		{
 			// Start with well-known access points in the list
 			char *ssid		= wpa[j].login;
 			char *password	= wpa[j].passw;
 			
 #			if _MONITOR>=1
-			if (debug>=1)  {
+			if ((debug>=1) && (pdebug & P_MAIN)) {
 				Serial.print(i);
 				Serial.print(':');
 				Serial.print(j); 
@@ -284,7 +284,7 @@ int WlanConnect(int maxTry) {
 			agains=1;
 			while ((WiFi.status() != WL_CONNECTED) && (agains < 8)) {
 				agains++;		
-				delay(8000);											//delay(agains*500);
+				delay(8000);											// delay(agains*500);
 #				if _MONITOR>=1
 				if ( debug>=0 ) {
 					Serial.print(".");									// Serial only
@@ -302,13 +302,6 @@ int WlanConnect(int maxTry) {
 		i++;			// Number of times we try to connect
 	} //while
 
-
-#		if _MONITOR>=1
-		if ((debug>=2) & (pdebug & P_MAIN)) {
-			mPrint("WlanConnect:: Connected="+ String(WiFi.SSID()) );
-		}
-#		endif //_MONITOR
-
 	yield();
 	return(1);
 	

+ 13 - 6
src/_gatewayMgt.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg 
 //
 // Based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
@@ -57,23 +57,30 @@ void gateway_mgt(uint8_t size, uint8_t *buff) {
 	
 	switch (opcode) {
 		case MGT_RESET:
-			Serial.println(F("gateway_mgt:: RESET"));
+#			if _MONITOR>=1
+				mPrint(F("gateway_mgt:: RESET"));
+#			endif // _MONITOR
 			// No further parameters, just reset the GWay
 			setup();								// Call the sketch setup function
 			// Send Ack to server
 			
 		break;
 		case MGT_SET_SF:
-			Serial.println(F("gateway_mgt:: SET SF"));
+#			if _MONITOR>=1
+				mPrint(F("gateway_mgt:: SET SF"));
+#			endif //_MONITOR
 			// byte [4] contains desired SF code (7 for SF7 and 12 for SF12)
 		break;
 		case MGT_SET_FREQ:
-			Serial.println(F("gateway_mgt:: SET FREQ"));
+#			if _MONITOR>=1
+				mPrint(F("gateway_mgt:: SET FREQ"));
+#			endif // _MONITOR
 			// Byte [4] contains index of Frequency
 		break;
 		default:
-			Serial.print(F("gateway_mgt:: Unknown UDP code=")); 
-			Serial.println(opcode);
+#			if _MONITOR>=1
+				mPrint(F("gateway_mgt:: Unknown UDP code=") + String(opcode) ); 
+#			endif
 			return;
 		break;
 	}

+ 85 - 134
src/_loraFiles.ino

@@ -54,7 +54,9 @@ void id_print (String id, String val)
 }
 
 
-
+// ============================================================================
+// config functions
+//
 
 // ----------------------------------------------------------------------------
 // INITCONFIG; Init the gateway configuration file
@@ -75,7 +77,9 @@ void initConfig(struct espGwayConfig *c)
 	(*c).monitor = true;				// Monitoring is ON
 	(*c).trusted = 1;
 	(*c).txDelay = 0;					// First Value without saving is 0;
-}
+	(*c).dusbStat = true;
+
+} // initConfig()
 
 // ----------------------------------------------------------------------------
 // Read the config file and fill the (copied) variables
@@ -95,6 +99,7 @@ int readGwayCfg(const char *fn, struct espGwayConfig *c)
 	}
 	debug	= (*c).debug;
 	pdebug	= (*c).pdebug;
+	(*c).boots++;										// Increment Boot Counter
 
 #	if _GATEWAYNODE==1
 		if (gwayConfig.fcnt != (uint8_t) 0) {
@@ -102,8 +107,11 @@ int readGwayCfg(const char *fn, struct espGwayConfig *c)
 		}
 #	endif
 
+	writeGwayCfg(CONFIGFILE, &gwayConfig );				// And writeback the configuration, not to miss a boot
+
 	return 1;
-}
+	
+} // readGwayCfg()
 
 
 // ----------------------------------------------------------------------------
@@ -222,6 +230,14 @@ int readConfig(const char *fn, struct espGwayConfig *c)
 			id_print(id, val);
 			(*c).ntpErr = (uint16_t) val.toInt();
 		}
+		else if (id == "WAITERR") {								// WAITERR setting
+			id_print(id, val);
+			(*c).waitErr = (uint16_t) val.toInt();
+		}
+		else if (id == "WAITOK") {								// WAITOK setting
+			id_print(id, val);
+			(*c).waitOk = (uint16_t) val.toInt();
+		}
 		else if (id == "NTPETIM") {								// NTPERR setting
 			id_print(id, val);
 			(*c).ntpErrTime = (uint32_t) val.toInt();
@@ -258,6 +274,10 @@ int readConfig(const char *fn, struct espGwayConfig *c)
 			id_print(id, val);
 			(*c).trusted= (int8_t) val.toInt();
 		}
+		else if (id == "FORMAT") {								// TRUSTED setting
+			id_print(id, val);
+			(*c).formatCntr= (int8_t) val.toInt();
+		}
 		else {
 #			if _MONITOR>=1
 				mPrint(F("readConfig:: tries++"));
@@ -268,7 +288,8 @@ int readConfig(const char *fn, struct espGwayConfig *c)
 	f.close();
 
 	return(1);
-}//readConfig
+	
+} // readConfig()
 
 
 // ----------------------------------------------------------------------------
@@ -291,7 +312,8 @@ int writeGwayCfg(const char *fn, struct espGwayConfig *c)
 #	endif //_GATEWAYNODE
 
 	return(writeConfig(fn, c));
-}
+} // writeGwayCfg
+
 
 // ----------------------------------------------------------------------------
 // Write the configuration as found in the espGwayConfig structure
@@ -330,10 +352,13 @@ int writeConfig(const char *fn, struct espGwayConfig *c)
 	f.print("REENTS");	f.print('='); f.print((*c).reents);		f.print('\n');
 	f.print("NTPETIM");	f.print('='); f.print((*c).ntpErrTime); f.print('\n');
 	f.print("NTPERR");	f.print('='); f.print((*c).ntpErr);		f.print('\n');
+	f.print("WAITERR");	f.print('='); f.print((*c).waitErr);	f.print('\n');
+	f.print("WAITOK");	f.print('='); f.print((*c).waitOk);		f.print('\n');
 	f.print("NTPS");	f.print('='); f.print((*c).ntps);		f.print('\n');
 	f.print("FILEREC");	f.print('='); f.print((*c).logFileRec); f.print('\n');
 	f.print("FILENO");	f.print('='); f.print((*c).logFileNo);	f.print('\n');
 	f.print("FILENUM");	f.print('='); f.print((*c).logFileNum); f.print('\n');
+	f.print("FORMAT");	f.print('='); f.print((*c).formatCntr); f.print('\n');
 	f.print("DELAY");	f.print('='); f.print((*c).txDelay); 	f.print('\n');
 	f.print("TRUSTED");	f.print('='); f.print((*c).trusted); 	f.print('\n');
 	f.print("EXPERT");	f.print('='); f.print((*c).expert); 	f.print('\n');
@@ -342,7 +367,7 @@ int writeConfig(const char *fn, struct espGwayConfig *c)
 	
 	f.close();
 	return(1);
-}
+} // writeConfig()
 
 
 
@@ -363,6 +388,7 @@ int writeConfig(const char *fn, struct espGwayConfig *c)
 int addLog(const unsigned char * line, int cnt) 
 {
 #	if _STAT_LOG==1
+
 	char fn[16];
 	
 	if (gwayConfig.logFileRec > LOGFILEREC) {		// Have to make define for this
@@ -417,12 +443,14 @@ int addLog(const unsigned char * line, int cnt)
 		Serial.print(gwayConfig.logFileRec);
 
 		Serial.print(F(": "));
-#		if _DUSB>=2
+#		if _MONITOR>=2
+		{
 			for (i=0; i< 12; i++) {				// The first 12 bytes contain non printable characters
 				Serial.print(line[i],HEX);
 				Serial.print(' ');
 			}
-#		else //_DUSB>=2
+		}
+#		else //_MONITOR>=2
 			i+=12;
 #		endif //_DUSB>=2
 		Serial.print((char *) &line[i]);	// The rest if the buffer contains ascii
@@ -441,44 +469,14 @@ int addLog(const unsigned char * line, int cnt)
 #	endif //_STAT_LOG
 
 	return(1);
-} //addLog
-
+} //addLog()
 
-// ----------------------------------------------------------------------------
-// Print (all) logfiles
-// Return:
-//	<none>
-// Parameters:
-//	<none>
-// ----------------------------------------------------------------------------
-void printLog()
-{
-#if _STAT_LOG==1
-	char fn[16];
 
-#	if _DUSB>=1
-	for (int i=0; i< LOGFILEMAX; i++ ) {
-		sprintf(fn,"/log-%d", gwayConfig.logFileNo - i);
-		if (!SPIFFS.exists(fn)) break;		// break the loop
 
-		// Open the file for reading
-		File f = SPIFFS.open(fn, "r");
-
-		for (int j=0; j<LOGFILEREC; j++) {
-			
-			String s=f.readStringUntil('\n');
-			if (s.length() == 0) break;
-
-			Serial.println(s.substring(12));			// Skip the first 12 Gateway specific binary characters
-			yield();
-		}
-	}
-#	endif //_DUSB
-#endif //_STAT_LOG
-} //printLog
-
-
-#if _MAXSEEN >= 1
+// ============================================================================
+// Below are the xxxSeen() functions. These functions keep track of the kast
+// time a device was seen bij the gateway.
+// These functions are not round-robin and they do not need to be.
 
 // ----------------------------------------------------------------------------
 // initSeen
@@ -490,6 +488,7 @@ void printLog()
 // ----------------------------------------------------------------------------
 int initSeen(struct nodeSeen *listSeen) 
 {
+#if _MAXSEEN >= 1
 	for (int i=0; i< _MAXSEEN; i++) {
 		listSeen[i].idSeen=0;
 		listSeen[i].sfSeen=0;
@@ -498,8 +497,10 @@ int initSeen(struct nodeSeen *listSeen)
 		listSeen[i].timSeen=(time_t) 0;					// 1 jan 1970 0:00:00 hrs
 	}
 	iSeen= 0;											// Init index to 0
+#endif // _MAXSEEN
 	return(1);
-}
+
+} // initSeen()
 
 
 // ----------------------------------------------------------------------------
@@ -514,6 +515,7 @@ int initSeen(struct nodeSeen *listSeen)
 // ----------------------------------------------------------------------------
 int readSeen(const char *fn, struct nodeSeen *listSeen)
 {
+#if _MAXSEEN >= 1
 	int i;
 	iSeen= 0;												// Init the index at 0
 	
@@ -540,10 +542,8 @@ int readSeen(const char *fn, struct nodeSeen *listSeen)
 		String val="";
 		
 		if (!f.available()) {
-#			if _MONITOR>=1
-				String response="readSeen:: No more info left in file, i=";
-				response += i;
-				mPrint(response);
+#			if _MONITOR>=2
+				mPrint("readSeen:: No more info left in file, i=" + String(i));
 #			endif //_MONITOR
 			break;
 		}
@@ -561,16 +561,13 @@ int readSeen(const char *fn, struct nodeSeen *listSeen)
 		iSeen++;											// Increase index, new record read
 	}
 	f.close();
-	
-#	if _MONITOR>=1
-	if ((debug >= 2) && (pdebug & P_MAIN)) {
-		Serial.print("readSeen:: ");
-		printSeen(listSeen);
-	}
-#	endif //_MONITOR
+
+#endif // _MAXSEEN
+
 	// So we read iSeen records
 	return 1;
-}
+	
+} // readSeen()
 
 
 // ----------------------------------------------------------------------------
@@ -584,6 +581,7 @@ int readSeen(const char *fn, struct nodeSeen *listSeen)
 // ----------------------------------------------------------------------------
 int writeSeen(const char *fn, struct nodeSeen *listSeen)
 {
+#if _MAXSEEN >= 1
 	int i;
 	if (!SPIFFS.exists(fn)) {
 #		if _MONITOR>=1
@@ -610,48 +608,11 @@ int writeSeen(const char *fn, struct nodeSeen *listSeen)
 	}
 	
 	f.close();
-#	if _DUSB>=1
-	if ((debug >= 2) && (pdebug & P_MAIN)) {
-		Serial.print("writeSeen:: ");
-		printSeen(listSeen);
-	}
-#	endif //_DUSB
+#endif // _MAXSEEN
 	return(1);
 }
 
-// ----------------------------------------------------------------------------
-// printSeen
-// - This function writes the last seen array to the USB !!
-// ----------------------------------------------------------------------------
-int printSeen(struct nodeSeen *listSeen) {
 
-#	if _DUSB>=1
-    int i;
-	if (( debug>=2 ) && ( pdebug & P_MAIN )) {
-	
-		Serial.println(F("printSeen:: "));
-		
-		for (i=0; i<iSeen; i++) {
-			if (listSeen[i].idSeen != 0) {
-				String response;
-				
-				response = i;
-				response += ", Tim=";
-
-				stringTime(listSeen[i].timSeen, response);
-
-				response += ", addr=0x";
-				printHex(listSeen[i].idSeen,' ', response);
-				
-				response += ", SF=0x";
-				response += listSeen[i].sfSeen;
-				Serial.println(response);
-			}
-		}
-	}
-#	endif //_DUSB
-	return(1);
-}
 
 // ----------------------------------------------------------------------------
 // addSeen
@@ -668,71 +629,61 @@ int printSeen(struct nodeSeen *listSeen) {
 // ----------------------------------------------------------------------------
 int addSeen(struct nodeSeen *listSeen, struct stat_t stat) 
 {
-
-	int i=0;
+#if _MAXSEEN >= 1
+	int i;
 	for (i=0; i<iSeen; i++) {						// For all known records
 
+		// If the record node is equal, we found the record already.
+		// So increment cntSeen
 		if (listSeen[i].idSeen==stat.node) {
-
-			listSeen[i].timSeen = (time_t)stat.tmst;
+		
+			listSeen[i].timSeen = (time_t)stat.time;
 			listSeen[i].cntSeen++;					// Not included on function para
-			listSeen[i].idSeen = stat.node;
+			//listSeen[i].idSeen = stat.node;		// Not necessary, is the same
 			listSeen[i].chnSeen = stat.ch;
-			listSeen[i].sfSeen = stat.sf;			// Or the argument
-	
-			writeSeen(_SEENFILE, listSeen);
+			listSeen[i].sfSeen = stat.sf;			// The SF argument
+//			writeSeen(_SEENFILE, listSeen);
+			
+#			if _MONITOR>=2
+			if ((debug>=1) && (pdebug & P_MAIN)) {
+				mPrint("addSeen:: adding i="+String(i)+", node="+String(stat.node,HEX));
+			}
+#			endif
 
 			return 1;
 		}
 	}
 	
-	// Else: We did not find the current record so make a new Seen entry
+	// else: We did not find the current record so make a new Seen entry
 	if ((i>=iSeen) && (i<_MAXSEEN)) {
 		listSeen[i].idSeen = stat.node;
 		listSeen[i].chnSeen = stat.ch;
-		listSeen[i].sfSeen = stat.sf;			// Or the argument
-		listSeen[i].timSeen = (time_t)stat.tmst;
-		listSeen[i].cntSeen = 1;					// Not included on function para	
+		listSeen[i].sfSeen = stat.sf;				// The SF argument
+		listSeen[i].timSeen = (time_t)stat.time;	// Timestamp correctly
+		listSeen[i].cntSeen = 1;					// We see this for the first time	
 		iSeen++;
-		//return(1);
 	}
 
-#	if _MONITOR>=2
-	if ((debug>=1) && (pdebug & P_MAIN)) {
-		String response= "addSeen:: New i=";
+#	if _MONITOR>=1
+	if ((debug>=2) && (pdebug & P_MAIN)) {
+		String response= "addSeen:: i=";
 		response += i;
 		response += ", tim=";
-		stringTime(stat.tmst, response);
+		stringTime(stat.time, response);
 		response += ", iSeen=";
-		response += String(iSeen,HEX);
+		response += String(iSeen);
 		response += ", node=";
 		response += String(stat.node,HEX);
 		response += ", listSeen[0]=";
-		
-		printHex(listSeen[0].idSeen,' ',response);
-		Serial.print("<"); Serial.print(listSeen[0].idSeen, HEX); Serial.print(">");
-
-		mPrint(response);
-		
-		response += ", listSeen[0]=";
-		printHex(listSeen[0].idSeen,' ',response);
+		printHex(listSeen[0].idSeen,':',response);
 
 		mPrint(response);
 	}
-#	endif
-
-	// USB Only
-#	if _DUSB>=2
-	if ((debug>=2) && (pdebug & P_MAIN)) {
-		Serial.print("addSeen i=");
-		Serial.print(i);
-		Serial.print(", id=");
-		Serial.print(listSeen[i].idSeen,HEX);
-		Serial.println();
-	}
-#	endif // _DUSB	
+#	endif // _MONITOR
 
+#endif //_MAXSEEN>=1 
 	return 1;
-}
+	
+} // addSeen()
 
-#endif //_MAXSEEN>=1 End of File
+// End of File

+ 89 - 143
src/_loraModem.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 // 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
@@ -193,14 +193,15 @@ void writeBuffer(uint8_t addr, uint8_t *buf, uint8_t len)
 void setRate(uint8_t sf, uint8_t crc) 
 {
 	uint8_t mc1=0, mc2=0, mc3=0;
-#	if _MONITOR>=2
+
 	if ((sf<SF7) || (sf>SF12)) {
+#		if _MONITOR>=2
 		if (( debug>=1 ) && ( pdebug & P_RADIO )) {
 			mPrint("setRate:: SF=" + String(sf));
 		}
-		return;
+#		endif //_MONITOR
+		sf=8;
 	}
-#	endif //_MONITOR
 
 	// Set rate based on Spreading Factor etc
     if (sx1272) {
@@ -303,7 +304,8 @@ void  opmode(uint8_t mode)
 // This function should only be used for receiver operation. The current
 // receiver frequency is determined by gwayConfig.ch index like so: freqs[gwayConfig.ch] 
 // ----------------------------------------------------------------------------------------
-void hop() {
+void hop() 
+{
 
 	// 1. Set radio to standby
 	opmode(OPMODE_STANDBY);
@@ -360,8 +362,8 @@ void hop() {
 }
 	
 
-// ----------------------------------------------------------------------------------------
-// UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP
+// ------------------------------------- UP -----------------------------------------------
+//
 // This LoRa function reads a message from the LoRa transceiver
 // on Success: returns message length read when message correctly received.
 // on Failure: it returns a negative value on error (CRC error for example).
@@ -446,16 +448,11 @@ uint8_t receivePkt(uint8_t *payload)
 		}
 
 		if (readRegister(REG_FIFO_RX_CURRENT_ADDR) != readRegister(REG_FIFO_RX_BASE_AD)) {
-#if			_DUSB>=1
+#if			_MONITOR>=1
 			if (( debug>=1 ) && ( pdebug & P_RADIO )) {
-				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();
+				mPrint("RX BASE <" + String(readRegister(REG_FIFO_RX_BASE_AD)) + "> != RX CURRENT <" + String(readRegister(REG_FIFO_RX_CURRENT_ADDR)) + ">"	);
 			}
-#			endif //_DUSB
+#			endif //_MONITOR
 		}
 		
         //uint8_t currentAddr = readRegister(REG_FIFO_RX_CURRENT_ADDR);	// 0x10
@@ -487,28 +484,23 @@ uint8_t receivePkt(uint8_t *payload)
 		// As long as _MONITOR is enabled, and P_RX debug messages are selected,
 		// the received packet is displayed on the output.
 #		if _MONITOR>=1
-		if (( debug>=1 ) && ( pdebug & P_RX )){
+		if ((debug>=1) && (pdebug & P_RX)){
 		
-			String response = "rxPkt:: t=";
+			String response = "UP receivePkt:: rxPkt: t=";
 			stringTime(now(), response);
+			response += ", f=" + String(gwayConfig.ch) + ", sf=" + String(sf);
 			
-			Serial.print(F(", f="));
-			Serial.print(gwayConfig.ch);
-			Serial.print(F(", sf="));
-			Serial.print(sf);
-			
-			Serial.print(F(", a="));
-			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);
+			response += ", a=";
+			uint8_t DevAddr [4];
+					DevAddr[0] = payload[4];
+					DevAddr[1] = payload[3];
+					DevAddr[2] = payload[2];
+					DevAddr[3] = payload[1];
+			printHex((IPAddress)DevAddr, ':', response);
 			
-			Serial.print(F(", flags="));
-			Serial.print(irqflags,HEX);
-			Serial.print(F(", addr="));
-			Serial.print(currentAddr);
-			Serial.print(F(", len="));
-			Serial.print(receivedCount);
+			response += ", flags=" + String(irqflags,HEX);
+			response += ", addr=" + String(currentAddr);
+			response += ", len=" + String(receivedCount);
 
 			// If debug level 1 is specified, we display the content of the message as well
 			// We need to decode the message as well will it make any sense
@@ -517,15 +509,9 @@ uint8_t receivePkt(uint8_t *payload)
 			if (debug>=1)  {							// Must be 1 for operational use
 
 				int index;								// The index of the codex struct to decode
-				String response="";
+				//String response="";
 
 				uint8_t data[receivedCount];
-				
-				uint8_t DevAddr [4];
-					DevAddr[0] = payload[4];
-					DevAddr[1] = payload[3];
-					DevAddr[2] = payload[2];
-					DevAddr[3] = payload[1];
 			
 				if ((index = inDecodes((char *)(payload+1))) >=0 ) {	
 					mPrint(", Ind="+String(index));
@@ -570,12 +556,10 @@ uint8_t receivePkt(uint8_t *payload)
 					Serial.print(data[i], HEX);
 					Serial.print(' ');
 				}
-			|
+			}
 #			endif // _TRUSTED_DECODE
 			
 			mPrint(response);							// Print response for Serial or mPrint
-			
-			if (debug>=2) Serial.flush();
 		}
 #		endif //MONITOR
 		return(receivedCount);
@@ -586,13 +570,15 @@ uint8_t receivePkt(uint8_t *payload)
 		IRQ_LORA_RXTOUT_MASK |
 		IRQ_LORA_HEADER_MASK | 
 		IRQ_LORA_CRCERR_MASK));							// 0x12; Clear RxDone IRQ_LORA_RXDONE_MASK
-    return 0;
+
+	return 0;
+	
 } //receivePkt UP
 	
 	
 	
-// ----------------------------------------------------------------------------------------
-// DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN 
+// ------------------------------ DOWN ----------------------------------------------------
+//
 // This DOWN function sends a payload to the LoRa node over the air
 // Radio must go back in standby mode as soon as the transmission is finished
 // 
@@ -600,15 +586,14 @@ uint8_t receivePkt(uint8_t *payload)
 // ----------------------------------------------------------------------------------------
 bool sendPkt(uint8_t *payLoad, uint8_t payLength)
 {
-#	if _DUSB>=2
+#	if _MONITOR>=2
 	if (payLength>=128) {
 		if (debug>=1) {
-			Serial.print("sendPkt:: len=");
-			Serial.println(payLength);
+			mPrint("sendPkt Dwn:: len="+String(payLength));
 		}
 		return false;
 	}
-#	endif
+#	endif //_MONITOR
 	
 	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_TX_BASE_AD));	// 0x0D, 0x0E
 	
@@ -617,9 +602,10 @@ bool sendPkt(uint8_t *payLoad, uint8_t payLength)
 	writeBuffer(REG_FIFO, (uint8_t *) payLoad, payLength);
 	
 	return true;
-}
+} // sendPkt()
 
-// ----------------------------------------------------------------------------------------
+
+// ------------------------------------------ DOWN ----------------------------------------
 // loraWait()
 // This function implements the wait protocol needed for downstream transmissions.
 // Note: Timing of downstream and JoinAccept messages is VERY critical.
@@ -635,72 +621,46 @@ bool sendPkt(uint8_t *payLoad, uint8_t payLength)
 // to make sure that delay() did not take too much time this works.
 // 
 // Parameter: uint32-t tmst gives the micros() value when transmission should start. (!!!)
-// Note: We assume LoraDown.sfTx contains the SF we will use for downstream message.
+// so it contains the local Gateway time as a reference when to start Downlink.
+// Note: We assume LoraDown->sfTx contains the SF we will use for downstream message.
+//		gwayConfig.txDelay is the delay as specified in the GUI
 // ----------------------------------------------------------------------------------------
 
-void loraWait(const uint32_t timestamp)
+void loraWait(struct LoraDown *LoraDown)
 {
-	uint32_t startMics = micros();						// Start of the loraWait function
-	uint32_t tmst = timestamp;
-
-	int32_t adjust=0;
-	switch (LoraDown.sfTx) {
-		case 7: adjust= 60000; break;					// Make time for SF7 longer 
-		case 8: break;									// Around 60ms
-		case 9: break;
-		case 10: break;
-		case 11: break;
-		case 12: break;
-		default:
-#		if _DUSB>=1
-		if (( debug>=1 ) && ( pdebug & P_TX )) {
-			Serial.print(F("T loraWait:: unknown SF="));
-			Serial.print(LoraDown.sfTx);
-		}
-#		endif
-		break;
-	}
-	tmst = tmst + gwayConfig.txDelay + adjust;			// tmst based on txDelay and spreading factor
-	uint32_t waitTime = tmst - micros();				// Or make waitTime an unsigned and change next statement
-	if (micros()>tmst) {								// to (waitTime<0)
-		Serial.println(F("loraWait:: Error wait time < 0"));
+	int32_t delayTmst = (int32_t)(LoraDown->tmst - micros()) + gwayConfig.txDelay;
+												// delayTmst based on txDelay and spreading factor
+	
+	if ((delayTmst > 8000000) || (delayTmst < 0)) {					// Delay is  > 8 secs
+#		if _MONITOR>=1
+			String response= "Dwn loraWait:: ERROR: ";
+			printDwn(LoraDown,response);
+			mPrint(response);
+#		endif // _MONITOR
+		gwayConfig.waitErr++;
 		return;
 	}
-	
+
 	// For larger delay times we use delay() since that is for > 15ms
-	// This is the most efficient way
-	while (waitTime > 16000) {
+	// This is the most efficient way.
+	// MMM Check for huge wait times
+	while (delayTmst > 15000) {
 		delay(15);										// ms delay including yield, slightly shorter
-		waitTime= tmst - micros();
-	}
-	// The remaining wait time is less tan 15000 uSecs
-	// And we use delayMicroseconds() to wait
-	if (waitTime>0) delayMicroseconds(waitTime);
-
-#	if _DUSB>=1
-	else if ((waitTime+20) < 0) {
-		Serial.println(F("loraWait:: TOO LATE"));		// Never happens
+//		delayMicroseconds(15000);						// ms delay including yield, slightly shorter
+		delayTmst -= 15000;
 	}
+	
+	// The remaining wait time is less than 15000 uSecs
+	// but more than 1000 mSecs (see above)
+	// therefore we use delayMicroseconds() to wait
+	delayMicroseconds(delayTmst);
 
-	if (( debug>=2 ) && ( pdebug & P_TX )) { 
-		Serial.print(F("T start: ")); 
-		Serial.print(startMics);
-		Serial.print(F(", tmst: "));					// tmst
-		Serial.print(tmst);
-		Serial.print(F(", end: "));						// This must be micros(), and equal to tmst
-		Serial.print(micros());
-		Serial.print(F(", waited: "));
-		Serial.print(tmst - startMics);
-		Serial.print(F(", delay="));
-		Serial.print(gwayConfig.txDelay);
-		Serial.println();
-		if (debug>=2) Serial.flush();
-	}
-#	endif
+	gwayConfig.waitOk++;
+	return;
 }
 
 
-// ----------------------------------------------------------------------------------------
+// -------------------------------------- DOWN --------------------------------------------
 // txLoraModem
 // Init the transmitter and transmit the buffer
 // After successful transmission (dio0==1) TxDone re-init the receiver
@@ -724,31 +684,12 @@ void loraWait(const uint32_t timestamp)
 // 14. Write buffer (byte by byte)
 // 15. Wait until the right time to transmit has arrived
 // 16. opmode TX
+//
+// Transmission to the device not is not done often, but is time critical.
 // ----------------------------------------------------------------------------------------
 
-void txLoraModem(
-				uint8_t *payLoad, 		// Payload contents
-				uint8_t payLength, 		// Length of payload
-				uint32_t tmst, 			// Timestamp
-				uint8_t sfTx,			// Sending spreading factor
-				uint8_t powe, 			// power
-				uint32_t freq, 			// frequency
-				uint8_t crc, 			// Do we use CRC error checking
-				uint8_t iiq				// Interrupt
-				)
+void txLoraModem(struct LoraDown *LoraDown)
 {
-#	if _DUSB>=2
-	if (debug>=1) {
-		// Make sure that all serial stuff is done before continuing
-		Serial.print(F("txLoraModem::"));
-		Serial.print(F("  powe: ")); Serial.print(powe);
-		Serial.print(F(", freq: ")); Serial.print(freq);
-		Serial.print(F(", crc: ")); Serial.print(crc);
-		Serial.print(F(", iiq: 0X")); Serial.print(iiq,HEX);
-		Serial.println();
-		if (debug>=2) Serial.flush();
-	}
-#	endif
 
 	_state = S_TX;
 		
@@ -762,19 +703,19 @@ void txLoraModem(
 	opmode(OPMODE_STANDBY);									// set 0x01 to 0x01
 	
 	// 3. Init spreading factor and other Modem setting
-	setRate(sfTx, crc);
+	setRate(LoraDown->sfTx, LoraDown->crc);
 	
 	// Frequency hopping
 	//writeRegister(REG_HOP_PERIOD, (uint8_t) 0x00);		// set 0x24 to 0x00 only for receivers
 	
 	// 4. Init Frequency, config channel
-	setFreq(freq);
+	setFreq(LoraDown->freq);
 
 	// 6. Set power level, REG_PAC
-	setPow(powe);
+	setPow(LoraDown->powe);
 	
 	// 7. prevent node to node communication
-	writeRegister(REG_INVERTIQ, (uint8_t) iiq);					// 0x33, (0x27 or 0x40)
+	writeRegister(REG_INVERTIQ, (uint8_t) (LoraDown->iiq));	// 0x33, (0x27 or 0x40)
 	
 	// 8. set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP (or less for 1ch gateway)
     writeRegister(REG_DIO_MAPPING_1, (uint8_t)(
@@ -793,27 +734,33 @@ void txLoraModem(
 	opmode(OPMODE_FSTX);									// set 0x01 to 0x02 (actual value becomes 0x82)
 	
 	// 11, 12, 13, 14. write the buffer to the FiFo
-	sendPkt(payLoad, payLength);
-
-	// 15. wait extra delay out. The delayMicroseconds timer is accurate until 16383 uSec.
-	loraWait(tmst);
+	sendPkt(LoraDown->payLoad, LoraDown->payLength);
 	
 	//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);	
 	
 	//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) LoraDown->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
 	
 	// Reset the IRQ register
 	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, clear TXDONE
+//	writeRegister(REG_IRQ_FLAGS, (uint8_t) IRQ_LORA_TXDONE_MASK);// set 0x12 to 0x08, clear TXDONE
+	writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);// set 0x12 to 0xFF, clear TXDONE and others
 	
 	// 16. Initiate actual transmission of FiFo
 	opmode(OPMODE_TX);											// set 0x01 to 0x03 (actual value becomes 0x83)
-	
+
+#	if _MONITOR>=1
+	if (( debug>=1 ) && ( pdebug & P_TX )) {
+		String response = "Dwn txLoraModem:: end: ";
+		printDwn(LoraDown, response);
+		mPrint(response);
+	}
+#	endif //_MONITOR
+
 }// txLoraModem
 
 
@@ -985,8 +932,7 @@ void cadScanner()
 //	8.	Set interrupt masks
 //	9.	Clear INT flags
 // ----------------------------------------------------------------------------------------
-void initLoraModem(
-				)
+void initLoraModem()
 {
 	_state = S_INIT;
 #if defined(ESP32_ARCH)
@@ -995,7 +941,6 @@ void initLoraModem(
     digitalWrite(pins.rst, HIGH);
 	delayMicroseconds(10000);
 	digitalWrite(pins.ss, HIGH);
-
 #else
 	// Reset the transceiver chip with a pulse of 10 mSec
 	digitalWrite(pins.rst, HIGH);
@@ -1095,7 +1040,8 @@ void initLoraModem(
 // the receiver either in single message (CAD) of in continuous
 // reception (STD).
 // ----------------------------------------------------------------------------------------
-void startReceiver() {
+void startReceiver() 
+{
 	initLoraModem();								// XXX 180326, after adapting this function 
 	if (gwayConfig.cad) {
 #		if _DUSB>=1

+ 19 - 19
src/_oLED.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 // 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
@@ -22,18 +22,18 @@
 
 
 // --------------------------------------------------------------------	
-// Initilize the OLED functions.
-// This function will init the OLED screenb. Depending on the 
+// Initilize the Oled functions.
+// This function will init the Oled screen. Depending on the 
 // availability of the reset button it will reset the display first.
 // --------------------------------------------------------------------
 void init_oLED() 
 {
-#if OLED>=1
+#if _OLED>=1
 #if defined OLED_RST
 	pinMode(OLED_RST,OUTPUT);
-	digitalWrite(OLED_RST, LOW); 	// low to reset OLED
+	digitalWrite(OLED_RST, LOW); 	// low to reset Oled
 	delay(100); 
-	digitalWrite(OLED_RST, HIGH); 	// must be high to turn on OLED
+	digitalWrite(OLED_RST, HIGH); 	// must be high to turn on Oled
 	delay(50);
 #else
 	//
@@ -51,24 +51,24 @@ void init_oLED()
 }
 
 // --------------------------------------------------------------------
-// Activate the OLED. Always print the same info.
+// Activate the Oled. Always print the same info.
 // These are 4 fields:
 // SSID, IP, ID, 
 //
 // --------------------------------------------------------------------
 void acti_oLED() 
 {
-#if OLED>=1
+#if _OLED>=1
 	// Initialising the UI will init the display too.
 	display.clear();
 	
-# if OLED==1
+# if _OLED==1
 	display.setFont(ArialMT_Plain_16);
 	display.drawString(0, 0, "READY,  SSID=");
 	display.drawString(0, 16, WiFi.SSID());
 	display.drawString(0, 32, "IP=");
 	display.drawString(0, 48, WiFi.localIP().toString().c_str() );
-# elif OLED==2
+# elif _OLED==2
 	display.setFont(ArialMT_Plain_16);
 	display.drawString(0, 0, "READY,  SSID=");
 	display.drawString(0, 16, WiFi.SSID());
@@ -78,18 +78,18 @@ void acti_oLED()
 
 	display.display();
 	
-#endif // OLED
+#endif // _OLED
 	delay(4000);
 }
 
 // --------------------------------------------------------------------
-// Print a message on the OLED.
+// Print a message on the Oled.
 // Note: The whole message must fit in the buffer
 //
 // --------------------------------------------------------------------
 void msg_oLED(String mesg) 
 {
-#if OLED>=1
+#if _OLED>=1
     display.clear();
 
 	display.flipScreenVertically();
@@ -99,14 +99,14 @@ void msg_oLED(String mesg)
 
     display.display();
 	yield();
-#endif //OLED
+#endif // _OLED
 }
 
-// Print a smaller OLED message consisting of two strings
+// Print a larger Oled message consisting of two strings
 
 void msg_lLED(String mesg, String mesg2) 
 {
-#if OLED>=1
+#if _OLED>=1
     display.clear();
 
 	display.flipScreenVertically();
@@ -117,16 +117,16 @@ void msg_lLED(String mesg, String mesg2)
 	
     display.display();
 	yield();
-#endif //OLED
+#endif // _OLED
 }
 
 // --------------------------------------------------------------------
-// Print the OLED address in use
+// Print the Oled address in use
 //
 // --------------------------------------------------------------------
 void addr_oLED() 
 {
-#if OLED>=1
+#if _OLED>=1
 	#if _DUSB>=1
 		Serial.print(F("OLED_ADDR=0x"));
 		Serial.println(OLED_ADDR, HEX);

+ 3 - 3
src/_otaServer.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 //
@@ -17,7 +17,7 @@
 // over the air.
 // This code uses the ESPhttpServer functions to update the gateway.
 
-#if A_OTA==1
+#if _OTA==1
 
 //extern ArduinoOTAClass ArduinoOTA;
 
@@ -75,7 +75,7 @@ void setupOta(char *hostname) {
 #	endif //_MONITOR
 	
 	// Only if the Webserver is active also
-#if A_SERVER==2										// Displayed for the moment
+#if _SERVER==2										// Displayed for the moment
 	ESPhttpUpdate.rebootOnUpdate(false);
    
 	server.on("/esp", HTTP_POST, [&](){

+ 1 - 1
src/_repeater.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg
 //
 // All rights reserved. This program and the accompanying materials

+ 7 - 8
src/_sensor.ino

@@ -1,4 +1,4 @@
-// sensor.ino; 1-channel LoRa Gateway for ESP8266
+// sensor.ino; 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg
 //
 // All rights reserved. This program and the accompanying materials
@@ -40,7 +40,7 @@ void smartDelay(uint32_t ms)
 		while (sGps.available()) {
 			gps.encode(sGps.read());
 		}
-		yield();									// MMM Maybe avoid crashes
+		yield();									// MMM Maybe  enable to fill buffer
 	} while (millis() - start < ms);
 }
 #endif //_GPS
@@ -82,7 +82,7 @@ int LoRaSensors(uint8_t *buf) {
 		buf[0] = 0x86;								// 134; User code <lCode + len==3 + Parity
 		
 #		if _MONITOR>=1
-		if (debug>=0) {
+		if ((debug>=1) && (pdebug & P_MAIN)) {
 			response += "LoRaSensors:: ";
 		}
 #		endif //_MONITOR
@@ -90,7 +90,7 @@ int LoRaSensors(uint8_t *buf) {
 		// GPS sensor is the second server we check for
 #		if _GPS==1
 			smartDelay(10);							// Use GPS to return fast!
-			if (millis() > 5000 && gps.charsProcessed() < 10) {
+			if ((millis() > 5000) && (gps.charsProcessed() < 10)) {
 #				if _MONITOR>=1
 					mPrint("ERROR: No GPS data received: check wiring");
 #				endif //_MONITOR
@@ -102,8 +102,8 @@ int LoRaSensors(uint8_t *buf) {
 
 			// Use lcode to code messages to server
 #			if _MONITOR>=1
-			if ((debug>=1) && (pdebug & P_MAIN)){
-				response += ", Gps lcode:: lat="+String(gps.location.lat())+", lng="+String(gps.location.lng())+", alt="+String(gps.altitude.feet()/3.2808)+", sats="+String(gps.satellites.value());
+			if ((debug>=1) && (pdebug & P_MAIN)) {
+				response += " Gps lcode:: lat="+String(gps.location.lat())+", lng="+String(gps.location.lng())+", alt="+String(gps.altitude.feet()/3.2808)+", sats="+String(gps.satellites.value());
 			}
 #			endif //_MONITOR
 			tchars += lcode.eGpsL(gps.location.lat(), gps.location.lng(), gps.altitude.value(), gps.satellites.value(), buf + tchars);
@@ -504,7 +504,6 @@ int sensorPacket() {
 	uint8_t buff_up[512];								// Declare buffer here to avoid exceptions
 	uint8_t message[64]={ 0 };							// Payload, init to 0
 	uint8_t mlength = 0;
-	uint32_t tmst = micros();
 	struct LoraUp LUP;
 	uint8_t NwkSKey[16] = _NWKSKEY;
 	uint8_t AppSKey[16] = _APPSKEY;
@@ -604,7 +603,7 @@ int sensorPacket() {
 	// be expanded if the server expects JSON messages.
 	// Note2: We fake this sensor message when sending
 	//
-	int buff_index = buildPacket(tmst, buff_up, LUP, true);
+	uint16_t buff_index = buildPacket(buff_up, &LUP, true);
 	
 	frameCount++;
 	statc.msg_ttl++;					// XXX Should we count sensor messages as well?

+ 59 - 67
src/_stateMachine.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 // 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
@@ -80,9 +80,9 @@ void stateMachine()
 	
 #	if _MONITOR>=1
 	if (intr != flags) {
-		String response = "";
+		String response = "stateMachine:: Error: flags="+String(flags,HEX)+", ";
 		mStat(intr, response);
-		mPrint("FLAG  ::"+response);
+		mPrint(response);
 	}
 #	endif //_MONITOR
 
@@ -204,10 +204,10 @@ void stateMachine()
 		
 		// else, S_RX of S_TX for example
 		else {
-			//yield();												// May take too much time for RX
+			//
 		} // else S_RX or S_TX, TXDONE
 		
-		yield();
+		yield();										// if hopping is enabled
 		
 	}// intr==0 && gwayConfig.hop
 
@@ -277,8 +277,8 @@ void stateMachine()
 			rssi = readRegister(REG_RSSI);							// Read the RSSI
 			_rssi = rssi;											// Read the RSSI in the state variable
 
-			_event = 0;												// Make 0, as soon as we have an interrupt
-			detTime = micros();										// mark time that preamble detected
+			_event=0;												// Make 0, as soon as we have an interrupt
+			detTime=micros();										// mark time that preamble detected
 			
 #			if _MONITOR>=1
 			if ((debug>=1) && (pdebug & P_PRE)) {
@@ -287,6 +287,7 @@ void stateMachine()
 				mPrint(response);
 			}
 #			endif //_MONITOR
+			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
 			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);			// reset all interrupt flags
 			opmode(OPMODE_RX_SINGLE);								// set reg 0x01 to 0x06 for receiving
 			
@@ -382,7 +383,7 @@ void stateMachine()
 			}
 #			endif //_MONITOR
 			_state=S_SCAN;
-			//_event=1;												// XXX 06/03 loop until interrupt
+			//_event=1;												// XXX 19/06/03 loop until interrupt
 			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
 			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
 		}
@@ -430,6 +431,7 @@ void stateMachine()
 			// Reset all interrupts as soon as possible
 			// But listen ONLY to RXDONE and RXTOUT interrupts 
 			//writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDETD_MASK | IRQ_LORA_RXDONE_MASK);
+
 			// If we want to reset CRC, HEADER and RXTOUT flags as well
 			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF );			// XXX 180326, reset all CAD Detect interrupt flags
 			
@@ -443,7 +445,7 @@ void stateMachine()
 
 			detTime = micros();
 #			if _MONITOR>=1
-			if (( debug>=1 ) && ( pdebug & P_CAD )) {
+			if ((debug>=1) && (pdebug & P_CAD)) {
 				String response = "CAD:: ";
 				mStat(intr, response);
 				mPrint(response);
@@ -520,7 +522,7 @@ void stateMachine()
 				mPrint ("CAD:: intr is 0x00");
 			}
 #			endif //_MONITOR
-			_event=1;												// Stay in CAD _state until real interrupt
+			//_event=1;												// Stay in CAD _state until real interrupt
 		}
 		
 		// else we do not recognize the interrupt. We print an error
@@ -528,8 +530,8 @@ void stateMachine()
 		//
 		else {
 #			if _MONITOR>=1
-			if (( debug>=0) && ( pdebug & P_CAD )) { 
-				mPrint("Err CAD: Unknown::" + String(intr) ); 
+			if ( debug>=0) { 
+				mPrint("ERROR CAD: Unknown::" + String(intr) ); 
 			}
 #			endif //_MONITOR
 			_state = S_SCAN;
@@ -564,10 +566,9 @@ void stateMachine()
 			//
 			if (intr & IRQ_LORA_CRCERR_MASK) {
 #				if _MONITOR>=1
-				if (( debug>=0 ) && ( pdebug & P_RX )) {
-					String response = "";
+				if ((debug>=0) && (pdebug & P_RX)) {
+					String response = "UP CRC ERROR:: ";
 					mStat(intr, response);
-					Serial.print(F("Rx CRC err: "));
 				}
 #				endif //_MONITOR
 
@@ -597,8 +598,9 @@ void stateMachine()
 			
 			// If we are here, no CRC error occurred, start timer
 #			if _DUSB>=1 || _MONITOR>=1
-				uint32_t ffTime = micros();	
-#			endif			
+				uint32_t rxDoneTime = micros();	
+#			endif	
+		
 			// There should not be an error in the message
 			LoraUp.payLoad[0]= 0x00;								// Empty the message
 
@@ -612,12 +614,13 @@ void stateMachine()
 			
 			if((LoraUp.payLength = receivePkt(LoraUp.payLoad)) <= 0) {
 #				if _MONITOR>=1
-				if (( debug>=1 ) && ( pdebug & P_RX )) {
-					String response = "sMachine:: Error S-RX: payLenth=";
+				if ((debug>=0) && (pdebug & P_RX)) {
+					String response = "sMachine:: ERROR S-RX: payLength=";
 					response += String(LoraUp.payLength);
 					mPrint(response);
 				}
 #				endif //_MONITOR
+
 				_event=1;
 				writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);	// Reset the interrupt mask
 				writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
@@ -627,9 +630,9 @@ void stateMachine()
 			}
 			
 #			if _MONITOR>=1
-			if ((pdebug & P_RX) && (debug >= 2)) {
+			if ((debug >=2) && (pdebug & P_RX)) {
 				String response  = "RXDONE:: dT=";
-				response += String(ffTime - detTime);
+				response += String(rxDoneTime - detTime);
 				mStat(intr, response);
 				mPrint(response);
 			}
@@ -664,7 +667,7 @@ void stateMachine()
 			if (receivePacket() <= 0) {								// read is not successful
 #				if _MONITOR>=1
 				if (( debug>=0 ) && ( pdebug & P_RX )) {
-					mPrint("sMach:: Error receivePacket");
+					mPrint("sMach:: ERROR receivePacket");
 				}
 #				endif //_MONITOR
 			}
@@ -737,7 +740,7 @@ void stateMachine()
 			// which is normall an indication of RXDONE
 			//writeRegister(REG_IRQ_FLAGS, IRQ_LORA_HEADER_MASK);
 #			if _MONITOR>=1
-			if (( debug>=3 ) && ( pdebug & P_RX )) {
+			if ((debug>=3 ) && (pdebug & P_RX)) {
 				mPrint("RX HEADER:: " + String(intr));
 			}
 #			endif //_MONITOR
@@ -750,7 +753,7 @@ void stateMachine()
 		//
 		else if (intr == 0x00) {
 #			if _MONITOR>=1
-			if (( debug>=3) && ( pdebug & P_RX )) {
+			if ((debug>=3) && (pdebug & P_RX)) {
 				mPrint("S_RX no INTR:: " + String(intr));
 			}
 #			endif //_MONITOR
@@ -773,16 +776,16 @@ void stateMachine()
 
 	  
 	  // ----------------------------------------------------------------------------------------
-	  // Start the transmission of a message in state S-TX
+	  // Start the transmission of a message in state S-TX (DOWN)
 	  // This is not an interrupt state, we use this state to start transmission
 	  // the interrupt TX-DONE tells us that the transmission was successful.
 	  // It therefore is no use to set _event==1 as transmission might
 	  // not be finished in the next loop iteration
 	  //
 	  case S_TX:
-	  
+
 		// We need a timeout for this case. In case there does not come an interrupt,
-		// then there will nog be a TXDONE but probably another CDDONE/CDDETD before
+		// then there will not be a TXDONE but probably another CDDONE/CDDETD before
 		// we have a timeout in the main program (Keep Alive)
 		if (intr == 0x00) {
 #			if _MONITOR>=1
@@ -790,40 +793,33 @@ void stateMachine()
 				mPrint("TX:: 0x00");
 			}
 #			endif //_MONITOR
-			_event=1;
-			_state=S_TXDONE;
+			_event= 1;
 		}
 
+		loraWait(&LoraDown);
+	
 		// Set state to transmit
-		_state = S_TXDONE;
-		
 		// Clear interrupt flags and masks
 		writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
 		writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);				// reset interrupt flags
 		
 	  	// 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
-		);
-		// After filling the buffer we only react on TXDONE interrupt
+		txLoraModem(&LoraDown);
 		
+		// After filling the buffer we only react on TXDONE interrupt
+		// So, more or less start at the "case TXDONE:" below 
+		_state=S_TXDONE;
+		_event=1;													// Or remove the break below
+
 #		if _MONITOR>=1
-		if (( debug>=1 ) && ( pdebug & P_TX )) { 
-			mPrint("TX done:: " + String(intr) ); 
+		if (( debug>=1 ) && ( pdebug & P_TX )) {
+			String response="TX fini:: ";
+			mStat(intr, response);
+			mPrint(response); 
 		}
 #		endif //_MONITOR
-		// More or less start at the "case TXDONE:" below 
-		_state=S_TXDONE;
-		_event=1;													// Or remove the break below
-		
+
 	  break; // S_TX
 
 	  
@@ -839,17 +835,17 @@ void stateMachine()
 		if (intr & IRQ_LORA_TXDONE_MASK) {
 
 #			if _MONITOR>=1
-			if (( debug>=0 ) && ( pdebug & P_TX )) {
-				String response =  "T TXDONE:: rcvd=" + String(micros());
-				response += ", diff=" + String(micros()-LoraDown.tmst);
-				mPrint(response);
-			}
-#			endif //_MONITOR
-
-#			if _MONITOR>=2
-			if (pdebug & P_TX) {
-				String response  = "T TXDONE:: rcvd=" + micros();
-				response += ", diff=" + String(micros()-LoraDown.tmst);
+			if (( debug>=1 ) && ( pdebug & P_TX )) {
+				String response =  "Dwns TXDONE:: OK: rcvd=";
+				printInt(micros(),response);
+				if (micros() < LoraDown.tmst) {
+					response += ", diff=-" ;
+					printInt(LoraDown.tmst - micros(), response );
+				}
+				else {
+					response += ", diff=";
+					printInt(micros()-LoraDown.tmst, response);
+				}
 				mPrint(response);
 			}
 #			endif //_MONITOR
@@ -869,9 +865,10 @@ void stateMachine()
 			_event=0;
 			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
 			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);			// reset interrupt flags
+			
 #			if _MONITOR>=1
-			if (( debug>=1 ) && ( pdebug & P_TX )) {
-				mPrint("T TXDONE:: done OK");
+			if (( debug>=2 ) && ( pdebug & P_TX )) {
+				mPrint("TXDONE:: done OK");
 			}
 #			endif //_MONITOR
 		}
@@ -880,7 +877,7 @@ void stateMachine()
 		else if ( intr != 0 ) {
 #			if _MONITOR>=1
 			if (( debug>=0 ) && ( pdebug & P_TX )) {
-				String response =  "TXDONE:: unknown int:";
+				String response =  "TXDONE:: Error unknown intr=";
 				mStat(intr, response);
 				mPrint(response);
 			} //_MONITOR
@@ -906,11 +903,6 @@ void stateMachine()
 #				endif //_MONITOR
 				startReceiver();
 			}
-#			if _MONITOR>=1
-			if (( debug>=3 ) && ( pdebug & P_TX )) {
-				mPrint("T TXDONE:: No Interrupt");
-			}
-#			endif //_MONITOR
 		}
 	
 

+ 1 - 1
src/_tcpTTN.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 // 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway

+ 217 - 219
src/_txRx.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 // 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
@@ -18,9 +18,8 @@
 // ========================================================================================
 
 
-
-// ----------------------------------------------------------------------------
-// DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN 
+// ------------------------------- DOWN ----------------------------------------
+//
 // Send DOWN a LoRa packet over the air to the node. This function does all the 
 // decoding of the server message and prepares a Payload buffer.
 // The payload is actually transmitted by the sendPkt() function.
@@ -29,7 +28,7 @@
 // NOTE: This is not an interrupt function, but is started by loop().
 // The _status is set an the end of the function to TX and in _stateMachine
 // function the actual transmission function is executed.
-// The LoraDown.tmst contains the timestamp that the tranmission should finish.
+// The LoraDown.tmst contains the timestamp that the tranmission should finish (Node start reading).
 // ----------------------------------------------------------------------------
 int sendPacket(uint8_t *buf, uint8_t length) 
 {
@@ -48,7 +47,6 @@ int sendPacket(uint8_t *buf, uint8_t length)
 	// 12-byte header;
 	//		HDR (1 byte)
 	//		
-	//
 	// Data Reply for JOIN_ACCEPT as sent by server:
 	//		AppNonce (3 byte)
 	//		NetID (3 byte)
@@ -58,46 +56,45 @@ int sendPacket(uint8_t *buf, uint8_t length)
 	//		CFList (fill to 16 bytes)
 			
 	int i=0;
-	StaticJsonDocument<312> jsonBuffer;							// Use of arduinoJson version 6!
+	//StaticJsonDocument<312> jsonBuffer;							// Use of arduinoJson version 6!
 	char * bufPtr = (char *) (buf);
 	buf[length] = 0;
 	
 #	if _MONITOR>=1
 	if (( debug>=2) && (pdebug & P_TX)) {
-		mPrint("sendPacket:: " + String((char *)buf) + "< ");
+		mPrint("Dwn sendPacket:: " + String((char *)buf) + "< ");
 	}
 #	endif //_MONITOR
 
 	// Use JSON to decode the string after the first 4 bytes.
 	// The data for the node is in the "data" field. This function destroys original buffer
-	auto error = deserializeJson(jsonBuffer, bufPtr);
-		
+	auto error = deserializeJson(jsonBuffer, bufPtr);		
 	if (error) {
 #		if _MONITOR>=1
-		if (( debug>=1) && (pdebug & P_TX)) {
-			mPrint("T sendPacket:: ERROR Json Decode: " + String(bufPtr) );
+		if ((debug>=0) && (pdebug & P_TX)) {
+			mPrint("Dwn sendPacket:: ERROR: Json Decode: " + String(bufPtr) );
 		}
 #		endif //_MONITOR
 		return(-1);
 	}
-	yield();
 	
 	// Meta Data sent by server (example)
 	// {"txpk":{"codr":"4/5","data":"YCkEAgIABQABGmIwYX/kSn4Y","freq":868.1,"ipol":true,"modu":"LORA","powe":14,"rfch":0,"size":18,"tmst":1890991792,"datr":"SF7BW125"}}
 
 	// Used in the protocol of Gateway:
+
 	JsonObject root		= jsonBuffer.as<JsonObject>();	// 191111 Avoid Crashes
-	
+
+	LoraDown.tmst		= (uint32_t) root["txpk"]["tmst"].as<unsigned long>();
 	const char * data	= root["txpk"]["data"];			// Downstream Payload
 	uint8_t psize		= root["txpk"]["size"];			// Payload size
 	bool ipol			= root["txpk"]["ipol"];
-	uint8_t powe		= root["txpk"]["powe"];			// power, e.g. 14 or 27
-	LoraDown.tmst		= (uint32_t) root["txpk"]["tmst"].as<unsigned long>();
+//	uint8_t powe		= root["txpk"]["powe"];			// power, e.g. 14 or 27
 	
 	// Not used in the protocol of Gateway TTN:
 	const char * datr	= root["txpk"]["datr"];			// eg "SF7BW125"
-	const char * modu	= root["txpk"]["modu"];			// =="LORA"
-	const char * codr	= root["txpk"]["codr"];			// e.g. "4/5"
+	//const char * modu	= root["txpk"]["modu"];			// =="LORA"
+	//const char * codr	= root["txpk"]["codr"];			// e.g. "4/5"
 	//if (root["txpk"].containsKey("imme") ) {
 	//	const bool imme = root["txpk"]["imme"];			// Immediate Transmit (tmst don't care)
 	//}
@@ -111,7 +108,7 @@ int sendPacket(uint8_t *buf, uint8_t length)
 	}
 	else {												// There is data!
 #		if _MONITOR>=1
-		if ((debug>=0) && ( pdebug & P_TX )) {
+		if ((debug>=0) && (pdebug & P_TX)) {
 			mPrint("sendPacket:: ERROR: data is NULL");
 		}
 #		endif //_MONITOR
@@ -125,81 +122,79 @@ int sendPacket(uint8_t *buf, uint8_t length)
 	base64_decode((char *) payLoad, (char *) data, strlen(data));	// Fill payload w decoded message
 
 	// Compute wait time in microseconds
-	uint32_t w = (uint32_t) (LoraDown.tmst - micros());	// Wait Time compute
+	int32_t w = (int32_t) (LoraDown.tmst - micros());	// Wait Time compute
 
 // _STRICT_1CH determines how we will react on downstream messages.
 //
 // If _STRICT==1, we will answer (in the RX1 timeslot) on the frequency we receive on.
-// We will anser in RX2 in rthe time set by _RX2_SF.
+// We will anser in RX2 in the Spreading Factor set by _RX2_SF (set in configGway.h)
 // This way, we can better communicate as a single gateway machine
-// Otherwise we will answer in RX with RF==12 and use special answer frequency
 //
 #if _STRICT_1CH == 1
 	// RX1 is requested frequency
 	// RX2 is SF _RX2_SF probably SF9
 	// If possible use RX1 timeslot as this is our frequency.
-	// Do not use RX2 or JOIN2 as they contain other frequencies
+	// Do not use RX2 or JOIN2 as they contain other frequencies (868.5 MHz)
+
+	LoraDown.powe	= 14;								// On all freqs except 869.5MHz power is limited
+	LoraDown.freq	= freqs[gwayConfig.ch].dwnFreq;		// Use the requestor Down frequency (always)
 	
-	// Wait time RX1
-	if ((w>1000000) && (w<3000000)) { 
-		LoraDown.tmst-=1000000; 
-		LoraDown.sfTx= sfi;									// Take care, TX sf not to be mixed with SCAN
+	// Wait time RX1, between 1 and 2 seconds, or OTAA between 6 and 7 seconds
+	if (((w>0000000) && (w<2000000)) ||
+		((w>5000000) && (w<6000000)) )
+	{ 													// LoraDown.sfTx set by initiator
+#		ifdef _PROFILER
+		if ((debug>=2) && (pdebug & P_TX)) {
+			mPrint("loraPacket:: RX1: micros="+String(micros())); 
+		}
+#		endif //_PROFILER
+	}
+
+	// RX2. 
+	else if (((w>2000000) && (w<3000000)) ||
+			 ((w>6000000) && (w<7000000)) )
+	{ 
+		LoraDown.sfTx= _RX2_SF;							// Use the RX2 downstream SF (may be dedicated to TTN)
 	}
-	// RX2. Is tmst correction necessary
-	else if ((w>6000000) && (w<7000000)) { 
-		LoraDown.tmst-=500000; 								// Corrrect the Timestamp
-		LoraDown.sfTx= _RX2_SF;								// Use the RX2 downstream SF (may be dedicated to TTN)
+	
+	else {
+#		if _PROFILER>=1
+		if ((debug>=2) && (pdebug & P_TX)) {
+			mPrint("_STRICT==1:: Not RX1 or RX2, wait= "+String(w/1000000)+"."+String(w%1000000)+", SF="+String(LoraDown.sfTx)+", Freq="+LoraDown.freq );
+		}
+#		endif //_PROFILER
+		// And do not convert the down SF.
 	}
-	LoraDown.powe	= 14;									// On all freqs except 869.5MHz power is limited
-	LoraDown.fff	= freqs[gwayConfig.ch].dwnFreq;			// Use the corresponding Down frequency
 
 #else
-// Elif _STRICT_1CH == 0, we will receive messags from the TTN gateway presumably on SF9/869.5MHz
+// elif _STRICT_1CH == 0, we will receive messags from the TTN gateway presumably on SF9/869.5MHz
 // And since the Gateway is a single channel gateway, and its nodes are probably
 // single channel too. They will not listen to that frequency at all.
 // Pleae note that this parameter is more for nodes (that cannot change freqs)
 // than for gateways.
+// We will probably answer in RX with RF==12 and use special answer frequency
 //
-	LoraDown.powe = powe;
-	const float ff		= root["txpk"]["freq"];			// eg 869.525
+	LoraDown.powe	= root["txpk"]["powe"];
+	const float ff	= root["txpk"]["freq"];				// eg 869.525
 	// convert double frequency (MHz) into uint32_t frequency in Hz.
-	LoraDown.fff = (uint32_t) ((uint32_t)((ff+0.000035)*1000)) * 1000;
+	LoraDown.freq = (uint32_t) ((uint32_t)((ff+0.000035)*1000)) * 1000;
 #endif //_STRICT_1CH
+
+	yield();
 	
 	LoraDown.payLoad = payLoad;				
 
 #	if _MONITOR>=1
-	if (( debug>=1 ) && ( pdebug & P_TX)) {
-	
-		mPrint("T LoraDown tmst=" + String(LoraDown.tmst));
-		
-		if ( debug>=2 ) {
-			Serial.print(F(" Request:: "));
-			Serial.print(F(" tmst="));		Serial.print(LoraDown.tmst); Serial.print(F(" wait=")); Serial.println(w);
-		
-			Serial.print(F(" strict="));	Serial.print(_STRICT_1CH);
-			Serial.print(F(" datr="));		Serial.println(datr);
-			Serial.print(F(" Rfreq=")); 	Serial.print(freqs[gwayConfig.ch].dwnFreq); 
-			Serial.print(F(" ->")); 		Serial.println(LoraDown.fff);
-			Serial.print(F(" sf  =")); 		Serial.print(atoi(datr+2)); Serial.print(F(" ->")); Serial.println(LoraDown.sfTx);
-		
-			Serial.print(F(" modu="));		Serial.println(modu);
-			Serial.print(F(" powe="));		Serial.println(powe);
-			Serial.print(F(" codr="));		Serial.println(codr);
-
-			Serial.print(F(" ipol="));		Serial.println(ipol);
-			Serial.println();
-		}
+	if (( debug>=2 ) && ( pdebug & P_TX)) {
+		mPrint("Dwn sendPacket:: TX tmst=" + String(LoraDown.tmst));
 	}
 #	endif // _MONITOR
 
 	if (LoraDown.payLength != psize) {
 #		if _MONITOR>=1
-		Serial.print(F("sendPacket:: WARNING payLength: "));
-		Serial.print(LoraDown.payLength);
-		Serial.print(F(", psize="));
-		Serial.println(psize);
-		if (debug>=2) Serial.flush();
+		if (debug>=0) {
+			mPrint("Dwn sendPacket:: WARNING payLength=" + String(LoraDown.payLength) + ", psize=" + String(psize) );
+		}
 #		endif //_MONITOR
 	}
 #	if _MONITOR>=1
@@ -221,30 +216,24 @@ int sendPacket(uint8_t *buf, uint8_t length)
 		case 2: statc.msg_down_2++; break;
 	}
 
-#	if _MONITOR>=1
-	if (( debug>=2 ) && ( pdebug & P_TX )) {
-		mPrint("T sendPacket:: fini OK");
-	}
-#	endif //_MONITOR
-
 	// All data is in Payload and parameters and need to be transmitted.
 	// The function is called in user-space
 	_state = S_TX;										// _state set to transmit
 	
 #	if _MONITOR>=1
-	if ((debug>=1) && ( pdebug & P_TX)) {
+	if ((debug>=2) && ( pdebug & P_TX)) {
 		mPrint("sendPacket:: STRICT=" + String(_STRICT_1CH) );
 	}
 #	endif //_MONITOR
 	
 	return 1;
-}//sendPacket DOWN
+} //sendPacket DOWN
 
 
 
 
-// ----------------------------------------------------------------------------
-// UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP
+// --------------------------------- UP ---------------------------------------
+//
 // Based on the information read from the LoRa transceiver (or fake message)
 // build a gateway message to send upstream (to the user somewhere on the web).
 //
@@ -257,19 +246,18 @@ int sendPacket(uint8_t *buf, uint8_t length)
 // returns:
 //	buff_index:
 // ----------------------------------------------------------------------------
-int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool internal) 
+int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal) 
 {
 	int32_t SNR;
     int16_t rssicorr;
 	int16_t prssi;										// packet rssi
 	
 	char cfreq[12] = {0};								// Character array to hold freq in MHz
-	//lastTmst = tmst;									// Following/according to spec
-	int buff_index=0;
+	uint16_t buff_index=0;
 	char b64[256];
 	
-	uint8_t *message = LoraUp.payLoad;
-	char messageLength = LoraUp.payLength;
+	uint8_t *message = LoraUp->payLoad;
+	char messageLength = LoraUp->payLength;
 		
 #if _CHECK_MIC==1
 	unsigned char NwkSKey[16] = _NWKSKEY;
@@ -284,9 +272,9 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte
 		rssicorr = 157;
 	}
 	else {
-		SNR = LoraUp.snr;
-		prssi = LoraUp.prssi;								// read register 0x1A, packet rssi
-		rssicorr = LoraUp.rssicorr;
+		SNR = LoraUp->snr;
+		prssi = LoraUp->prssi;								// read register 0x1A, packet rssi
+		rssicorr = LoraUp->rssicorr;
 	}
 
 #if _STATISTICS >= 1
@@ -300,25 +288,25 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte
 #if _LOCALSERVER==1
 	statr[0].datal=0;
 	int index;
-	if ((index = inDecodes((char *)(LoraUp.payLoad+1))) >=0 ) {
+	if ((index = inDecodes((char *)(LoraUp->payLoad+1))) >=0 ) {
 
-		uint16_t frameCount=LoraUp.payLoad[7]*256 + LoraUp.payLoad[6];
+		uint16_t frameCount=LoraUp->payLoad[7]*256 + LoraUp->payLoad[6];
 		
-		for (int k=0; (k<LoraUp.payLength) && (k<23); k++) {
-			statr[0].data[k] = LoraUp.payLoad[k+9];
+		for (int k=0; (k<LoraUp->payLength) && (k<23); k++) {
+			statr[0].data[k] = LoraUp->payLoad[k+9];
 		};
 		
 		// XXX Check that k<23 when leaving the for loop
 		// XXX or we can not display in statr
 		
 		uint8_t DevAddr[4]; 
-		DevAddr[0]= LoraUp.payLoad[4];
-		DevAddr[1]= LoraUp.payLoad[3];
-		DevAddr[2]= LoraUp.payLoad[2];
-		DevAddr[3]= LoraUp.payLoad[1];
+		DevAddr[0]= LoraUp->payLoad[4];
+		DevAddr[1]= LoraUp->payLoad[3];
+		DevAddr[2]= LoraUp->payLoad[2];
+		DevAddr[3]= LoraUp->payLoad[1];
 
 		statr[0].datal = encodePacket((uint8_t *)(statr[0].data), 
-								LoraUp.payLength-9-4, 
+								LoraUp->payLength -9 -4, 
 								(uint16_t)frameCount, 
 								DevAddr, 
 								decodes[index].appKey, 
@@ -326,29 +314,15 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte
 	}
 #endif //_LOCALSERVER
 
-	statr[0].tmst = now();
+	statr[0].time = now();								// Not a real timestamp. but the current time
 	statr[0].ch =	gwayConfig.ch;
 	statr[0].prssi = prssi - rssicorr;
-	statr[0].sf =	LoraUp.sf;
+	statr[0].sf =	LoraUp->sf;
 	
 #	if RSSI==1
 		statr[0].rssi = _rssi - rssicorr;
 #	endif // RSII
 
-#	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]<0x10) Serial.print('0');
-				Serial.print(message[i],HEX);
-				Serial.print(' ');
-			}
-			Serial.println();
-		}
-	}
-#	endif //DUSB
-
 	statr[0].node = ( message[1]<<24 | message[2]<<16 | message[3]<<8 | message[4] );
 
 #if _STATISTICS >= 2
@@ -423,12 +397,12 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte
 					Serial.print(' ');
 		}
 		Serial.println();
-		yield();
+		yield();										// only if debug>=2
 	}
 #endif // _MONITOR
 
-// Show received message status on OLED display
-#if OLED>=1
+// Show received message status on Oled display
+#if _OLED>=1
     char timBuff[20];
     sprintf(timBuff, "%02i:%02i:%02i", hour(), minute(), second());
 	
@@ -455,22 +429,23 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte
     display.drawString(0, 48, "LEN: " );
     display.drawString(40, 48, String((int)messageLength) );
     display.display();
-	//yield();
 
-#endif //OLED>=1
+#endif // _OLED>=1
 			
-	int j;
+//	int j;
 	
 	// XXX Base64 library is nopad. So we may have to add padding characters until
 	// 	message Length is multiple of 4!
 	// Encode message with messageLength into b64
 	int encodedLen = base64_enc_len(messageLength);		// max 341
+
 #	if _MONITOR>=1
-	if ((debug>=1) && (encodedLen>255) && ( pdebug & P_RADIO )) {
+	if ((debug>=1) && (encodedLen>255) && (pdebug & P_RADIO)) {
 		mPrint("R buildPacket:: b64 err, len=" + String(encodedLen));
 		return(-1);
 	}
 #	endif // _MONITOR
+
 	base64_encode(b64, (char *) message, messageLength);// max 341
 	// start composing datagram with the header 
 	uint8_t token_h = (uint8_t)rand(); 					// random token
@@ -479,7 +454,6 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte
 	// pre-fill the data buffer with fixed fields
 	buff_up[0] = PROTOCOL_VERSION;						// 0x01 still
 
-
 	buff_up[1] = token_h;
 	buff_up[2] = token_l;
 	
@@ -495,118 +469,118 @@ 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];
 
-
-	buff_index = 12; 									// 12-byte binary (!) header
-
+	buff_index	= 12; 									// 12-byte binary (!) header
+	
 	// start of JSON structure that will make payload
-	memcpy((void *)(buff_up + buff_index), (void *)"{\"rxpk\":[", 9);
-	buff_index += 9;
-	buff_up[buff_index] = '{';
-	++buff_index;
-	j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, "\"tmst\":%u", tmst);
+	memcpy((void *)(buff_up + buff_index), (void *)"{\"rxpk\":[{", 10); buff_index += 10;
 	
-#	if _MONITOR>=1
-	if ((j<0) && ( debug>=1 ) && ( pdebug & P_RADIO )) {
-		mPrint("buildPacket:: Error ");
-	}
-#	endif //_MONITOR
+// More versions are defined for the moment, in order to keep timing as low as [possible. 
+// The serializeJson() version hopefully is quicker
 
-	buff_index += j;
-	ftoa((double)freqs[gwayConfig.ch].upFreq / 1000000, cfreq, 6);				// XXX This can be done better
+#ifdef _JSONENCODE
+//------------------
+	StaticJsonDocument<400> doc;
+	// MMM Get rid of this code when ready
 	
-	j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%s", 0, 0, cfreq);
-	buff_index += j;
-	memcpy((void *)(buff_up + buff_index), (void *)",\"stat\":1", 9);
-	buff_index += 9;
-	memcpy((void *)(buff_up + buff_index), (void *)",\"modu\":\"LORA\"", 14);
-	buff_index += 14;
+	//doc["time"] = "";
+
+	doc["chan"] = "0";
+	doc["rfch"] = "0";
+	doc["freq"] = "" + (freqs[gwayConfig.ch].upFreq / 1000000);
+	doc["stat"] = "1";
+	doc["modu"] = "LORA";
+	doc["datr"] = "SF" + String(LoraUp->sf) + "BW125";
+	doc["rssi"] = "" +(prssi-rssicorr);
+	doc["lsnr"] = "" +(long)SNR;
+	doc["codr"] = "4/5";
+
+	// Use gBase64 library to fill in the data string
+	encodedLen = base64_enc_len(messageLength);			// max 341	
+	doc["size"] = "" + encodedLen;
+
+	int len= base64_encode(doc["data"], (char *)message, messageLength);
+
+	LoraUp->tmst = doc["tmst"] = "" + (uint32_t) micros() + _RXDELAY1;		// Tmst correction							
 	
-	/* Lora datarate & bandwidth, 16-19 useful chars */
-	switch (LoraUp.sf) {
-		case SF6:
-			memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF6", 12);
-			buff_index += 12;
-			break;
-		case SF7:
-			memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF7", 12);
-			buff_index += 12;
-			break;
-		case SF8:
-            memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF8", 12);
-            buff_index += 12;
-            break;
-		case SF9:
-            memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF9", 12);
-            buff_index += 12;
-            break;
-		case SF10:
-            memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF10", 13);
-            buff_index += 13;
-            break;
-		case SF11:
-            memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF11", 13);
-            buff_index += 13;
-            break;
-		case SF12:
-            memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF12", 13);
-            buff_index += 13;
-            break;
-		default:
-            memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF?", 12);
-            buff_index += 12;
-	}
-	memcpy((void *)(buff_up + buff_index), (void *)"BW125\"", 6); buff_index += 6;
-	memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"4/5\"", 13); buff_index += 13;
-	buff_index += snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"lsnr\":%li", (long)SNR);
-	buff_index += snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"rssi\":%d,\"size\":%u", prssi-rssicorr, messageLength);
-	memcpy((void *)(buff_up + buff_index), (void *)",\"data\":\"", 9); buff_index += 9;
+	const char	* p =  (const char *) & (buff_up [buff_index]);				// Start in buff where to put the serializedJson
+	int written = serializeJson(doc, (const char *)p, buff_index+20 );		// size is buff_index + encoded data + some closing chars
+
+
+#else // _JSONENCODE undefined or ==0, this is default
+// -----------------
+	ftoa((double)freqs[gwayConfig.ch].upFreq / 1000000, cfreq, 6);		// XXX This can be done better
+	if ((LoraUp->sf<6) || (LoraUp->sf>12)) { 				// Lora datarate & bandwidth SF6-SF12, 16-19 useful chars */
+		LoraUp->sf=7;
+	}			
+
+//	buff_index += snprintf((char *)(buff_up + buff_index), 
+//		TX_BUFF_SIZE-buff_index, 
+//		"%04d-%02d-%02d %02d:%02d:%02d CET", 
+//		year(),month(),day(),hour(),minute(),second());
+
+	buff_index += snprintf((char *)(buff_up + buff_index), 
+		TX_BUFF_SIZE-buff_index, 
+		"\"chan\":%1u,\"rfch\":%1u,\"freq\":%s,\"stat\":1,\"modu\":\"LORA\"" , 
+		0, 0, cfreq);
+	
+	buff_index += snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index
+		, ",\"datr\":\"SF%uBW125\",\"codr\":\"4/5\",\"lsnr\":%li,\"rssi\":%d,\"size\":%u,\"data\":\""
+		, LoraUp->sf, (long)SNR, prssi-rssicorr, messageLength);
 
 	// Use gBase64 library to fill in the data string
-	encodedLen = base64_enc_len(messageLength);			// max 341
+	encodedLen = base64_enc_len(messageLength);				// max 341
 	buff_index += base64_encode((char *)(buff_up + buff_index), (char *) message, messageLength);
 
-	buff_up[buff_index] = '"';
-	++buff_index;
+	
+	LoraUp->tmst = (uint32_t) micros()+ _RXDELAY1; 			// MMM Correct timing with defined number,
+															// https://github.com/TheThingsNetwork/lorawan-stack/issues/277
+
+	// Get rid of this code when ready	
+
+	buff_index += snprintf((char *)(buff_up + buff_index), 
+		TX_BUFF_SIZE-buff_index, "\",\"tmst\":%u", 
+		LoraUp->tmst);	
+
+#endif // _JSONENCODE undefined or ==0
+// ---------------------
+
 
 	// End of packet serialization
-	buff_up[buff_index] = '}';
-	++buff_index;
-	buff_up[buff_index] = ']';
-	++buff_index;
+	buff_up[buff_index]   = '}'; 
+	buff_up[buff_index+1] = ']'; 
+	buff_up[buff_index+2] = '}'; 
+	buff_index += 3;
 	
-	// end of JSON datagram payload */
-	buff_up[buff_index] = '}';
-	++buff_index;
 	buff_up[buff_index] = 0; 							// add string terminator, for safety
 
-// When we have the node address and the SF, fill the listSeen array
-// with the required data. _MAXSEEN must be >0 for this to happen.
-// statr[0] contains the statistics of the node last seen.
-#if  _MAXSEEN >= 1
-	yield();											// MMM May not be necessary
-	addSeen(listSeen, statr[0] );
-#endif
-
-#if _STAT_LOG == 1	
-	// Do statistics logging. In first version we might only
-	// write part of the record to files, later more
-	addLog( (unsigned char *)(buff_up), buff_index );
-#endif //_STAT_LOG
-	
+	// When we have the node address and the SF, fill the listSeen array
+	// with the required data. _MAXSEEN must be >0 for this to happen.
+	// statr[0] contains the statistics of the node last seen.
+#	if  _MAXSEEN >= 1
+		//yield();										// MMM 200316 Huge influence !!! on timing
+		addSeen(listSeen, statr[0]);
+#	endif
+
+#	if _STAT_LOG == 1	
+		// Do statistics logging. In first version we might only
+		// write part of the record to files, later more
+		addLog( (unsigned char *)(buff_up), buff_index );
+#	endif //_STAT_LOG
+
 #	if _MONITOR>=1
-	if (( debug>=2 ) && ( pdebug & P_RX )) {			// debug: display JSON payload
-		mPrint("RXPK:: "+String((char *)(buff_up + 12))+"R RXPK:: package length="+String(buff_index));		
+	if ((debug>=2) && (pdebug & P_RX)) {			// debug: display JSON payload
+		mPrint("UP RXPK:: "+String((char *)(buff_up + 12))+" , length="+String(buff_index));		
 	}
 #	endif
-	return(buff_index);
-}// buildPacket
-
 
+	return(buff_index);
+	
+}// buildPacket()
 
 
 
-// ----------------------------------------------------------------------------
-// UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP 
+// --------------------------------- UP ---------------------------------------
+//
 // Receive a LoRa package over the air, LoRa and deliver to server(s)
 //
 // Receive a LoRa message and fill the buff_up char buffer.
@@ -629,40 +603,61 @@ int receivePacket()
 	// in one UDP message as the Semtech Gateway spec does allow this.
 	// XXX Bit ... Not yet supported
 
-		// Take the timestamp as soon as possible, to have accurate reception timestamp
-		// TODO: tmst can jump if micros() overflow.
-		uint32_t tmst = (uint32_t) micros();			// Only microseconds, rollover in 5X minutes
+#ifdef _PROFILER
+	if ((debug>=1) && (pdebug & P_RX)) {
+		String response = "UP receivePacket:: start: micr=";
+			printInt(micros(),response);
+			response += ", tmst=";
+			printInt(LoraUp.tmst,response);
+			mPrint(response);
+	}
+#endif // _PROFILER
 		
 		// Handle the physical data read from LoraUp
 		if (LoraUp.payLength > 0) {
 
 			// externally received packet, so last parameter is false (==LoRa external)
-            int build_index = buildPacket(tmst, buff_up, LoraUp, false);
+			// Make a buffer to transmit later
+            int build_index = buildPacket(buff_up, &LoraUp, false);
 
-			// REPEATER is a special function where we retransmit received 
+			// REPEATER is a special function where we retransmit package received 
 			// message on _ICHANN to _OCHANN.
 			// Note:: For the moment _OCHANN is not allowed to be same as _ICHANN
-#if _REPEATER==1
+#			if _REPEATER==1
 			if (!sendLora(LoraUp.payLoad, LoraUp.payLength)) {
 				return(-3);
 			}
-#endif
-			
+#			endif
+
+#			ifdef _TTNSERVER	
 			// This is one of the potential problem areas.
 			// If possible, USB traffic should be left out of interrupt routines
 			// rxpk PUSH_DATA received from node is rxpk (*2, par. 3.2)
-#ifdef _TTNSERVER
+
 			if (!sendUdp(ttnServer, _TTNPORT, buff_up, build_index)) {
 				return(-1); 							// received a message
 			}
+#			endif //_TTNSERVER
+
 			yield();
-#endif
-			// Use our own defined server or a second well kon server
-#ifdef _THINGSERVER
+			Udp.flush();								// MMM 200419
+
+#ifdef _PROFILER
+			if ((debug>=1) && (pdebug & P_RX)) {
+				String response = "UP receivePacket:: sendUdp: micr=";
+				printInt(micros(),response);
+				response += ", tmst=";
+				printInt(LoraUp.tmst,response);
+				mPrint(response);
+			}
+#endif // _PROFILER
+
+#			ifdef _THINGSERVER
+			// Use our own defined server or a second well known server
 			if (!sendUdp(thingServer, _THINGPORT, buff_up, build_index)) {
 				return(-2); 							// received a message
 			}
-#endif
+#			endif //_THINGSERVER
 
 #if _LOCALSERVER==1
 			// Or special case, we do not use a local server to receive
@@ -698,7 +693,7 @@ int receivePacket()
 				//uint16_t frameCount=LoraUp.payLoad[7]*256 + LoraUp.payLoad[6];
 
 #if _DUSB>=1
-				if (( debug>=1 ) && ( pdebug & P_RX )) {
+				if ((debug>=2) && (pdebug & P_RX)) {
 					Serial.print(F("R receivePacket:: Ind="));
 					Serial.print(index);
 					Serial.print(F(", Len="));
@@ -719,6 +714,7 @@ int receivePacket()
 					Serial.println();
 				}
 #endif //DUSB
+
 			}
 #			if _MONITOR>=1
 			else if (( debug>=2 ) && ( pdebug & P_RX )) {
@@ -730,6 +726,8 @@ int receivePacket()
 			// Reset the message area
 			LoraUp.payLength = 0;
 			LoraUp.payLoad[0] = 0x00;
+			
+			
 			return(build_index);
         }
 		

+ 270 - 120
src/_udpSemtech.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 // 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
@@ -28,7 +28,7 @@
 
 // The following functions ae defined in this module:
 // int readUdp(int Packetsize)
-// int sendUdp(IPAddress server, int port, uint8_t *msg, int length)
+// int sendUdp(IPAddress server, int port, uint8_t *msg, uint16_t length)
 // bool connectUdp();
 // void pullData();
 // void sendstat();
@@ -72,76 +72,87 @@ bool connectUdp()
 }// connectUdp
 
 
-// ----------------------------------------------------------------------------
-// DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN 
+// ----------------------------------- DOWN -----------------------------------
+//
 // readUdp()
 // Read DOWN a package from UDP socket, can come from any server
 // Messages are received when server responds to gateway requests from LoRa nodes 
 // (e.g. JOIN requests etc.) or when server has downstream data.
-// We respond only to the server that sent us a message!
+// We respond only to the server that sents us a message!
 //
 // Note: So normally we can forget here about codes that do upstream
 //
 // Parameters:
 //	Packetsize: size of the buffer to read, as read by loop() calling function
 //
+//	Byte 0: 	Contains Protocol Version
+//	Byte 1+2:	Contain Token
+//	Byte 3:		Contains PULL_RESP or other identifier
+//	Byte 4 >	Contains payload (or Gateway EUI 8 bytes first)
+//
 // Returns:
 //	-1 or false if not read
-//	Or number of characters read is success
+//	Or number of characters read if success
 //
 // ----------------------------------------------------------------------------
 int readUdp(int packetSize)
 { 
-	uint8_t buff[32]; 						// General buffer to use for UDP, set to 64
+	uint8_t buff[32]; 						// General buffer to use for UDP, set to 32
 	uint8_t buff_down[RX_BUFF_SIZE];		// Buffer for downstream
 
-	if (WlanConnect(10) < 0) {
+	// Make sure we are connected over WiFI
+	if (WlanConnect(10) < 0) {				// MMM 200316 Every call contains yield()
 #		if _MONITOR>=1
-			mPrint("readUdp: ERROR connecting to WLAN");
+			mPrint("Dwn readUdp:: ERROR connecting to WLAN");
 #		endif //_MONITOR
 		Udp.flush();
-		yield();
 		return(-1);
 	}
-
-	yield();
+	
+	//yield();								// MMM 200320 Clear buffer in kernel (?)
 	
 	if (packetSize > RX_BUFF_SIZE) {
 #		if _MONITOR>=1
-			mPrint("readUdp:: ERROR package of size: " + String(packetSize));
+			mPrint("Dwn readUdp:: ERROR package of size: " + String(packetSize));
 #		endif //_MONITOR
 		Udp.flush();
 		return(-1);
 	}
-  
+
+	//yield();								// MMM 200406
+	
 	// We assume here that we know the originator of the message.
 	// In practice however this can be any sender!
 	if (Udp.read(buff_down, packetSize) < packetSize) {
 #		if _MONITOR>=1
-			mPrint("readUdp:: Reading less chars");
+			mPrint("Dwn readUdp:: Reading less chars");
 #		endif //_MONITOR
 		return(-1);
 	}
 
+	yield();								// MMM 200406
+
 	// Remote Address should be known
 	IPAddress remoteIpNo = Udp.remoteIP();
 
 	// Remote port is either of the remote TTN server or from NTP server (=123)
 	
 	unsigned int remotePortNo = Udp.remotePort();
-	if (remotePortNo == 123) {
+	if (remotePortNo == 123) {				// NTP message arriving, not expected
 		// This is an NTP message arriving
 #		if _MONITOR>=1
 		if (debug>=0) {
-			mPrint("readUdp:: NTP msg rcvd");
+			mPrint("Dwn readUdp:: NTP msg rcvd");
 		}
 #		endif //_MONITOR
 		gwayConfig.ntpErr++;
 		gwayConfig.ntpErrTime = now();
+		Udp.flush();						// MMM 200326 Clear buffer when time response arrives (error)
 		return(0);
 	}
 	
 	// If it is not NTP it must be a LoRa message for gateway or node
+	
 	else {
 		uint8_t *data = (uint8_t *) ((uint8_t *)buff_down + 4);
 		//uint8_t protocol= buff_down[0];
@@ -149,135 +160,268 @@ int readUdp(int packetSize)
 		uint8_t ident= buff_down[3];
 
 #		if _MONITOR>=1
-		if ((debug>1) && (pdebug & P_MAIN)) {
-			mPrint("M readUdp:: message waiting="+String(ident));
+		if ((debug>=2) && (pdebug & P_TX)) {
+			mPrint("Dwn readUdp:: message ident="+String(ident));
 		}
 #		endif //_MONITOR
 
 		// now parse the message type from the server (if any)
 		switch (ident) {
 
-		// This message is used by the gateway to send sensor data to the server. 
+
+		// This message is used by the gateway to send sensor data UP to server. 
 		// As this function is used for downstream only, this option
 		// will never be selected but is included as a reference only
-		case PKT_PUSH_DATA: // 0x00 UP
+		// Para 5.2.1, Semtech Gateway to Server Interface document
+		case PKT_PUSH_DATA: 							// 0x00 UP
 #			if _MONITOR>=1
-			if (debug >=1) {
-				mPrint("PKT_PUSH_DATA:: size "+String(packetSize)+" From "+String(remoteIpNo.toString()));
+			if (debug>=1) {
+				mPrint("Dwn PKT_PUSH_DATA:: size "+String(packetSize)+" From "+String(remoteIpNo.toString()));
 			}
 #			endif //_MONITOR
+			//Udp.flush();
 		break;
-	
-		// This message is sent by the server to acknowledge receipt of a
-		// (sensor) message sent with the code above.
-		case PKT_PUSH_ACK:	// 0x01 DOWN
-#if _MONITOR>=1
-			if (( debug>=2) && (pdebug & P_MAIN )) {
-				mPrint("M PKT_PUSH_ACK:: size="+String(packetSize)+" From "+String(remoteIpNo.toString())); 
+
+
+		// This message is sent DOWN by the server to acknowledge receipt of a
+		// (sensor) PKT_PUSH_DATA message sent with the code above.
+		// Para 5.2.2, Semtech Gateway to Server Interface document
+		case PKT_PUSH_ACK:								// 0x01 DOWN
+#			if _MONITOR>=1
+			if ((debug>=2) && (pdebug & P_TX)) {
+				mPrint("Dwn PKT_PUSH_ACK:: size="+String(packetSize)+", From "+String(remoteIpNo.toString())); 
 			}
-#endif //_MONITOR
+#			endif //_MONITOR
+			//Udp.flush();
 		break;
-	
-		case PKT_PULL_DATA:	// 0x02 UP
+
+
+		// PULL DATA message
+		// This is a request/UP message and will not be executed by this function.
+		// We have it here as a description only. 
+		//	Para 5.2.3, Semtech Gateway to Server Interface document
+		case PKT_PULL_DATA:								// 0x02 UP
 #			if _MONITOR>=1
-				mPrint("readUdp:: PKT_PULL_DATA");
+			if ((debug>=1) && (pdebug & P_RX)) {
+				mPrint("Dwn readUdp:: PKT_PULL_DATA");
+			}
 #			endif //_MONITOR
+			Udp.flush();					// MMM 200419 Added
 		break;
-	
-		// This message type is used to confirm OTAA message to the node
-		// XXX This message format may also be used for other downstream communication
-		case PKT_PULL_RESP:	// 0x03 DOWN
+
+
+		// PULL_ACK message
+		// This is the response to PKT_PULL_DATA message
+		// Para 5.2.4, Semtech Gateway to Server Interface document
+		// With this ACK, the server confirms the gateway that the route is open
+		// for further PULL_RESP messages from the server (to the device)
+		// The server sends a PULL_ACK to confirm PULL_DATA receipt, no response is needed
+		//
+		// Byte 0		contains Protocol Version == 2
+		// Byte 1-2		Random Token
+		// Byte 3		PULL_ACK ident == 0x04
+		//
+		case PKT_PULL_ACK:								// 0x04 DOWN
 #			if _MONITOR>=1
-			if (( debug>=0 ) && ( pdebug & P_MAIN )) {
-				mPrint("readUdp:: PKT_PULL_RESP received from IP="+String(remoteIpNo.toString()));
+			if ((debug>=2) && (pdebug & P_TX)) {
+				String response="Dwn readUdp PKT_PULL_ACK: micr=";
+				printInt(micros(), response);
+				response += ", size="+String(packetSize)+" From ";
+				printIP(remoteIpNo,'.',response);
+				response += ", port " +String(remotePortNo);
+				mPrint(response);
+
+				if (debug>=2) {
+					Serial.print(F(", data: "));
+					for (int i=0; i<packetSize; i++) {
+						Serial.print(buff_down[i],HEX);
+						Serial.print(':');
+					}
+					Serial.println();
+				}
 			}
 #			endif //_MONITOR
-//			lastTmst = micros();					// Store the tmst this package was received
+
 			
-			// Send to the LoRa Node first (timing) and then do reporting to Serial
-			_state=S_TX;
-			sendTime = micros();					// record when we started sending the message
+			yield();			
 			
+			Udp.flush();								// MMM 200419 
+		break;
+
+
+
+		// This message type is used to confirm OTAA message to the node,
+		// but this message format will also be used for other downstream communication
+		// It's length shall not exceed 1000 Octets. This is:
+		//	RECEIVE_DELAY1		1 s 
+		//	RECEIVE_DELAY2		2 s (is RECEIVE_DELAY1+1)
+		//	JOIN_ACCEPT_DELAY1	5 s
+		//	JOIN_ACCEPT_DELAY2	6 s
+		// Para 5.2.5, Semtech Gateway to Server Interface document
+		//
+		case PKT_PULL_RESP:								// 0x03 DOWN
+
+			// Define when we start with the response to node
+#			ifdef _PROFILER
+			if ((debug>=1) && (pdebug & P_TX)) {
+				mPrint("Dwn PKT_PULL_RESP:: sendPacket: micros="+String(micros() ));
+			}
+#			endif //_PROFILER
+
+			// Send to the LoRa Node first (timing) and then do reporting to _Monitor
+			_state=S_TX;
+			sendTime = micros();						// record when we started sending the message
+
+			// Send the package DOWN to the sensor
+			// We just read the packet from the Network Server and it is formatted
+			// as described in the specs.
 			if (sendPacket(data, packetSize-4) < 0) {
 #				if _MONITOR>=1
-				if ( debug>=0 ) {
-					mPrint("readUdp:: ERROR: PKT_PULL_RESP sendPacket failed");
+				if (debug>=0) {
+					mPrint("Dwn readUdp:: ERROR: PKT_PULL_RESP sendPacket failed");
 				}
 #				endif //_MONITOR
+				Udp.flush();
 				return(-1);
 			}
 
-			// Now respond with an PKT_TX_ACK; 0x04 UP
-			buff[0]=buff_down[0];
-			buff[1]=buff_down[1];
-			buff[2]=buff_down[2];
-			buff[3]=PKT_TX_ACK;
-			buff[4]=MAC_array[0];
-			buff[5]=MAC_array[1];
-			buff[6]=MAC_array[2];
-			buff[7]=0xFF;
-			buff[8]=0xFF;
-			buff[9]=MAC_array[3];
-			buff[10]=MAC_array[4];
-			buff[11]=MAC_array[5];
-			buff[12]=0;
+
+			// We need a timeout for this case. In case there does not come an interrupt,
+			// then there will not be a TXDONE but probably another CDDONE/CDDETD before
+			// we have a timeout in the main program (Keep Alive)
+
+			loraWait(&LoraDown);
+
+			// Set state to transmit
+			// Clear interrupt flags and masks
+			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);			// MMM 200407 Reset
+			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);				// reset interrupt flags
+			
+			// Initiate the transmission of the buffer
+			// (We normally react on ALL interrupts if we are in TX state)
+			txLoraModem(&LoraDown);
+
+			// wait extra delay out. The delayMicroseconds timer is accurate until 16383 uSec.
+#			ifdef _PROFILER
+			if ((debug>=1) && (pdebug & P_TX))
+			{
+				String response = "Dwn PKT_PULL_RESP:: txLoraModem done: ";
+				printDwn(&LoraDown, response);
+				mPrint(response);
+			}
+#			endif //_PROFILER
+
 #			if _MONITOR>=1
-			if (( debug >= 2 ) && ( pdebug & P_TX )) {
-				mPrint("readUdp:: TX buff filled and ready");
+			if (( debug>=2 ) && ( pdebug & P_TX )) {
+				uint8_t flags = readRegister(REG_IRQ_FLAGS);
+				uint8_t mask  = readRegister(REG_IRQ_FLAGS_MASK);
+				uint8_t intr  = flags & ( ~ mask );
+
+				String response="Dwn txLoraModem fini:: ";
+				mStat(intr, response);
+				mPrint(response); 
+
+				response = "Dwn readUdp:: PKT_PULL_RESP from IP="+String(remoteIpNo.toString())
+					+", micros=" + String(micros())
+					+", wait=";
+				if (sendTime < micros()) {
+					response += String(micros() - sendTime) ;
+				}
+				else {
+					response += "-" + String(sendTime - micros()) ;
+				};
+				mPrint(response);
 			}
 #			endif //_MONITOR
 
+			// After filling the buffer we only react on TXDONE interrupt
+			// So, more or less start at the "case TXDONE:"  
+			_state=S_TXDONE;
+			_event=1;										// Or remove the break below
+			
+			// No break!!
+
+
+		// This is the response to the PKT_PULL_RESP message by the sensor device
+		// it is sent by the gateway UP to the server to confirm the PULL_RESP message.
+		//
+		case PKT_TX_ACK:									// Message id: 0x05 UP
+
+			if (buff_down[0]== 1) {
+#				if _MONITOR>=1
+				if ((debug>=1) && (pdebug & P_TX)) {
+					mPrint("UP readUDP:: PKT_TX_ACK: protocol version 1");
+					data = buff_down + 4;
+					data[packetSize] = 0;
+					mPrint("UP readUdp:: PKT_TX_ACK: size="+String(packetSize)+", data="+String((char *)data)); 
+				}
+#				endif
+				break;										// return
+			}
+
+#			ifdef _PROFILER
+				mPrint("UP readUDP:: TX_ACK protocol version 2+");
+#			endif //_PROFILER
+
+
+			// Now respond with an PKT_TX_ACK; UP 
+			// Byte 3 is 0x05; see para 5.2.6 of spec
+			buff[0]= buff_down[0];							// As read from the Network Server
+			buff[1]= buff_down[1];							// Token 1
+			buff[2]= buff_down[2];							// Token 2
+			buff[3]= PKT_TX_ACK;							// ident == 0x05;
+			buff[4]= MAC_array[0];
+			buff[5]= MAC_array[1];
+			buff[6]= MAC_array[2];
+			buff[7]= 0xFF;
+			buff[8]= 0xFF;
+			buff[9]= MAC_array[3];
+			buff[10]=MAC_array[4];
+			buff[11]=MAC_array[5];
+			buff[12]=0;										// Not error means "\0"
+			// If it is an error, please look at para 6.1.2
+
+			yield();
+
 			// Only send the PKT_PULL_ACK to the UDP socket that just sent the data!!!
 			Udp.beginPacket(remoteIpNo, remotePortNo);
+			
 			if (Udp.write((unsigned char *)buff, 12) != 12) {
 #				if _MONITOR>=1
 				if (debug>=0) {
-					mPrint("readUdp:: ERROR: PKT_PULL_ACK UDP write");
+					mPrint("UP readUdp:: ERROR: PKT_PULL_ACK write");
 				}
 #				endif //_MONITOR
 			}
 			else {
 #				if _MONITOR>=1
-				if (( debug>=0 ) && ( pdebug & P_TX )) {
-					mPrint("readUdp:: PKT_TX_ACK:: micros="+String(micros()));
+				if ((debug>=0) && (pdebug & P_TX)) {
+					mPrint("UP readUdp:: PKT_TX_ACK: micros="+String(micros()));
 				}
 #				endif //_MONITOR
 			}
 
 			if (!Udp.endPacket()) {
 #				if _MONITOR>=1
-				if (( debug>=0 ) && ( pdebug & P_TX )) {
-					mPrint("readUdp:: PKT_PULL_DATALL ERROR Udp.endPacket");
+				if ((debug>=0) && (pdebug & P_TX)) {
+					mPrint("UP readUdp:: PKT_PULL_DATALL: ERROR Udp.endPacket");
 				}
 #				endif //_MONITOR
 			}
 			
 			yield();
+
+			// ONLY NOW WE START TO MONITOR THE PKT_PULL_RESP MESSAGE.
 #			if _MONITOR>=1
-			if (( debug>=1 ) && (pdebug & P_TX )) {
+			if ((debug>=1) && (pdebug & P_TX)) {
 				data = buff_down + 4;
 				data[packetSize] = 0;
-				mPrint("readUdp:: PKT_PULL_RESP:: size="+String(packetSize)+" From "+String(remoteIpNo.toString())+":"+String(remotePortNo)+", data="+String((char *)data)); 
-			}
-#			endif //_MONITOR	
-		break;
-	
-		case PKT_PULL_ACK:	// 0x04 DOWN; the server sends a PULL_ACK to confirm PULL_DATA receipt
-#			if _MONITOR>=1
-			if (( debug >= 2 ) && (pdebug & P_TX )) {
-				Serial.print(F("readUdp:: PKT_PULL_ACK: size=")); Serial.print(packetSize);
-				Serial.print(F(" From ")); Serial.print(remoteIpNo);
-				Serial.print(F(", port ")); Serial.print(remotePortNo);	
-				Serial.print(F(", data: "));
-				for (int i=0; i<packetSize; i++) {
-					Serial.print(buff_down[i],HEX);
-					Serial.print(':');
-				}
-				Serial.println();
+				mPrint("Dwn readUdp:: PKT_PULL_RESP: size="+String(packetSize)+", data="+String((char *)data)); 
 			}
 #			endif //_MONITOR
+
 		break;
-	
+
 		default:
 #			if _GATEWAYMGT==1
 				// For simplicity, we send the first 4 bytes too
@@ -290,18 +434,25 @@ int readUdp(int packetSize)
 #			endif //_MONITOR
 		break;
 		}
-#		if _MONITOR>=2
+		
+#		if _MONITOR>=1
 		if (debug>=2) {
-			mPrint("readUdp:: returning=" + String(packetSize)); 
+			String response= "Dwn readUdp:: ret="+String(packetSize)+", data=";
+			for (int i=0; i<packetSize; i++) {
+				response+= String(buff_down[i]) + " ";
+			}
+			mPrint(response); 
 		}
 #		endif //_MONITOR
+
 		// For downstream messages
 		return packetSize;
 	}
 }//readUdp
 
 
-// ----------------------------------------------------------------------------
+// ----------------------------------- UP -------------------------------------
+//
 // sendUdp()
 // Send UP an UDP/DGRAM message to the MQTT server
 // If we send to more than one host (not sure why) then we need to set sockaddr 
@@ -315,21 +466,20 @@ int readUdp(int packetSize)
 //	0: Error
 //	1: Success
 // ----------------------------------------------------------------------------
-int sendUdp(IPAddress server, int port, uint8_t *msg, int length) {
-
+int sendUdp(IPAddress server, int port, uint8_t *msg, uint16_t length) 
+{
 	// Check whether we are conected to Wifi and the internet
 	if (WlanConnect(3) < 0) {
 #		if _MONITOR>=1
-		if (( debug>=0 ) && ( pdebug & P_MAIN )) {
+		if ( pdebug & P_MAIN ) {
 			mPrint("sendUdp: ERROR not connected to WiFi");
 		}
-#		endif //_MONITOR
+#		endif //_MONITOR						// MMM 200426 We removed yield() 
 		Udp.flush();
-		yield();
 		return(0);
 	}
 
-	yield();
+	//yield();									// MMM 200327 yield not necessary
 
 	//send the update
 #	if _MONITOR>=1
@@ -347,7 +497,7 @@ int sendUdp(IPAddress server, int port, uint8_t *msg, int length) {
 		return(0);
 	}
 	
-	yield();
+	yield();									// MMM 200316 May not be necessary
 
 	if (Udp.write((unsigned char *)msg, length) != length) {
 #		if _MONITOR>=1
@@ -377,7 +527,7 @@ int sendUdp(IPAddress server, int port, uint8_t *msg, int length) {
 
 // ----------------------------------------------------------------------------
 // pullData()
-// Send UDP periodic Pull_DATA message to server to keepalive the connection
+// Send UDP periodic Pull_DATA message UP to server to keepalive the connection
 // and to invite the server to send downstream messages when these are available
 // *2, par. 5.2
 //	- Protocol Version (1 byte)
@@ -385,11 +535,10 @@ int sendUdp(IPAddress server, int port, uint8_t *msg, int length) {
 //	- PULL_DATA identifier (1 byte) = 0x02
 //	- Gateway unique identifier (8 bytes) = MAC address
 // ----------------------------------------------------------------------------
-void pullData() {
-
+void pullData()
+{
     uint8_t pullDataReq[12]; 								// status report as a JSON object
     int pullIndex=0;
-	int i;
 	
 	uint8_t token_h = (uint8_t)rand(); 						// random token
     uint8_t token_l = (uint8_t)rand();						// random token
@@ -431,22 +580,21 @@ void pullData() {
 	sendUdp(thingServer, _THINGPORT, pullDataReq, pullIndex);
 #endif
 
-#if _DUSB>=1
-    if (( debug>=2 ) && ( pdebug & P_MAIN )) {
+#if _MONITOR>=1
+    if ((debug>=2) && (pdebug & P_MAIN)) {
 		yield();
-		Serial.print(F("M PKT_PULL_DATA request, len=<"));
-		Serial.print(pullIndex);
-		Serial.print(F("> "));
-		for (i=0; i<pullIndex; i++) {
+		mPrint("M PKT_PULL_DATA request, len=" + String(pullIndex) );
+		for (int i=0; i<pullIndex; i++) {
 			Serial.print(pullDataReq[i],HEX);				// debug: display JSON stat
 			Serial.print(':');
 		}
 		Serial.println();
-		if (debug>=2) Serial.flush();
 	}
-#endif
+#endif // _MONITOR
+
 	return;
-}//pullData
+	
+} // pullData()
 
 
 // ----------------------------------------------------------------------------
@@ -456,7 +604,8 @@ void pullData() {
 // Parameters:
 //	- <none>
 // ----------------------------------------------------------------------------
-void sendstat() {
+void sendstat()
+{
 
     uint8_t status_report[STATUS_SIZE]; 					// status report as a JSON object
     char stat_timestamp[32];								// 
@@ -487,18 +636,18 @@ void sendstat() {
 	
 	// XXX Using CET as the current timezone. Change to your timezone	
 	sprintf(stat_timestamp, "%04d-%02d-%02d %02d:%02d:%02d CET", year(),month(),day(),hour(),minute(),second());
-	yield();
-	
+	//sprintf(stat_timestamp, "%04d-%02d-%02dT%02d:%02d:%02d.00000000Z", year(),month(),day(),hour(),minute(),second());
+
 	ftoa(lat,clat,5);										// Convert lat to char array with 5 decimals
 	ftoa(lon,clon,5);										// As IDE CANNOT prints floats
-	
+
 	// Build the Status message in JSON format, XXX Split this one up...
-	delay(1);
-	
+	yield();
+
     int j = snprintf((char *)(status_report + stat_index), STATUS_SIZE-stat_index, 
 		"{\"stat\":{\"time\":\"%s\",\"lati\":%s,\"long\":%s,\"alti\":%i,\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%u.0,\"dwnb\":%u,\"txnb\":%u,\"pfrm\":\"%s\",\"mail\":\"%s\",\"desc\":\"%s\"}}", 
 		stat_timestamp, clat, clon, (int)alt, statc.msg_ttl, statc.msg_ok, statc.msg_down, 0, 0, 0, platform, email, description);
-		
+
 	yield();												// Give way to the internal housekeeping of the ESP8266
 
     stat_index += j;
@@ -512,22 +661,23 @@ void sendstat() {
 
 	if (stat_index > STATUS_SIZE) {
 #		if _MONITOR>=1
-			mPrint("A sendstat:: ERROR buffer too big");
+			mPrint("sendstat:: ERROR buffer too big");
 #		endif //_MONITOR
 		return;
 	}
-	
+
     //send the update
 #	ifdef _TTNSERVER
 		sendUdp(ttnServer, _TTNPORT, status_report, stat_index);
-		yield();
 #	endif
 
 #	ifdef _THINGSERVER
+		yield();
 		sendUdp(thingServer, _THINGPORT, status_report, stat_index);
 #	endif
 	return;
-}//sendstat
+
+} // sendstat()
 
 
 #endif //_UDPROUTER

+ 96 - 42
src/_utils.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 // 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
@@ -20,6 +20,57 @@
 
 // ==================== STRING STRING STRING ==============================================
 
+// --------------------------------------------------------------------------------
+// PRINT INT
+// The function printInt prints a number with Thousands seperator
+// Paraneters:
+//	i:			Integer containing Microseconds
+//	response:	String & value containig the converted number
+// Retur:
+//	<none>
+// --------------------------------------------------------------------------------
+void printInt (uint32_t i, String & response)
+{
+	response+=(String(i/1000000) + "." + String(i%1000000));
+}
+
+// --------------------------------------------------------------------------------
+// PRINT Dwn
+// IN a uniform way, this function prints the timstamp, the current time and the 
+// time the function must wait to execute. It will print all Downstream data
+// --------------------------------------------------------------------------------
+void printDwn(struct LoraDown *LoraDown, String & response)
+{
+	uint32_t i= LoraDown->tmst;
+	uint32_t m= micros();
+	
+	response += "micr=";	printInt(m, response);
+	response += ", tmst=";	printInt(i, response);
+
+	response += ", wait=";
+	if (i>m) {
+		response += String(i-m);
+	}
+	else {
+		response += "(";
+		response += String(m-i);
+		response += ")";
+	}
+
+	response += ", SF="		+String(LoraDown->sfTx);
+	response += ", Freq="	+String(LoraDown->freq);
+
+	response += ", a=";
+	uint8_t DevAddr [4];
+		DevAddr[0] = LoraDown->payLoad[4];
+		DevAddr[1] = LoraDown->payLoad[3];
+		DevAddr[2] = LoraDown->payLoad[2];
+		DevAddr[3] = LoraDown->payLoad[1];
+	printHex((IPAddress)DevAddr, ':', response);
+
+	yield();
+	return;
+}
 
 
 // --------------------------------------------------------------------------------
@@ -73,9 +124,14 @@ void printHexDigit(uint8_t digit, String & response)
 
 }
 
+// ========================= MONITOR FUNCTIONS ============================================
+// Monitor functions
+// These functions write to the Monitor and print the monitor.
+
+
 // ----------------------------------------------------------------------------------------
 // Print to the monitor console.
-// This function is used all over the gateway code as a substite for USB debug code.
+// This function is used all over the gateway code as a substitute for USB debug code.
 // It allows webserver users to view printed/debugging code.
 // With initMonitor() we init the index iMoni=0;
 //
@@ -86,39 +142,32 @@ void printHexDigit(uint8_t digit, String & response)
 // ----------------------------------------------------------------------------------------
 void mPrint(String txt) 
 {
-	
-#	if _DUSB>=1
-		Serial.println(txt);
-		if (debug>=2) Serial.flush();
-#	endif //_DUSB
-
 #	if _MONITOR>=1
 	time_t tt = now();
-
-	monitor[iMoni].txt  = String(day(tt))	+ "-";
-	monitor[iMoni].txt += String(month(tt))	+ "-";
-	monitor[iMoni].txt += String(year(tt))	+ " ";
 	
-	uint8_t _hour   = hour(tt);
-	uint8_t _minute = minute(tt);
-	uint8_t _second = second(tt);
-
-	if (_hour   < 10) monitor[iMoni].txt += "0"; monitor[iMoni].txt += String( _hour )+ ":";
-	if (_minute < 10) monitor[iMoni].txt += "0"; monitor[iMoni].txt += String(_minute) + ":";
-	if (_second < 10) monitor[iMoni].txt += "0"; monitor[iMoni].txt += String(_second) + "- ";
+	monitor[iMoni].txt = "";
+	stringTime(tt, monitor[iMoni].txt);
 	
-	monitor[iMoni].txt += String(txt);
+	monitor[iMoni].txt += "- " + String(txt);
 	
 	// Use the circular buffer to increment the index
+
+#	if _DUSB>=1
+	if (gwayConfig.dusbStat>=1) {
+		Serial.println(monitor[iMoni].txt);			// Copy to serial when configured
+	}
+#	endif //_DUSB
+
 	iMoni = (iMoni+1) % _MAXMONITOR	;				// And goto 0 when skipping over _MAXMONITOR
 	
 #	endif //_MONITOR
+
 	return;
 }
 
 
 // ----------------------------------------------------------------------------
-// mStat
+// mStat (Monitor-Statistics)
 // Print the statistics on Serial (USB) port and/or Monitor
 // Depending on setting of _DUSB and _MONITOR.
 // Note: This function does not initialise the response var, will only append.
@@ -137,23 +186,24 @@ int mStat(uint8_t intr, String & response)
 	
 		response += "I=";
 
-		if (intr & IRQ_LORA_RXTOUT_MASK) response += String("RXTOUT ");		// 0x80
-		if (intr & IRQ_LORA_RXDONE_MASK) response += String("RXDONE ");		// 0x40
+		if (intr & IRQ_LORA_RXTOUT_MASK) response += "RXTOUT ";		// 0x80
+		if (intr & IRQ_LORA_RXDONE_MASK) response += "RXDONE ";		// 0x40
 		if (intr & IRQ_LORA_CRCERR_MASK) response += "CRCERR ";		// 0x20
 		if (intr & IRQ_LORA_HEADER_MASK) response += "HEADER ";		// 0x10
 		if (intr & IRQ_LORA_TXDONE_MASK) response += "TXDONE ";		// 0x08
-		if (intr & IRQ_LORA_CDDONE_MASK) response += String("CDDONE ");		// 0x04
+		if (intr & IRQ_LORA_CDDONE_MASK) response += "CDDONE ";		// 0x04
 		if (intr & IRQ_LORA_FHSSCH_MASK) response += "FHSSCH ";		// 0x02
 		if (intr & IRQ_LORA_CDDETD_MASK) response += "CDDETD ";		// 0x01
 
 		if (intr == 0x00) response += "  --  ";
 			
 		response += ", F=" + String(gwayConfig.ch);
+		
 		response += ", SF=" + String(sf);
+		
 		response += ", E=" + String(_event);
 			
 		response += ", S=";
-
 		switch (_state) {
 			case S_INIT:
 				response += "INIT ";
@@ -165,7 +215,7 @@ int mStat(uint8_t intr, String & response)
 				response += "CAD  ";
 			break;
 			case S_RX:
-				response += String("RX   ");
+				response += "RX   ";
 			break;
 			case S_TX:
 				response += "TX   ";
@@ -178,10 +228,9 @@ int mStat(uint8_t intr, String & response)
 		}
 		response += ", eT=";
 		response += String( micros() - eventTime );
+		
 		response += ", dT=";
 		response += String( micros() - doneTime );
-
-		//mPrint(response);							// Do actual printing
 	}
 #endif //_MONITOR
 	return(1);
@@ -217,7 +266,7 @@ void ftoa(float f, char *val, int p)
 	strcat(val,".");										// Copy decimal point
 	
 	itoa(fval,b,10);										// Copy fraction part base 10
-	for (int i=0; i<(p-strlen(b)); i++) {
+	for (unsigned int i=0; i<(p-strlen(b)); i++) {
 		strcat(val,"0");									// first number of 0 of faction?
 	}
 	
@@ -272,18 +321,22 @@ static void stringTime(time_t t, String & response)
 	byte _minute = minute(eTime);
 	byte _second = second(eTime);
 	
+	byte _month = month(eTime);
+	byte _day = day(eTime);
+	
 	switch(weekday(eTime)) {
-		case 1: response += "Sunday "; break;
-		case 2: response += "Monday "; break;
-		case 3: response += "Tuesday "; break;
-		case 4: response += "Wednesday "; break;
-		case 5: response += "Thursday "; break;
-		case 6: response += "Friday "; break;
-		case 7: response += "Saturday "; break;
+		case 1: response += "Sun "; break;
+		case 2: response += "Mon "; break;
+		case 3: response += "Tue "; break;
+		case 4: response += "Wed "; break;
+		case 5: response += "Thu "; break;
+		case 6: response += "Fri "; break;
+		case 7: response += "Sat "; break;
 	}
-	response += String(day(eTime)) + "-";
-	response += String(month(eTime)) + "-";
+	if (_day < 10) response += "0"; response += String(_day) + "-";
+	if (_month < 10) response += "0"; response += String(_month) + "-";
 	response += String(year(eTime)) + " ";	
+	
 	if (_hour < 10) response += "0";   response += String(_hour) + ":";
 	if (_minute < 10) response += "0"; response += String(_minute) + ":";
 	if (_second < 10) response += "0"; response += String(_second);
@@ -317,7 +370,8 @@ int sendNtpRequest(IPAddress timeServerIP)
 		return(0);	
 	}
 	return(1);
-}
+	
+} // sendNtpRequest()
 
 
 // ----------------------------------------------------------------------------
@@ -428,11 +482,11 @@ int SerialName(uint32_t a, String & response)
 	uint8_t * in = (uint8_t *)(& a);
 	uint32_t id = ((in[0]<<24) | (in[1]<<16) | (in[2]<<8) | in[3]);
 
-	for (int i=0; i< (sizeof(nodes)/sizeof(nodex)); i++) {
+	for (unsigned int i=0; i< (sizeof(nodes)/sizeof(nodex)); i++) {
 
 		if (id == nodes[i].id) {
 #			if _MONITOR>=1
-			if ((debug>=2) && (pdebug & P_MAIN )) {
+			if ((debug>=3) && (pdebug & P_MAIN )) {
 				mPrint("SerialName:: i="+String(i)+", Name="+String(nodes[i].nm)+". for node=0x"+String(nodes[i].id,HEX));
 			}
 #			endif //_MONITOR
@@ -460,7 +514,7 @@ int inDecodes(char * id) {
 
 	uint32_t ident = ((id[3]<<24) | (id[2]<<16) | (id[1]<<8) | id[0]);
 
-	for (int i=0; i< (sizeof(decodes)/sizeof(codex)); i++) {
+	for (unsigned int i=0; i< (sizeof(decodes)/sizeof(codex)); i++) {
 		if (ident == decodes[i].id) {
 			return(i);
 		}

+ 110 - 66
src/_wwwServer.ino

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 // 	based on work done by many people and making use of several libraries.
@@ -34,9 +34,9 @@
 
 
 //
-// The remainder of the file ONLY works is A_SERVER=1 is set.
+// The remainder of the file ONLY works is _SERVER=1 is set.
 //
-#if A_SERVER==1
+#if _SERVER==1
 
 // ================================================================================
 // WEBSERVER DECLARATIONS 
@@ -106,7 +106,6 @@ boolean YesNo()
 // --------------------------------------------------------------------------------
 void wwwFile(String fn)
 {
-
 #if _STAT_LOG == 1
 
 	if (!SPIFFS.exists(fn)) {
@@ -127,8 +126,7 @@ void wwwFile(String fn)
 	
 	// MMM Change LOGFILEREC to file available
 	while (f.available()) {
-//	for (int j=0; j<LOGFILEREC; j++) {
-			
+
 		String s=f.readStringUntil('\n');
 		if (s.length() == 0) {
 			Serial.print(F("wwwFile:: String length 0"));
@@ -138,11 +136,14 @@ void wwwFile(String fn)
 		server.sendContent("\n");
 		yield();
 	}
-	
+
 	f.close();
 
 #	endif //_MONITOR
 #endif //_STAT_LOG
+
+	return;
+	
 }
 
 // --------------------------------------------------------------------------------
@@ -163,13 +164,15 @@ void buttonDocu()
 	response += "    txt += \"Click OK to continue viewing documentation,\\n\"; ";
 	response += "    txt += \"or Cancel to return to the home page.\\n\\n\"; ";
 	response += "    if(confirm(txt)) { ";
-	response += "      document.location.href = \"https://things4u.github.io/Projects/SingleChannelGateway/1ch_GWay.html\"; ";
+	response += "      document.location.href = \"https://things4u.github.io/Projects/SingleChannelGateway/index.html\"; ";
 	response += "    }";
 	response += "  }";
 	response += "}";
 	
 	response += "</script>";
 	server.sendContent(response);
+	
+	return;
 }
 
 
@@ -189,6 +192,8 @@ void buttonLog()
 	}
 	
 #endif //_STAT_LOG
+
+	return;
 }
 
 
@@ -219,6 +224,8 @@ static void wwwButtons()
 	response += "<a href=\"SEEN\" download><button type=\"button\">" +seen+ "</button></a>";
 #	endif
 	server.sendContent(response);							// Send to the screen
+	
+	return;
 }
 
 
@@ -237,7 +244,8 @@ static void wwwButtons()
 // Returns:
 //		<none>
 // --------------------------------------------------------------------------------
-static void setVariables(const char *cmd, const char *arg) {
+static void setVariables(const char *cmd, const char *arg)
+{
 
 	// DEBUG settings; These can be used as a single argument
 	if (strcmp(cmd, "DEBUG")==0) {									// Set debug level 0-2
@@ -372,7 +380,7 @@ static void setVariables(const char *cmd, const char *arg) {
 #endif //_WIFIMANAGER
 
 	// Update the software (from User Interface)
-#if A_OTA==1
+#if _OTA==1
 	if (strcmp(cmd, "UPDATE")==0) {
 		if (atoi(arg) == 1) {
 			updateOtaa();
@@ -381,7 +389,7 @@ static void setVariables(const char *cmd, const char *arg) {
 	}
 #endif
 
-#if A_REFRESH==1
+#if _REFRESH==1
 	if (strcmp(cmd, "REFR")==0) {									// Set refresh on=1 or off=0
 		gwayConfig.refresh =(bool)atoi(arg);
 		writeGwayCfg(CONFIGFILE, &gwayConfig );						// Save configuration to file
@@ -412,7 +420,7 @@ static void openWebPage()
 	server.send(200, "text/html", "");
 	String tt=""; printIP((IPAddress)WiFi.localIP(),'.',tt);	// Time with separator
 	
-#if A_REFRESH==1
+#if _REFRESH==1
 	if (gwayConfig.refresh) {
 		response += String() + "<!DOCTYPE HTML><HTML><HEAD><meta http-equiv='refresh' content='"+_WWW_INTERVAL+";http://";
 		response += tt;
@@ -446,6 +454,7 @@ static void openWebPage()
 	uint8_t _minute = minute(secs);
 	uint8_t _second = second(secs);
 	response += String(days) + "-";
+	
 	if (_hour < 10) response += "0"; response += String(_hour) + ":";
 	if (_minute < 10) response += "0"; response += String(_minute) + ":";
 	if (_second < 10) response += "0"; 	response += String(_second);
@@ -485,8 +494,8 @@ static void gatewaySettings()
 	response +="<tr><td class=\"cell\">CAD</td>";
 	response +="<td colspan=\"2\" style=\"border: 1px solid black;"; response += bg; response += "\">";
 	response += ( gwayConfig.cad ? "ON" : "OFF" );
-	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"CAD=1\"><button>ON</button></a></td>";
 	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"CAD=0\"><button>OFF</button></a></td>";
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"CAD=1\"><button>ON</button></a></td>";
 	response +="</tr>";
 	
 	bg = " background-color: ";
@@ -494,8 +503,8 @@ static void gatewaySettings()
 	response +="<tr><td class=\"cell\">HOP</td>";
 	response +="<td colspan=\"2\" style=\"border: 1px solid black;"; response += bg; response += "\">";
 	response += ( gwayConfig.hop ? "ON" : "OFF" );
-	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"HOP=1\"><button>ON</button></a></td>";
 	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"HOP=0\"><button>OFF</button></a></td>";
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"HOP=1\"><button>ON</button></a></td>";
 	response +="</tr>";
 	
 	response +="<tr><td class=\"cell\">SF Setting</td><td class=\"cell\" colspan=\"2\">";
@@ -598,13 +607,29 @@ static void gatewaySettings()
 	response +="<button>RDIO</button></a></td>";
 	response +="</tr>";
 #endif
+
 	// USB Debug, Serial Debugging
-	response +="<tr><td class=\"cell\">Usb Debug</td><td class=\"cell\" colspan=\"2\">"; 
-	response += _DUSB; 
+	bg = " background-color: ";
+	bg += ( (gwayConfig.dusbStat == 1) ? "LightGreen" : "orange" );
+	response +="<tr><td class=\"cell\">Usb Debug</td>";
+	response +="<td class=\"cell\" colspan=\"2\" style=\"border: 1px solid black; " + bg + "\">";
+	response += ( (gwayConfig.dusbStat == true) ? "ON" : "OFF" );
 	response +="</td>";
-	//response +="<td class=\"cell\"> </td>";
-	//response +="<td class=\"cell\"> </td>";
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"DUSB=0\"><button>OFF</button></a></td>";
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"DUSB=1\"><button>ON</button></a></td>";
+	response +="</tr>";
+
+	/// WWW Refresh
+#if _REFRESH==1
+	bg = " background-color: ";
+	bg += ( (gwayConfig.refresh == 1) ? "LightGreen" : "orange" );
+	response +="<tr><td class=\"cell\">WWW Refresh</td>";
+	response +="<td class=\"cell\" colspan=\"2\" style=\"border: 1px solid black; " + bg + "\">";
+	response += ( (gwayConfig.refresh == 1) ? "ON" : "OFF" );
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"REFR=0\"><button>OFF</button></a></td>";
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"REFR=1\"><button>ON</button></a></td>";
 	response +="</tr>";
+#endif
 	
 #if _GATEWAYNODE==1
 	response +="<tr><td class=\"cell\">Framecounter Internal Sensor</td>";
@@ -624,18 +649,6 @@ static void gatewaySettings()
 	response +="</tr>";
 #endif
 
-	/// WWW Refresh
-#if A_REFRESH==1
-	bg = " background-color: ";
-	bg += ( (gwayConfig.refresh == 1) ? "LightGreen" : "orange" );
-	response +="<tr><td class=\"cell\">WWW Refresh</td>";
-	response +="<td class=\"cell\" colspan=\"2\" style=\"border: 1px solid black; " + bg + "\">";
-	response += ( (gwayConfig.refresh == 1) ? "ON" : "OFF" );
-	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"REFR=1\"><button>ON</button></a></td>";
-	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"REFR=0\"><button>OFF</button></a></td>";
-	response +="</tr>";
-#endif
-
 	// Time Correction DELAY
 	if (gwayConfig.expert) {
 		response +="<tr><td class=\"cell\">Time Correction (uSec)</td><td class=\"cell\" colspan=\"2\">"; 
@@ -661,7 +674,7 @@ static void gatewaySettings()
 
 	// Format the Filesystem
 	response +="<tr><td class=\"cell\">Format SPIFFS</td>";
-	response +=String() + "<td class=\"cell\" colspan=\"2\" >"+""+"</td>";
+	response +=String() + "<td class=\"cell\" colspan=\"2\" >"+gwayConfig.formatCntr+"</td>";
 	response +="<td style=\"width:30px;\" colspan=\"2\" class=\"cell\"><input type=\"button\" value=\"FORMAT\" onclick=\"ynDialog(\'Do you really want to format?\',\'FORMAT\')\" /></td></tr>";
 
 	// Reset all statistics
@@ -678,6 +691,7 @@ static void gatewaySettings()
 	
 	response +="</table>";
 	
+	
 	server.sendContent(response);
 }
 
@@ -891,7 +905,7 @@ static void messageHistory()
 		response = "";
 		
 		response += String() + "<tr><td class=\"cell\">";					// Tmst
-		stringTime((statr[i].tmst), response);								// XXX Change tmst not to be millis() dependent
+		stringTime((statr[i].time), response);								// XXX Change tmst not to be millis() dependent
 		response += "</td>";
 		
 		response += String() + "<td class=\"cell\">"; 						// Node
@@ -967,7 +981,7 @@ static void messageHistory()
 // --------------------------------------------------------------------------------
 static void nodeHistory() 
 {
-#if _MAXSEEN >= 1
+#	if _MAXSEEN >= 1
 	if (gwayConfig.seen) {
 		// First draw the headers
 		String response= "";
@@ -1045,17 +1059,20 @@ static void nodeHistory()
 		}
 		server.sendContent("</table>");
 	}
-#endif //_MAXSEEN
+#	endif //_MAXSEEN
+
+	return;
 } // nodeHistory()
 
 
 
 // --------------------------------------------------------------------------------
 // MONITOR DATA
+// This is a cicular buffer that starts at the latest mPrint line and then goes
+// down until there are no lines left (or we reach the limit).
 // This function will print monitor data for the gateway based on the settings in
 // _MONITOR in file configGway.h
-// Only when the _MONITOR is positive then the function will be executed. 
-// If less then nothing will be displayed.
+//
 // XXX We have to make the function such that when printed, the webpage refreshes.
 // --------------------------------------------------------------------------------
 void monitorData() 
@@ -1071,13 +1088,12 @@ void monitorData()
 		response +="</tr>";
 		
 		for (int i= iMoni-1+_MAXMONITOR; i>=iMoni; i--) {
-			if (monitor[i % _MAXMONITOR].txt == "-") {		// If equal to init value '-'
+			if (monitor[i % _MAXMONITOR].txt == "-") {			// If equal to init value '-'
 				break;
 			}
 			response +="<tr><td class=\"cell\">" ;
 			response += String(monitor[i % _MAXMONITOR].txt);
 			response += "</td></tr>";
-
 		}
 		
 		response +="</table>";
@@ -1131,7 +1147,6 @@ static void wifiConfig()
 		response +="</tr>";
 #endif
 
-
 		response +="</table>";
 
 		server.sendContent(response);
@@ -1178,7 +1193,7 @@ static void systemStatus()
 		response+="</tr>";
 		response +="<tr><td class=\"cell\">ESP Chip ID</td><td class=\"cell\">"; response+=ESP.getChipId(); response+="</tr>";
 #endif
-		response +="<tr><td class=\"cell\">OLED</td><td class=\"cell\">"; response+=OLED; response+="</tr>";
+		response +="<tr><td class=\"cell\">OLED</td><td class=\"cell\">"; response+=_OLED; response+="</tr>";
 		
 #if _STATISTICS >= 1
 		response +="<tr><td class=\"cell\">WiFi Setups</td><td class=\"cell\">"; response+=gwayConfig.wifis; response+="</tr>";
@@ -1220,43 +1235,55 @@ static void interruptData()
 			case S_CAD: response +="CAD"; break;
 			case S_RX: response +="RX"; break;
 			case S_TX: response +="TX"; break;
+			case S_TXDONE: response +="TXDONE"; break;
 			default: response +="unknown"; break;
 		}
 		response +="</td></tr>";
 		
 		response +="<tr><td class=\"cell\">_STRICT_1CH</td>";
 		response +="<td class=\"cell\">" ;
-		response += String() + _STRICT_1CH;
+		response += String(_STRICT_1CH);
 		response +="</td></tr>";		
 
 		response +="<tr><td class=\"cell\">flags (8 bits)</td>";
 		response +="<td class=\"cell\">0x";
 		if (flags <16) response += "0";
-		response +=String(flags,HEX); response+="</td></tr>";
+		response +=String(flags,HEX); 
+		response+="</td></tr>";
 
 		
 		response +="<tr><td class=\"cell\">mask (8 bits)</td>";
 		response +="<td class=\"cell\">0x"; 
 		if (mask <16) response += "0";
-		response +=String(mask,HEX); response+="</td></tr>";
+		response +=String(mask,HEX); 
+		response+="</td></tr>";
 		
 		response +="<tr><td class=\"cell\">Re-entrant cntr</td>";
 		response +="<td class=\"cell\">"; 
-		response += String() + gwayConfig.reents;
+		response += String(gwayConfig.reents);
 		response +="</td></tr>";
 
 		response +="<tr><td class=\"cell\">ntp call cntr</td>";
 		response +="<td class=\"cell\">"; 
-		response += String() + gwayConfig.ntps;
+		response += String(gwayConfig.ntps);
 		response+="</td></tr>";
 		
 		response +="<tr><td class=\"cell\">ntpErr cntr</td>";
 		response +="<td class=\"cell\">"; 
-		response += String() + gwayConfig.ntpErr;
+		response += String(gwayConfig.ntpErr);
 		response +="</td>";
 		response +="<td colspan=\"2\" style=\"border: 1px solid black;\">";
 		stringTime(gwayConfig.ntpErrTime, response);
+		response +="</td></tr>";
+
+		response +="<tr><td class=\"cell\">loraWait errors/success</td>";
+		response +="<td class=\"cell\">"; 
+		response += String(gwayConfig.waitErr);
 		response +="</td>";
+		response +="<td colspan=\"2\" style=\"border: 1px solid black;\">";
+		response += String(gwayConfig.waitOk);
+		response +="</td></tr>";
+		
 		response +="</tr>";
 		
 		response +="</table>";
@@ -1294,7 +1321,7 @@ void setupWWW()
 	server.on("/HELP", []() {
 		sendWebPage("HELP","");					// Send the webPage string
 		server.sendHeader("Location", String("/"), true);
-		server.send ( 302, "text/plain", "");
+		server.send( 302, "text/plain", "");
 	});
 
 	// Format the filesystem
@@ -1303,7 +1330,8 @@ void setupWWW()
 		msg_oLED("FORMAT");		
 		SPIFFS.format();							// Normally not used. Use only when SPIFFS corrupt
 		initConfig(&gwayConfig);					// Well known values
-		writeConfig( CONFIGFILE, &gwayConfig);
+		gwayConfig.formatCntr++;
+		writeConfig(CONFIGFILE, &gwayConfig);
 		writeSeen( _SEENFILE, listSeen);			// Write the last time record  is Seen
 #		if _MONITOR>=1
 		if ((debug>=1) && (pdebug & P_GUI )) {
@@ -1315,7 +1343,7 @@ void setupWWW()
 	});
 	
 	
-	// Reset the statistics
+	// Reset the statistics (th egateway function) but not the system specific things
 	server.on("/RESET", []() {
 		mPrint("RESET");
 		startTime= now() - 1;					// Reset all timers too (-1 to avoid division by 0)
@@ -1364,27 +1392,29 @@ void setupWWW()
 				statc.sf11_0= 0; statc.sf11_1= 0; statc.sf11_2= 0;
 				statc.sf12_0= 0; statc.sf12_1= 0; statc.sf12_2= 0;
 #			endif //_STATISTICS==3
+
 #		endif //_STATISTICS==2
 #	endif //_STATISTICS==1
-
+			
 		initSeen(listSeen);						// Clear all Seen records as well.
 		
 		server.sendHeader("Location", String("/"), true);
 		server.send ( 302, "text/plain", "");
 	});
 
-	// Reset the boot counter
+	// Reset the boot counter, and other system specific counters
 	server.on("/BOOT", []() {
 		mPrint("BOOT");
-#if _STATISTICS >= 2
-		gwayConfig.boots = 0;
+#if _STATISTICS >= 2		
 		gwayConfig.wifis = 0;
 		gwayConfig.views = 0;
 		gwayConfig.ntpErr = 0;					// NTP errors
 		gwayConfig.ntpErrTime = 0;				// NTP last error time
 		gwayConfig.ntps = 0;					// Number of NTP calls
 #endif
+		gwayConfig.boots = 0;					//
 		gwayConfig.reents = 0;					// Re-entrance
+		
 		writeGwayCfg(CONFIGFILE, &gwayConfig );
 #if _MONITOR>=1
 		if ((debug>=2) && (pdebug & P_GUI)) {
@@ -1404,6 +1434,7 @@ void setupWWW()
 		ESP.restart();
 	});
 
+	// New SSID for the machine
 	server.on("/NEWSSID", []() {
 		sendWebPage("NEWSSID","");				// Send the webPage string
 		server.sendHeader("Location", String("/"), true);
@@ -1515,7 +1546,7 @@ void setupWWW()
 	server.on("/TRUSTED=1", []() {
 	gwayConfig.trusted = (gwayConfig.trusted +1)%4;
 		writeGwayCfg(CONFIGFILE, &gwayConfig );	// Save configuration to file
-#		if _MONITOR>=2
+#		if _MONITOR>=1
 			mPrint("TRUSTED +, config written");
 #		endif //_MONITOR
 		server.sendHeader("Location", String("/"), true);
@@ -1524,9 +1555,9 @@ void setupWWW()
 	server.on("/TRUSTED=-1", []() {
 		gwayConfig.trusted = (gwayConfig.trusted -1)%4;
 		writeGwayCfg(CONFIGFILE, &gwayConfig );	// Save configuration to file
-#if _DUSB>=2
-		Serial.println(F("TRUSTED +, config written"));
-#endif
+#		if _MONITOR>=1
+			mPrint("TRUSTED -, config written");
+#		endif //_MONITOR
 		server.sendHeader("Location", String("/"), true);
 		server.send ( 302, "text/plain", "");
 	});
@@ -1607,9 +1638,10 @@ void setupWWW()
 		server.send ( 302, "text/plain", "");
 	});
 #endif
+
 	// WWW Page refresh function
 	server.on("/REFR=1", []() {					// WWW page auto refresh ON
-#if A_REFRESH==1
+#if _REFRESH==1
 		gwayConfig.refresh =1;
 		writeGwayCfg(CONFIGFILE, &gwayConfig );	// Save configuration to file
 #endif		
@@ -1617,7 +1649,7 @@ void setupWWW()
 		server.send ( 302, "text/plain", "");
 	});
 	server.on("/REFR=0", []() {					// WWW page auto refresh OFF
-#if A_REFRESH==1
+#if _REFRESH==1
 		gwayConfig.refresh =0;
 		writeGwayCfg(CONFIGFILE, &gwayConfig );	// Save configuration to file
 #endif
@@ -1625,6 +1657,19 @@ void setupWWW()
 		server.send ( 302, "text/plain", "");
 	});
 
+	// WWW Page serial print function
+	server.on("/DUSB=1", []() {					// WWW page Serial Print ON
+		gwayConfig.dusbStat =1;
+		writeGwayCfg(CONFIGFILE, &gwayConfig );	// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/DUSB=0", []() {					// WWW page Serial Print OFF
+		gwayConfig.dusbStat =0;
+		writeGwayCfg(CONFIGFILE, &gwayConfig );	// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
 	
 	// Switch off/on the HOP functions
 	server.on("/HOP=1", []() {
@@ -1664,7 +1709,7 @@ void setupWWW()
 	// Display LOGging information
 	server.on("/LOG", []() {
 		server.sendHeader("Location", String("/"), true);
-#if _MONITOR>=2
+#if _MONITOR>=1
 		mPrint("LOG button");
 #endif //_MONITOR
 		buttonLog();
@@ -1697,7 +1742,7 @@ void setupWWW()
 	
 	// Update the sketch. Not yet implemented
 	server.on("/UPDATE=1", []() {
-#if A_OTA==1
+#if _OTA==1
 		updateOtaa();
 #endif
 		server.sendHeader("Location", String("/"), true);
@@ -1710,10 +1755,9 @@ void setupWWW()
 	// Maybe not all information should be put on the screen since it
 	// may take too much time to serve all information before a next
 	// package interrupt arrives at the gateway
-#	if _DUSB>=1
-		Serial.print(F("WWW Server started on port "));
-		Serial.println(A_SERVERPORT);
-#	endif //_DUSB
+#	if _MONITOR>=1
+		mPrint("WWW Server started on port " + String(_SERVERPORT) );
+#	endif // _MONITOR
 
 	return;
 } // setupWWW
@@ -1767,5 +1811,5 @@ static void websiteFooter()
 }
 
 
-#endif // A_SERVER==1
+#endif // _SERVER==1
 

+ 54 - 38
src/configGway.h

@@ -1,7 +1,7 @@
 // 1-channel LoRa Gateway for ESP32 and ESP8266
 // Copyright (c) Maarten Westenberg 2016-2020 
 
-#define VERSION "V.6.2.3.E.EU868; PlatformIO 200223a"
+#define VERSION "V.6.2.4.EU868; PlatformIO 200425m"
 //
 // Based on work done by Thomas Telkamp for Raspberry PI 1ch gateway and many others.
 //
@@ -23,11 +23,11 @@
 // "ESP32 Dev Module" or "Heltec WiFi Lora 32"
 // 
 // For ESP8266 Wemos: compile with "Wemos R1 D1" and choose
-// the right _PIN_OUT below. Selecting OLED while that is not connected does not 
+// the right _PIN_OUT below. Selecting Oled while that is not connected does not 
 // really matter (so you can leave that in).
 //
-// The source has been optimized for PlatformIO usage. Than means the some variables
-// can be define in the .platformio.ini file. Pleas look at the example file to see 
+// The source has been optimized for PlatformIO usage. That means some variables
+// can be defined in the .platformio.ini file. Please look at the example file to see 
 // how to set these #defines
 //
 // ========================================================================================
@@ -60,7 +60,9 @@
 // Define the monitor screen. When it is greater than 0 then logging is displayed in
 // the special screen at the GUI.
 // If _DUSB is also set to 1 then most messages will also be copied to USB devices.
+#if !defined _MONITOR
 #define _MONITOR 1
+#endif
 
 
 // Gather statistics on sensor and Wifi status
@@ -119,6 +121,7 @@
 // device and also connect enable dio1 to detect this state. 
 #define _CAD 1
 
+
 // CRCCHECK
 // Defines whether we should check on the CRC of RXDONE messages (see stateMachine.ino)
 // This should prevent us from getting a lot os stranges messgages of unknown nodes.
@@ -126,12 +129,12 @@
 #define _CRCCHECK 1
 
 // Definitions for the admin webserver.
-// A_SERVER determines whether or not the admin webpage is included in the sketch.
+// _SERVER determines whether or not the admin webpage is included in the sketch.
 // Normally, leave it in!
-#define A_SERVER 1				// Define local WebServer only if this define is set
-#define A_REFRESH 1				// Allow the webserver refresh or not?
-#define A_SERVERPORT 80			// Local webserver port (normally 80)
-#define A_MAXBUFSIZE 192		// Must be larger than 128, but small enough to work
+#define _SERVER 1				// Define local WebServer only if this define is set
+#define _REFRESH 1				// Allow the webserver refresh or not?
+#define _SERVERPORT 80			// Local webserver port (normally 80)
+#define _MAXBUFSIZE 192		// Must be larger than 128, but small enough to work
 
 
 // Definitions for over the air updates. At the moment we support OTA with IDE
@@ -139,16 +142,18 @@
 // Bonjour is included in iTunes (which is free) and OTA is recommended to install 
 // the firmware on your router witout having to be really close to the gateway and 
 // connect with USB.
-#define A_OTA 1
+#define _OTA 1
 
 
-// We support a few pin-out configurations out-of-the-box: HALLARD, COMPRESULT and TTGO ESP32.
-// If you use one of these two, just set the parameter to the right value.
-// If your pin definitions are different, update the loraModem.h file to reflect these settings.
+// We support a few pin-out configurations out-of-the-box: HALLARD, COMPRESULT and 
+// Heltec/TTGO ESP32.
+// If you use one of these, just set the parameter to the right value.
+// If your pin definitions are different, update the loraModem.h file to reflect the
+// hardware settings.
 //	1: HALLARD
 //	2: COMRESULT pin out
 //	3: ESP32, Wemos pin out (Not used)
-//	4: ESP32, Heltec and TTGO pin out (should work for Heltec, 433 and OLED too).
+//	4: ESP32, Heltec and TTGO pin out (should work for Heltec, 433 and Oled too).
 //	5: Other, define your own in loraModem.h (does not include GPS Code)
 #if !defined _PIN_OUT
 #	define _PIN_OUT 1
@@ -165,13 +170,20 @@
 // NOTE: If your node has only one frequency enabled and one SF, you must set this to 1
 //		in order to receive downlink messages. This is the default mode.
 // NOTE: In all other cases, value 0 works for most gateways with CAD enabled
-#define _STRICT_1CH 1
-
+#if !defined _STRICT_1CH
+#	define _STRICT_1CH 1
+#endif //_STRICT_1CH
 
 //
 // Also, normally the server will respond with SF12 in the RX2 timeslot.
-// For TTN, thr RX2 timeslot is SF9, and we should use that one for TTN
-#define _RX2_SF 9
+// For TTN, the RX2 timeslot is SF9, so we should use that one for TTN
+#if !defined _RXDELAY1
+#	define _RXDELAY1 1000
+#endif
+
+#if !defined _RX2_SF
+#	define _RX2_SF 9
+#endif
 
 
 // This section defines whether we use the gateway as a repeater
@@ -185,14 +197,14 @@
 #define _MUTEX 0
 
 
-// Define if OLED Display is connected to I2C bus. Note that defining an OLED display does not
-// impact performance negatively, certainly if no OLED is connected. Wrong OLED will not show
-// sensible results on the OLED display
-// OLED==0; No OLED display connected
-// OLED==1; 0.9" Oled Screen based on SSD1306
-// OLED==2;	1.3" Oled screens for Wemos, 128x64 SH1106
-#if !defined OLED
-#	define OLED 1
+// Define if Oled Display is connected to I2C bus. Note that defining an Oled display does not
+// impact performance negatively, certainly if no Oled is connected. Wrong Oled will not show
+// sensible results on the Oled display
+// _OLED==0;	No Oled display connected
+// _OLED==1;	0.9" Oled Screen based on SSD1306
+// _OLED==2;	1.3" Oled screens for Wemos, 128x64 SH1106
+#if !defined _OLED
+#	define _OLED 1
 #endif
 
 
@@ -208,25 +220,27 @@
 #define _GATEWAYMGT 0
 
 
-// Do extensive logging
+// Do extensive logging to file
 // Use the ESP8266 SPIFS filesystem to do extensive logging.
 // We must take care that the filesystem never(!) is full, and for that purpose we
 // rather have new records/line of statistics than very old.
 // Of course we must store enough records to make the filesystem work
-#define _STAT_LOG 1
-
+#if !defined _START_LOG
+#	define _STAT_LOG 0
+#endif //_START_LOG
 
 
 // Set the Server Settings (IMPORTANT)
 #define _LOCUDPPORT 1700					// UDP port of gateway! Often 1700 or 1701 is used for upstream comms
 
 // Timing
-#define _MSG_INTERVAL 15					// Reset timer in seconds
-#define _PULL_INTERVAL 55					// PULL_DATA messages to server to get downstream in milliseconds
+#define _PULL_INTERVAL 20					// PULL_DATA messages to server to get downstream in seconds
 #define _STAT_INTERVAL 120					// Send a 'stat' message to server
 #define _NTP_INTERVAL 3600					// How often do we want time NTP synchronization
-#define _WWW_INTERVAL	60					// Number of seconds before we refresh the WWW page
-
+#define _WWW_INTERVAL 60					// Number of seconds before we refresh the WWW page
+#define _FILE_INTERVAL 30					// Number of timer (in secs) before writing to files
+#define _MSG_INTERVAL 31					// Message timeout timer in seconds
+#define _RST_INTERVAL 90					// Reset interval in seconds, total chip reset
 
 // This defines whether or not we would use the gateway as 
 // as sort of backend decoding system for local sensors which decodes
@@ -247,7 +261,7 @@
 // ntp
 // Please add daylight saving time to NTP_TIMEZONES when desired
 #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 NTP_TIMEZONES	2					// How far is our Timezone from UTC (excl daylight saving/summer time)
 #define SECS_IN_HOUR	3600
 #define NTP_INTR 0							// Do NTP processing with interrupts or in loop();
 
@@ -260,7 +274,9 @@
 // as a regular sensor value.
 // Set its LoRa address and key below in this file, See spec. para 4.3.2
 // NOTE: The node is switched off by default. Switch it on in the GUI
-#define _GATEWAYNODE 0
+#if !defined _GATEWAYNODE
+#	define _GATEWAYNODE 0
+#endif //_GATEWAYNODE
 
 
 // We can put the gateway in such a mode that it will (only) recognize
@@ -297,7 +313,9 @@
 //	- SF seen (8-bit integer with SF per bit)
 // The initial version _NUMMAX stores max this many nodes, please make
 // _MAXSEEN==0 when not used
-#define _MAXSEEN 20
+#if !defined _MAXSEEN
+#	define _MAXSEEN 20
+#endif
 #define _SEENFILE "/gwayNum.txt"
 
 
@@ -320,5 +338,3 @@
 // and need no changing
 #define _TTNSERVER "router.eu.thethings.network"
 #define _TTNPORT 1700							// Standard port for TTN
-
-

+ 93 - 43
src/configNode.h

@@ -1,4 +1,4 @@
-// sensor.h; 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 // based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
@@ -11,8 +11,7 @@
 //
 // NO WARRANTY OF ANY KIND IS PROVIDED
 //
-// Author: Maarten Westenberg (mw12554@hotmail.com) 
-
+// Author: Maarten Westenberg (mw12554@hotmail.com)
 //
 // This file contains a number of compile-time settings and declarations that are'
 // specific to the LoRa rfm95, sx1276, sx1272 radio of the gateway.
@@ -23,23 +22,22 @@
 // It is possible to use the gateway as a normal sensor node also. In this case,
 // substitute the node info below.
 #if _GATEWAYNODE==1
-
-// Valid coding for internal sensors are LCODE and RAW. 
-// Make sure to only select one.
-//#define _LCODE 1
-#define _RAW 1
-#define _CHECK_MIC 1
-#define _SENSOR_INTERVAL 300
-
-#define _DEVADDR { 0xAA, 0xAA, 0xAA, 0xAA }
-#define _APPSKEY { 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB }
-#define _NWKSKEY { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC }
-#define _SENSOR_INTERVAL 300
-// For ESP32 based T_BEAM/TTGO boards these two are normally included
-// If included make value 1, else if not, make them 0
-#define _GPS 1
-#define _BATTERY 1
-
+	// Valid coding for internal sensors are LCODE and RAW.
+	// Make sure to only select one.
+#	define _LCODE 1
+//#	define _RAW 1
+#	define _CHECK_MIC 1
+#	define _SENSOR_INTERVAL 300
+
+	// Sensor and app address information
+#	define _DEVADDR { 0x26, 0x01, 0x1B, 0x96 }
+#	define _APPSKEY { 0x02, 0x02, 0x04, 0x32, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
+#	define _NWKSKEY { 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
+
+	// For ESP32 based T_BEAM/TTGO boards these two are normally included
+	// If included make value 1, else if not, make them 0
+#	define _GPS 1
+#	define _BATTERY 1
 #endif //_GATEWAYNODE
 
 
@@ -54,6 +52,26 @@ nodex nodes[] = {
 	{ 0x260116BD , "lora-34 PIR node" },						// F=0
 	{ 0x26011152 , "lora-35 temp+humi node" },					// F=0
 	{ 0x2601148C , "lora-36 test node"  },						// F=0
+// LoRa 37
+	{ 0x26011B90 , "lora-39 temp DS18B20" },					// F=1
+	{ 0x260119A6 , "lora-40 airquality" },						// F=0
+	{ 0x2601117D , "lora-41 temp+humi SR04T" },
+	{ 0x26011593 , "lora-42 temp DS18B20" },					// F=0	
+	{ 0x26011514 , "lora-43 temp DS18B20" },					// F=0
+	{ 0x2601145C , "lora-44 Distance" },						// F=0
+	{ 0x26011D77 , "lora-45 no sensor" },
+	{ 0x2601160F , "lora-46 HTU21 metal case" },				// F=0
+	{ 0x26011E71 , "lora-47 Dallas temperature" },				// F=0
+	{ 0x26011215 , "lora-48 AM2315 temp/Humi" },				// F=0
+	{ 0x2601168F , "lora-49 EU433 Sensor" },					// 433, F=0
+	{ 0x26011b96 , "lora-50 Internal T-Beam gway" },			// F=0
+//	{ 0xAABBCCDD , "lora-51 Heltec OTAA" },						// F=ALL
+	{ 0x26011199 , "lora-52 CubeCell sensor" },					// F=ALL
+	{ 0x26011E52 , "lora-53 gas sensor" },						// F=ALL
+	{ 0x26011F66 , "lora-62 CubeCell Capsule" },				// F=ALL
+	{ 0x260110ED , "lora-63 CubeCell Board" },					// F=ALL
+	{ 0x26011A2B , "lora-64 Metal temp humi" },					// F=0
+	{ 0x260114EA , "lora-65 Waterproof temp humi" },			// F=0
 	{ 0x00000000 , "lora-00 well known sensor" }				// F=0
 };
 
@@ -69,7 +87,7 @@ nodex nodes[] = {
 #if _LOCALSERVER==1
 struct codex  {
 	uint32_t id;				// This is the device ID (coded in 4 bytes uint32_t
-	unsigned char nm[32];			// A name string which is free to choose
+	unsigned char nm[32];		// A name string which is free to choose
 	uint8_t nwkKey[16];			// The Network Session Key of 16 bytes
 	uint8_t appKey[16];			// The Application Session Key of 16 bytes
 };
@@ -81,30 +99,53 @@ struct codex  {
 // Definition of all nodes that we want to decode locally on the gateway.
 //
 codex decodes[] = {
-	{ 0xAAAAAAAA , "lora-EE", 	// F=0
-		{ 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB },
-		{ 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC } 
+	{	0x26011152 , "lora-35",	// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x23, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 } 
+	},
+	{	0x2601148C , "lora-36",	// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 } 
+	},
+	{	0x26011B90 , "lora-39",	// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x27, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
+	},
+	{	0x26011593 , "lora-42",	// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
+	},
+	{	0x26011D77 , "lora-43",	// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
+	},
+	{	0x27000599 , "lora-44",	// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
 	},
-	{ 0xBBBBBBBB , "lora-FF", 	// F=0
-		{ 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB },
-		{ 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC } 
+	{	0x26011B96 , "lora-50",	// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x32, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
 	},
-	{ 0x00000000 , "lora-00",	// F=0
+	{	0x26011B96 , "lora-62",	// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
+	},
+	{	0x26011B96 , "lora-63",	// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
+	},
+	{	0x260114EA , "lora-65",	// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x41, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
+	},
+	{	0x00000000 , "lora-00",	// F=0
 		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
 		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
 	}					
 };
 #endif //_LOCALSERVER
 
-// If you have a second back-end server defined such as Semtech or loriot.io
-// your can define _THINGPORT and _THINGSERVER with your own value.
-// If not, make sure that you do not define these, which will save CPU time
-// Port is UDP port in this program
-//
-// Default for testing: Switched off
-// #define _THINGSERVER "your.extra.server"		// Server URL of the LoRa-udp.js handler
-// #define _THINGPORT 1700						// Port 1700 is old compatibility
-
 
 // Wifi definitions
 // WPA is an array with SSID and password records. Set WPA size to number of entries in array
@@ -122,16 +163,25 @@ struct wpas {
 // below. This is needed to get the gateway working
 //
 wpas wpa[] = {
-	{ "yourSSID", "yourPassword" },
-	{ "Your2SSID", "your2Password" }
+	{ "platenspeler", "maanlama@16" }
 };
 
 
-// Define the name of the accesspoint if the gateway is in accesspoint mode (is
-// getting WiFi SSID and password using WiFiManager)
-#define AP_NAME "ESP8266-Gway-Things4U"
-#define AP_PASSWD "ttnAutoPw"
+// If you have a second back-end server defined such as Semtech or loriot.io
+// your can define _THINGPORT and _THINGSERVER with your own value.
+// If not, make sure that you do not define these, which will save CPU time
+// Port is UDP port in this program
+//
+// Default for testing: Switched off
+// #define _THINGSERVER "westenberg.org"		// Server URL of the LoRa-udp.js handler
+// #define _THINGPORT 1700						// Port 1700 is old compatibility
+
 
+// Define the name of the accesspoint if the gateway is in accesspoint mode (is
+// getting WiFi SSID and password using WiFiManager). 
+// If you do not need them, comment out.
+//#define AP_NAME "Gway-Things4U"
+//#define AP_PASSWD "ttnAutoPw"
 
 
 // For asserting and testing the following defines are used.

+ 8 - 3
src/loraFiles.h

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP mcu's
 //
 // 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
@@ -66,17 +66,21 @@ struct espGwayConfig {
 	uint16_t views;				// Number of sendWebPage() calls
 	uint16_t wifis;				// Number of WiFi Setups
 	uint16_t reents;			// Number of re-entrant interrupt handler calls
-	uint16_t ntpErr;			// Number of UTP requests that failed
 	uint16_t ntps;
 	uint16_t logFileRec;		// Logging File Record number
 	uint16_t logFileNo;			// Logging File Number
 	uint16_t logFileNum;		// Number of log files max
+	uint16_t formatCntr;		// Count the number of formats
+
+	uint16_t ntpErr;			// Number of UTP requests that failed
+	uint16_t waitErr;			// Number of times the loraWait() call failed
+	uint16_t waitOk;			// Number of times the loraWait() call success
 	
 	uint8_t ch;					// index to freqs array, freqs[gwayCofnig.ch]=868100000 default
 	uint8_t sf;					// range from SF7 to SF12
 	uint8_t debug;				// range 0 to 4
 	uint8_t pdebug;				// pattern debug, 
-	uint8_t trusted;			// pattern debug, 
+	uint8_t trusted;			// pattern debug,
 	
 	bool cad;					// is CAD enabled?
 	bool hop;					// Is HOP enabled (Note: default be disabled)
@@ -85,6 +89,7 @@ struct espGwayConfig {
 	bool seen;
 	bool expert;
 	bool monitor;
+	bool dusbStat;				// Status of _DUSB
 	
 } gwayConfig;
 

+ 18 - 17
src/loraModem.h

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 // 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
@@ -56,7 +56,7 @@
 // Set center frequency. If in doubt, choose the first one, comment out all others
 // Each "real" gateway should support the first 3 frequencies according to LoRa spec.
 // NOTE: This means you have to specify at least 3 frequencies here for the single
-//	channel gateway to work. 
+//	channel gateway to work according to spec. 
 struct vector {
 	// Upstream messages
 	uint32_t upFreq;							// 4 bytes
@@ -70,7 +70,8 @@ struct vector {
 	uint8_t  dwnHi;								// 1 bytes
 };
 
-
+// Define all the relevant LoRa Regions
+//==
 #ifdef EU863_870
 // This the the EU863_870 format as used in most of Europe
 // It is also the default for most of the single channel gateway work.
@@ -106,7 +107,7 @@ vector freqs [] = {
 };
 
 #elif defined(US902_928)
-// The frequency plan for USA is a difficult one. As yout can see, the uplink protocol uses
+// The frequency plan for USA is a difficult one. As you can see, the uplink protocol uses
 // SF7-SF10 and BW125 whereas the downlink protocol uses SF7-SF12 and BW500.
 // Also the number of chanels is not equal.
 vector freqs [] = {
@@ -180,10 +181,9 @@ volatile uint8_t _event=0;
 // so we need to store the current value we like to work with
 uint8_t _rssi;	
 
-uint32_t nowTime=0;
-uint32_t msgTime=0;
-uint32_t hopTime=0;
-uint32_t detTime=0;
+uint32_t msgTime=0;							// in seconds, Thru nowSeconds, now()
+uint32_t hopTime=0;							// in micros()
+uint32_t detTime=0;							// In micros()
 
 #if _PIN_OUT==1
 // ----------------------------------------------------------------------------
@@ -271,7 +271,7 @@ struct pins {
 // Each time a message is received or sent the statistics are updated.
 // In case _STATISTICS==1 we define the last _MAXSTAT messages as statistics
 struct stat_t {
-	time_t tmst;							// Time since 1970 in seconds		
+	uint32_t time;							// Time since 1970 in seconds		
 	uint32_t node;							// 4-byte DEVaddr (the only one known to gateway)
 	uint8_t ch;								// Channel index to freqs array
 	uint8_t sf;
@@ -342,27 +342,28 @@ struct stat_t	statr[1];					// Always have at least one element to store in
 // Define the payload structure used to separate interrupt and SPI
 // processing from the loop() part
 uint8_t payLoad[128];						// Payload i
-struct LoraBuffer {
-	uint8_t	* 	payLoad;
+struct LoraDown {
+	uint32_t	tmst;						//
+	uint32_t	freq;
 	uint8_t		payLength;
-	uint32_t	tmst;						// in millis()
 	uint8_t		sfTx;
 	uint8_t		powe;
-	uint32_t	fff;
 	uint8_t		crc;
 	uint8_t		iiq;
+	uint8_t	* 	payLoad;
 } LoraDown;
 
 // Up buffer (from Lora sensor to UDP)
 // This struct contains all data of the buffer received from devices to gateway
 
 struct LoraUp {
-	uint8_t		payLoad[128];
-	uint8_t		payLength;
-	int			prssi; 
 	int32_t		snr;
-	int			rssicorr;
+	uint32_t	tmst;
+	int16_t		prssi; 
+	int16_t		rssicorr;
+	uint8_t		payLength;
 	uint8_t		sf;
+	uint8_t		payLoad[128];
 } LoraUp;
 
 

+ 6 - 6
src/oLED.h

@@ -1,4 +1,4 @@
-// 1-channel LoRa Gateway for ESP8266
+// 1-channel LoRa Gateway for ESP8266 and ESP32
 // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
 //
 // based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
@@ -13,7 +13,7 @@
 //
 // Author: Maarten Westenberg (mw12554@hotmail.com)
 //
-// This file contains a number of compile-time settings and definitions for OLED support.
+// This file contains a number of compile-time settings and definitions for Oled support.
 //
 // ----------------------------------------------------------------------------------------
 
@@ -27,7 +27,7 @@
 // 2. 1.3" OLED with much better and larger display
 // 4. TTGO board
 
-#if OLED>=1										// If OLED is used
+#if _OLED>=1										// If OLED is used
 
 // ----------------------------------------------------------------------------------------
 // Define the different PIN's used for SCL/SDA for each arch.
@@ -56,17 +56,17 @@
 // ----------------------------------------------------------------------------------------
 // Define the different OLED versions
 //
-#if OLED==1
+#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
 
 // This is an 1.3" OLED display which is running on I2C
-#if OLED==2
+#if _OLED==2
 #include "SH1106.h"
 #define OLED_ADDR 0x3C							// Default 0x3C for 1.3" SH1106
 SH1106  display(OLED_ADDR, OLED_SDA, OLED_SCL);	// i2c ADDR & SDA, SCL on wemos
 #endif
 
-#endif//OLED>=1
+#endif// _OLED>=1