Jelajahi Sumber

Version 6.2.6; Initial Version

platenspeler 5 tahun lalu
induk
melakukan
5313b59d66
19 mengubah file dengan 1009 tambahan dan 526 penghapusan
  1. 4 0
      CHANGELOG.md
  2. 2 2
      README.md
  3. 9 7
      TODO.md
  4. 227 25
      platformio.ini
  5. 26 25
      src/ESP-sc-gway.ino
  6. 28 20
      src/_loraFiles.ino
  7. 127 89
      src/_loraModem.ino
  8. 79 14
      src/_repeater.ino
  9. 9 7
      src/_sensor.ino
  10. 13 14
      src/_stateMachine.ino
  11. 1 1
      src/_tcpTTN.ino
  12. 156 105
      src/_txRx.ino
  13. 163 130
      src/_udpSemtech.ino
  14. 27 6
      src/_utils.ino
  15. 40 27
      src/_wwwServer.ino
  16. 25 12
      src/configGway.h
  17. 7 7
      src/configNode.h
  18. 1 1
      src/loraFiles.h
  19. 65 34
      src/loraModem.h

+ 4 - 0
CHANGELOG.md

@@ -16,6 +16,10 @@ Maintained by Maarten Westenberg (mw12554@hotmail.com)
 
 # Release Notes
 
+Features release 6.2.6 (September 08, 2020)
+- Better timing for downstream
+- Other display of statistics
+
 Features release 6.2.5 (April 30, 2020)
 - Repaired SF and BW for upstream
 - Rewrote Monitoring to output only the most significant messages for debug==1

+ 2 - 2
README.md

@@ -1,7 +1,7 @@
 # Single Channel LoRaWAN Gateway
 
-Version 6.2.5, 
-Data: May 21, 2020  
+Version 6.2.6, 
+Data: September 08, 2020  
 Author: M. Westenberg (mw12554@hotmail.com)  
 Copyright: M. Westenberg (mw12554@hotmail.com)  
 

+ 9 - 7
TODO.md

@@ -1,6 +1,6 @@
 # Single Channel LoRaWAN Gateway
 
-Last Updated: February 23, 2020	  
+Last Updated: September 08, 2020	  
 Author: M. Westenberg (mw12554@hotmail.com)  
 Copyright: M. Westenberg (mw12554@hotmail.com)  
 
@@ -16,17 +16,19 @@ Maintained by Maarten Westenberg (mw12554@hotmail.com)
 
 # ToDo Functions
 
-Features not in release 6.2.4
+Features not in release 6.2.6
 
-- Change Downstream timing to be interrupt driven.
+- Make better version for _ENCODE
+- Repair _REPEATER to work for devices that are far away from the gateway
+- Change Downstream timing to be more accurate (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
+- Get HOP frequency functions to work on three frequencies (Naah)
 - Security: Enable passwords for GUI access and WiFi upload (may not be necessary for normal home router use)
 - Enable remote updating through GUI
-- Support FSK (May not be necessary)
-- Support for other Mode A and B of LoRa devices
-- Support for 3G/4G/5G devices
+- Support FSK (This may not be necessary)
+- Support for other Class A and B, C of LoRa devices
+- Support for 3G/4G/5G devices (Probably overkill for ESP devices, better buy a real gateway)
 
 
 

+ 227 - 25
platformio.ini

@@ -4,11 +4,11 @@
 ;   Upload options: custom upload port, speed and extra flags
 ;   Library options: dependencies, extra library storages
 ;   Advanced options: extra scripting
-;
 ; Please visit documentation for the other options and examples
 ; https://docs.platformio.org/page/projectconf.html
-
+;
 ; Nr 21 has WIFIMANAGER set
+; When set as a repaeter, also set the Channel to 1.
 [env:Gateway_21]
 platform = espressif8266
 board = d1_mini
@@ -17,82 +17,284 @@ board_build.f_cpu = 80000000L
 build_flags =
   -D _PIN_OUT=1
   -D _WIFIMANAGER=0
-  -D _SPIFFS_FORMAT=0
+;  -D _SPIFFS_FORMAT=1
+  -D _CHANNEL=0
+  -D _REPEATER=0
   -D _OLED=0
   -D _DUSB=1
   -D _PROFILER=1
   -D _STAT_LOG=1
+  -D _STRICT_1CH=2
+  -D _LOCALSERVER=1
 framework = arduino
 upload_protocol = espota
 board_build.flash_mode = qio
 upload_speed = 115200
 upload_port = 192.168.2.21
 
-[env:Gateway_29]
+[env:Gateway_22]
 platform = espressif8266
 board = d1_mini
 board_build.mcu = esp8266
 board_build.f_cpu = 80000000L
 build_flags =
-  -D _PIN_OUT=2
   -D _WIFIMANAGER=0
-  -D _SPIFFS_FORMAT=0
-  -D _OLED=0
+  -D _OLED=1
   -D _DUSB=1
   -D _PROFILER=1
-  -D _STAT_LOG=0
 framework = arduino
 upload_protocol = espota
 board_build.flash_mode = qio
 upload_speed = 115200
-upload_port = 192.168.2.29
+upload_port = 192.168.2.22
+
+;[env:Gateway_30]
+;platform = espressif8266
+;board = d1_mini
+;board_build.mcu = esp8266
+;board_build.f_cpu = 80000000L
+;build_flags =
+;  -D _PIN_OUT=1
+;  -D _WIFIMANAGER=0
+;  -D _SPIFFS_FORMAT=0
+;  -D _OLED=0
+;  -D _DUSB=1
+;  -D _PROFILER=1
+;  -D _STAT_LOG=0
+;framework = arduino
+;upload_protocol = espota
+;board_build.flash_mode = qio
+;upload_speed = 115200
+;upload_port = 192.168.2.30
+
+;[env:Gateway_HC8]
+;platform = espressif8266
+;board = d1_mini
+;board_build.mcu = esp8266
+;board_build.f_cpu = 80000000L
+;build_flags =
+;  -D _WIFIMANAGER=0
+;  -D _SPIFFS_FORMAT=1
+;  -D _OLED=0
+;  -D _DUSB=1
+;  -D _PROFILER=1
+;  -D _STAT_LOG=0
+;framework = arduino
+;upload_protocol = espota
+;board_build.flash_mode = qio
+;upload_speed = 115200
+;upload_port = COM8
+
+;[env:Gateway_72]
+;platform = espressif32
+;board = heltec_wifi_lora_32
+;framework = arduino
+;build_flags =
+;  -D _SPIFFS_FORMAT=0
+;;  -D _CHANNEL=1
+;;  -D _REPEATER=1
+;  -D _WIFIMANAGER=0
+;  -D _OLED=0
+;  -D _DUSB=1
+;  -D _PROFILER=1
+;  -D _STAT_LOG=0
+;upload_protocol = espota
+;board_build.flash_mode = qio
+;upload_speed = 115200
+;upload_port = 192.168.2.72
 
-[env:Gateway_72]
+[env:Gateway_38]
 platform = espressif32
 board = heltec_wifi_lora_32
 framework = arduino
 build_flags =
-  -D _SPIFFS_FORMAT=0
   -D _WIFIMANAGER=0
-  -D _OLED=0
+  -D _SPIFFS_FORMAT=0
+  -D _OLED=1
   -D _DUSB=1
   -D _PROFILER=1
+;  -D _JSONENCODE=1
+  -D _MAXSEEN=10
+  -D _STAT_LOG=0
 upload_protocol = espota
 board_build.flash_mode = qio
 upload_speed = 115200
-upload_port = 192.168.2.72
+upload_port = 192.168.2.38
+
+; Nr. 54 has WIFIMANAGER sometimes set
+; Also the Gateway is a T-Beam sensor for temperature and GPS (_GATEWAYNODE=1)
+;[env:Gateway_59]
+;platform = espressif32
+;board = heltec_wifi_lora_32
+;build_flags =
+;  -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
+
+;[env:Gateway_23]
+;platform = espressif8266
+;board = d1_mini
+;board_build.mcu = esp8266
+;board_build.f_cpu = 80000000L
+;build_flags =
+;  -D _WIFIMANAGER=0
+;  -D _OLED=0
+;framework = arduino
+;upload_protocol = espota
+;board_build.flash_mode = qio
+;upload_speed = 115200
+;upload_port = 192.168.2.23
 
-[env:Gateway_138]
+;[env:Gateway_24]
+;platform = espressif8266
+;board = d1_mini
+;board_build.mcu = esp8266
+;board_build.f_cpu = 80000000L
+;build_flags =
+;  -D _OLED=1
+;framework = arduino
+;upload_protocol = espota
+;board_build.flash_mode = qio
+;upload_speed = 115200
+;upload_port = 192.168.2.24
+
+;[env:Gateway_26]
+;platform = espressif8266
+;board = d1_mini
+;board_build.mcu = esp8266
+;board_build.f_cpu = 80000000L
+;build_flags =
+;  -D _OLED=1
+;framework = arduino
+;upload_protocol = espota
+;board_build.flash_mode = qio
+;upload_speed = 115200
+;upload_port = 192.168.2.26
+
+;[env:Gateway_27]
+;platform = espressif8266
+;board = d1_mini
+;board_build.mcu = esp8266
+;board_build.f_cpu = 80000000L
+;build_flags =
+;  -D _OLED=1
+;framework = arduino
+;upload_protocol = espota
+;board_build.flash_mode = qio
+;upload_speed = 115200
+;upload_port = 192.168.2.27
+
+;[env:Gateway_28]
+;platform = espressif8266
+;board = d1_mini
+;board_build.mcu = esp8266
+;board_build.f_cpu = 80000000L
+;build_flags =
+;  -D _OLED=1
+;framework = arduino
+;upload_protocol = espota
+;board_build.flash_mode = qio
+;upload_speed = 115200
+;upload_port = 192.168.2.28
+
+;[env:Gateway_29]
+;platform = espressif8266
+;board = d1_mini
+;board_build.mcu = esp8266
+;board_build.f_cpu = 80000000L
+;build_flags =
+;  -D _PIN_OUT=2
+;  -D _OLED=0
+;  -D _WIFIMANAGER=0
+;  -D _SPIFFS_FORMAT=1
+;  -D _DUSB=1
+;  -D _PROFILER=1
+;  -D _STAT_LOG=1
+;framework = arduino
+;upload_protocol = espota
+;board_build.flash_mode = qio
+;upload_speed = 115200
+;upload_port = 192.168.2.29
+
+;[env:Gateway_31]
+;platform = espressif8266
+;board = d1_mini
+;board_build.mcu = esp8266
+;board_build.f_cpu = 80000000L
+;build_flags =
+;  -D _PIN_OUT=2
+;  -D _WIFIMANAGER=1
+;  -D _OLED=2
+;framework = arduino
+;upload_protocol = espota
+;board_build.flash_mode = qio
+;upload_speed = 115200
+;upload_port = 192.168.2.31
+
+[env:Gateway_152]
 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 _SPIFFS_FORMAT=1
+  -D _STAT_LOG=0
 ;  -D _JSONENCODE=1
-;  -D _MAXSEEN=0
+;  -D _MAXSEEN=20
 upload_protocol = espota
 board_build.flash_mode = qio
 upload_speed = 115200
-upload_port = 192.168.2.138
+upload_port = 192.168.2.152
 
-; 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
+;[env:Gateway_174]
+;platform = espressif32
+;board = heltec_wifi_lora_32
+;framework = arduino
+;build_flags =
+;  -D _WIFIMANAGER=0
+;  -D _OLED=1
+;  -D _DUSB=1
+;  -D _PROFILER=1
+;  -D _SPIFFS_FORMAT=0
+;  -D _STAT_LOG=0
+;;  -D _JSONENCODE=1
+;;  -D _MAXSEEN=0
+;upload_protocol = espota
+;board_build.flash_mode = qio
+;upload_speed = 115200
+;upload_port = 192.168.2.174
+
+[env:Gateway_36]
+platform = espressif8266
+board = d1_mini
+board_build.mcu = esp8266
+board_build.f_cpu = 80000000L
 build_flags =
+  -D _PIN_OUT=1
   -D _WIFIMANAGER=0
   -D _SPIFFS_FORMAT=0
-  -D _DUSB=0
+  -D _CHANNEL=0
+  -D _REPEATER=0
   -D _OLED=0
-  -D _GATEWAYNODE=1
+  -D _DUSB=1
   -D _PROFILER=1
+  -D _STAT_LOG=1
+  -D _STRICT_1CH=2
+  ;-D _MONITOR=1
+  -D _MAXSEEN=10
 framework = arduino
 upload_protocol = espota
 board_build.flash_mode = qio
 upload_speed = 115200
-upload_port = 192.168.2.54
+upload_port = 192.168.1.36

+ 26 - 25
src/ESP-sc-gway.ino

@@ -259,8 +259,8 @@ void stateMachine();													// _stateMachine.ino
 
 bool connectUdp();														// _udpSemtech.ino
 int readUdp(int packetSize);											// _udpSemtech.ino
-int sendUdp(IPAddress server, int port, uint8_t *msg, uint16_t length);		// _udpSemtech.ino
-void sendstat();														// _udpSemtech.ino
+int sendUdp(IPAddress server, int port, uint8_t *msg, uint16_t length);	// _udpSemtech.ino
+void sendStat();														// _udpSemtech.ino
 void pullData();														// _udpSemtech.ino
 
 #if _MUTEX==1
@@ -605,7 +605,7 @@ void setup() {
 		rxLoraModem();
 	}
 	LoraUp.payLoad[0]= 0;
-	LoraUp.payLength = 0;									// Init the length to 0
+	LoraUp.size = 0;										// Init the length to 0
 
 	// init interrupt handlers, which are shared for GPIO15 / D8, 
 	// we switch on HIGH interrupts
@@ -619,12 +619,6 @@ void setup() {
 		//attachInterrupt(pins.dio2, Interrupt_2, RISING);	// Separate interrupts		
 	}
 
-#	if _MONITOR>=1
-	if ((debug>=2) && (pdebug & P_TX)) {
-		mPrint("sendPacket:: STRICT=" + String(_STRICT_1CH) );
-	}
-#	endif //_MONITOR
-
 	writeConfig(_CONFIGFILE, &gwayConfig);					// Write config
 	writeSeen(_SEENFILE, listSeen);							// Write the last time record  is seen
 
@@ -664,7 +658,7 @@ void loop ()
 	// 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)) {
+		if ((debug>=0) && (pdebug & P_MAIN)) {
 			mPrint("loop:: ERROR reconnect WLAN");
 		}
 #		endif //_MONITOR
@@ -688,13 +682,15 @@ void loop ()
 #		endif //_MONITOR
 
 		// DOWNSTREAM
-		// Packet may be PKT_PUSH_ACK (0x01), PKT_PULL_ACK (0x03) or PKT_PULL_RESP (0x04)
+		// Packet may be PUSH_ACK (0x01), PULL_ACK (0x03) or PULL_RESP (0x04)
 		// This command is found in byte 4 (buffer[3])
-
+		// Only PULL_RESP carries more data for sensor and needs action,
+		// others are for Gateway only.
+		//
 		if (readUdp(packetSize) < 0) {
 #			if _MONITOR>=1
 			if (debug>=0)
-				mPrint("Dwn readUdp ERROR, returning < 0");
+				mPrint("v readUdp ERROR, returning < 0");
 #			endif //_MONITOR
 			break;
 		}
@@ -703,7 +699,7 @@ void loop ()
 		// 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));
+			//mPrint("v udp received="+String(micros())+", packetSize="+String(packetSize));
 		}
 	}
 
@@ -751,30 +747,30 @@ void loop ()
 		msgTime = nowSeconds;
 	}
 
-#if _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 //_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.
+	// v as less yield() as possible.
 	if (_event == 1) {
 		return;
 	}
 	else yield();
 
-#if _SERVER==1
+#	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
+#	endif //_SERVER
 
 
 
@@ -782,10 +778,10 @@ void loop ()
 	//	
     if ((nowSeconds - statTime) >= _STAT_INTERVAL) {		// Wake up every xx seconds
 		yield();											// on 26/12/2017
-        sendstat();											// Show the status message and send to server
+        sendStat();											// Show the status message and send to server
 #		if _MONITOR>=1
 		if ((debug>=2) && (pdebug & P_MAIN)) {
-			mPrint("Send Pushdata sendstat");
+			mPrint("Send Pushdata sendStat");
 		}
 #		endif //_MONITOR
 
@@ -818,6 +814,11 @@ void loop ()
 	// send PULL_DATA message (*2, par. 4)
 	//
 	// This message will also restart the server which taken approx. 3 ms.
+	// Byte 0:		Prtocol Version
+	// Byte 1-2:	Arbritary Token Value
+	// Byte 3:		PULL_DATA ident ==0x02
+	// Byte 4-7:	Gateway EUI
+	//
 	nowSeconds = now();
     if ((nowSeconds - pullTime) >= _PULL_INTERVAL) {		// Wake up every xx seconds
 
@@ -827,8 +828,8 @@ void loop ()
 		pullTime = nowSeconds;
 		
 #		if _MONITOR>=1
-		if ((debug>=2) && (pdebug & P_RX)) {
-			String response = "UP ESP-sc-gway:: PKT_PULL_DATA message sent: micr=";
+		if ((debug>=3) && (pdebug & P_RX)) {
+			String response = "^ PULL_DATA:: ESP-sc-gway: message micr=";
 			printInt(micros(), response);
 			mPrint(response);
 		}
@@ -848,7 +849,7 @@ void loop ()
 		
 #		if _MONITOR>=1
 		if ((debug>=2) && (pdebug & P_MAIN)) {
-			String response = "UP ESP-sc-gway:: RST_DATA message sent: micr=";
+			String response = "^ ESP-sc-gway:: RST_DATA message sent: micr=";
 			printInt(micros(), response);
 			mPrint(response);
 		}
@@ -860,7 +861,7 @@ void loop ()
 	// We do not use the timer interrupt but use the timing
 	// of the loop() itself which is better for SPI
 #	if NTP_INTR==0
-		// Set the time in a manual way. Do not use setSyncProvider
+		// Set the time in a manual way. v not use setSyncProvider
 		//  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();

+ 28 - 20
src/_loraFiles.ino

@@ -67,10 +67,10 @@ void id_print (String id, String val)
 // ----------------------------------------------------------------------------
 void initConfig(struct espGwayConfig *c)
 {
-	(*c).ch = 0;
-	(*c).sf = _SPREADING;
+	(*c).ch = _CHANNEL;					// Default channel to listen to (for region)
+	(*c).sf = _SPREADING;				// Default Spreading Factor when not using CAD
 	(*c).debug = 1;						// debug level is 1
-	(*c).pdebug = P_GUI | P_MAIN;
+	(*c).pdebug = P_GUI | P_MAIN | P_TX;
 	(*c).cad = _CAD;
 	(*c).hop = false;
 	(*c).seen = true;					// Seen interface is ON
@@ -102,7 +102,7 @@ void initConfig(struct espGwayConfig *c)
 		statr[i].sf=0;							// Init Spreading Factor
 	}
 
-	free(listSeen); delay(10);
+	free(listSeen); delay(50);
 	listSeen = (struct nodeSeen *) malloc((*c).maxSeen * sizeof(struct nodeSeen));
 	for (int i=0; i<(*c).maxSeen; i+=1) {
 		listSeen[i].idSeen=0;
@@ -356,7 +356,7 @@ int writeConfig(const char *fn, struct espGwayConfig *c)
 	File f = SPIFFS.open(fn, "w");
 	if (!f) {
 #if _MONITOR>=1	
-		mPrint("writeConfig: ERROR open file="+String(fn));
+		mPrint("writeConfig:: ERROR open file="+String(fn));
 #endif //_MONITOR
 		return(-1);
 	}
@@ -468,7 +468,7 @@ int addLog(const unsigned char * line, int cnt)
 
 	int i=0;
 #	if _MONITOR>=1
-	if ((debug>=1) && (pdebug & P_RX)) {
+	if ((debug>=2) && (pdebug & P_RX)) {
 		char s[256];
 		i+=12;									// First 12 chars are non printable
 		sprintf(s, "addLog:: fileno=%d, rec=%d : %s",gwayConfig.logFileNo,gwayConfig.logFileRec,&line[i]);
@@ -553,7 +553,7 @@ int readSeen(const char *fn, struct nodeSeen *listSeen)
 	File f = SPIFFS.open(fn, "r");
 	if (!f) {
 #		if _MONITOR>=1
-			mPrint("readSeen:: ERROR open file=" + String(fn));
+			mPrint("readSeen:: ERROR open file="+String(fn));
 #		endif //_MONITOR
 		return(-1);
 	}
@@ -575,14 +575,18 @@ int readSeen(const char *fn, struct nodeSeen *listSeen)
 		val=f.readStringUntil('\t'); listSeen[i].cntSeen = (uint32_t) val.toInt();
 		val=f.readStringUntil('\t'); listSeen[i].chnSeen = (uint8_t) val.toInt();
 		val=f.readStringUntil('\n'); listSeen[i].sfSeen = (uint8_t) val.toInt();
-		
+
+		if ((int32_t)listSeen[i].idSeen != 0) {
+			iSeen++;									// Increase index, new record read
+		}
+
 #		if _MONITOR>=1
 		if ((debug>=2) && (pdebug & P_MAIN)) {
-				mPrint("readSeen:: idSeen ="+String(listSeen[i].idSeen,HEX)+", i="+String(i));
+				mPrint(" readSeen:: idSeen ="+String(listSeen[i].idSeen,HEX)+", i="+String(i));
 		}
-#		endif
-		iSeen++;											// Increase index, new record read
+#		endif								
 	}
+
 	f.close();
 
 #endif //_MAXSEEN
@@ -623,11 +627,12 @@ int writeSeen(const char *fn, struct nodeSeen *listSeen)
 	delay(500);
 
 	for (i=0; i<iSeen; i++) {							// For all records indexed
-			f.print((time_t)listSeen[i].timSeen);	f.print('\t');
-			f.print((int32_t)listSeen[i].idSeen);	f.print('\t'); // Typecast to avoid errors in unsigned conversion!
-			f.print((uint32_t)listSeen[i].cntSeen);	f.print('\t');
-			f.print((uint8_t)listSeen[i].chnSeen);	f.print('\t');
-			f.print((uint8_t)listSeen[i].sfSeen);	f.print('\n');			
+		if ((int32_t)listSeen[i].idSeen == 0) break;
+		f.print((time_t)listSeen[i].timSeen);	f.print('\t');
+		f.print((int32_t)listSeen[i].idSeen);	f.print('\t'); // Typecast to avoid errors in unsigned conversion!
+		f.print((uint32_t)listSeen[i].cntSeen);	f.print('\t');
+		f.print((uint8_t)listSeen[i].chnSeen);	f.print('\t');
+		f.print((uint8_t)listSeen[i].sfSeen);	f.print('\n');			
 	}
 	
 	f.close();
@@ -653,8 +658,10 @@ int writeSeen(const char *fn, struct nodeSeen *listSeen)
 // ----------------------------------------------------------------------------
 int addSeen(struct nodeSeen *listSeen, struct stat_t stat) 
 {
+
 #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.
@@ -669,7 +676,7 @@ int addSeen(struct nodeSeen *listSeen, struct stat_t stat)
 //			writeSeen(_SEENFILE, listSeen);
 			
 #			if _MONITOR>=2
-			if ((debug>=1) && (pdebug & P_MAIN)) {
+			if ((debug>=3) && (pdebug & P_MAIN)) {
 				mPrint("addSeen:: adding i="+String(i)+", node="+String(stat.node,HEX));
 			}
 #			endif
@@ -677,9 +684,10 @@ int addSeen(struct nodeSeen *listSeen, struct stat_t stat)
 			return 1;
 		}
 	}
-	
+
 	// else: We did not find the current record so make a new listSeen entry
 	if ((i>=iSeen) && (i<gwayConfig.maxSeen)) {
+		mPrint("addSeen:: Adding node i="+String(i) );
 		listSeen[i].idSeen = stat.node;
 		listSeen[i].chnSeen = stat.ch;
 		listSeen[i].sfSeen = stat.sf;				// The SF argument
@@ -689,8 +697,8 @@ int addSeen(struct nodeSeen *listSeen, struct stat_t stat)
 	}
 
 #	if _MONITOR>=1
-	if ((debug>=2) && (pdebug & P_MAIN)) {
-		String response= "addSeen:: i=";
+	if ((debug>=1) && (pdebug & P_MAIN)) {
+		String response= "  addSeen:: i=";
 		response += i;
 		response += ", tim=";
 		stringTime(stat.time, response);

+ 127 - 89
src/_loraModem.ino

@@ -114,7 +114,7 @@ uint8_t readRegister(uint8_t addr)
 
 	SPI.beginTransaction(readSettings);				
     digitalWrite(pins.ss, LOW);					// Select Receiver
-	SPI.transfer(addr & 0x7F);
+	SPI.transfer(addr & 0x7F);					// First address bit 0x00 is read
 	uint8_t res = (uint8_t) SPI.transfer(0x00);
     digitalWrite(pins.ss, HIGH);				// Unselect Receiver
 	SPI.endTransaction();
@@ -140,11 +140,10 @@ void writeRegister(uint8_t addr, uint8_t value)
 	SPI.beginTransaction(writeSettings);
 	digitalWrite(pins.ss, LOW);					// Select Receiver
 	
-	SPI.transfer((addr | 0x80) & 0xFF);
+	SPI.transfer((addr | 0x80) & 0xFF);			// 0x80 is write operation
 	SPI.transfer(value & 0xFF);
 	
     digitalWrite(pins.ss, HIGH);				// Unselect Receiver
-	
 	SPI.endTransaction();
 }
 
@@ -154,7 +153,8 @@ void writeRegister(uint8_t addr, uint8_t value)
 // Function writes one byte at a time.
 // Parameters:
 //	addr: SPI address to write to
-//	value: The value to write to address
+//	buf: The values to write to address
+//	len: Length of the buffer in bytes
 // Returns:
 //	<void>
 // ----------------------------------------------------------------------------------------
@@ -162,17 +162,19 @@ void writeRegister(uint8_t addr, uint8_t value)
 void writeBuffer(uint8_t addr, uint8_t *buf, uint8_t len)
 {
 	//noInterrupts();							// XXX
-	
+
 	SPI.beginTransaction(writeSettings);
 	digitalWrite(pins.ss, LOW);					// Select Receiver
-	
+
 	SPI.transfer((addr | 0x80) & 0xFF);			// write buffer address
-	for (uint8_t i=0; i<len; i++) {
+	for (uint8_t i=0; i<len; i++) {				// write all bytes of buffer
 		SPI.transfer(buf[i] & 0xFF);
 	}
+
     digitalWrite(pins.ss, HIGH);				// Unselect Receiver
-	
 	SPI.endTransaction();
+
+	//interrupts();
 }
 
 // ----------------------------------------------------------------------------------------
@@ -187,7 +189,7 @@ void writeBuffer(uint8_t addr, uint8_t *buf, uint8_t len)
 //		CRC_ON == 0x04
 //
 //	sf is SF7 to SF12
-//	CRC is 0x00 (off) or 
+//	CRC is 0x00 (off) or 0x04 (On)
 // ----------------------------------------------------------------------------------------
 
 void setRate(uint8_t sf, uint8_t crc) 
@@ -197,7 +199,7 @@ void setRate(uint8_t sf, uint8_t crc)
 	if ((sf<SF7) || (sf>SF12)) {
 #		if _MONITOR>=2
 		if ((debug>=1) && (pdebug & P_RADIO)) {
-			mPrint("setRate:: SF=" + String(sf));
+			mPrint("setRate:: SF=" + String(sf) );
 		}
 #		endif //_MONITOR
 		sf=8;
@@ -205,7 +207,7 @@ void setRate(uint8_t sf, uint8_t crc)
 
 	// Set rate based on Spreading Factor etc
     if (sx1272) {
-		mc1= 0x0A;				// SX1276_MC1_BW_250 0x80 | SX1276_MC1_CR_4_5 0x02
+		mc1= 0x0A;						// SX1276_MC1_BW_250 0x80 | SX1276_MC1_CR_4_5 0x02 (MMM define BW)
 		mc2= ((sf<<4) | crc) % 0xFF;
 		// SX1276_MC1_BW_250 0x80 | SX1276_MC1_CR_4_5 0x02 | SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01
         if (sf == SF11 || sf == SF12) { 
@@ -213,17 +215,18 @@ void setRate(uint8_t sf, uint8_t crc)
 		}			        
     }
 	
-	// For sx1276 chips is the CRC ON is 
+	// For sx1276 chips is the CRC is either ON for receive or OFF for transmit
 	else {
 		
-		if (sf==SF8) {
-			mc1= 0x78;				// SX1276_MC1_BW_125==0x70 | SX1276_MC1_CR_4_8==0x08
-		}
-		else {
-			mc1= 0x72;				// SX1276_MC1_BW_125==0x70 | SX1276_MC1_CR_4_5==0x02
-		}
-		mc2= ((sf<<4) | crc) & 0xFF; // crc is 0x00 or 0x04==SX1276_MC2_RX_PAYLOAD_CRCON
-		mc3= 0x04;					// 0x04; SX1276_MC3_AGCAUTO
+		//if (sf==SF8) {
+		//	mc1= 0x78;					// SX1276_MC1_BW_125==0x70 | SX1276_MC1_CR_4_8==0x08
+		//}
+		//else {
+			mc1= 0x72;					// SX1276_MC1_BW_125==0x70 | SX1276_MC1_CR_4_5==0x02 (4/5)
+										// MMM Read this from the freq table
+		//
+		mc2= ((sf<<4) | crc) & 0xFF;	// crc is 0x00 or 0x04==SX1276_MC2_RX_PAYLOAD_CRCON
+		mc3= 0x04;						// 0x04; SX1276_MC3_AGCAUTO
         if (sf == SF11 || sf == SF12) { mc3|= 0x08; }		// 0x08 | 0x04
     }
 	
@@ -270,9 +273,8 @@ void  setFreq(uint32_t freq)
 // ----------------------------------------------------------------------------------------
 void setPow(uint8_t powe)
 {
-	if (powe >= 16) powe = 15;
-	//if (powe >= 15) powe = 14;
-	else if (powe < 2) powe =2;
+	if (powe > 15) powe = 15;
+	else if (powe < 2) powe = 2;
 	
 	ASSERT((powe>=2)&&(powe<=15));
 	
@@ -291,12 +293,17 @@ void setPow(uint8_t powe)
 // Values are 0x00 to 0x07
 // The value is set for the lowest 3 bits, the other bits are as before.
 // ----------------------------------------------------------------------------------------
-void  opmode(uint8_t mode)
+void opmode(uint8_t mode)
 {
-	if (mode == OPMODE_LORA) 
+	if (mode == OPMODE_LORA) {
+#		ifdef CFG_sx1276_radio
+		//mode |= 0x08;   											// TBD: sx1276 high freq
+#		endif
 		writeRegister(REG_OPMODE, (uint8_t) mode);
-	else
+	}
+	else {
 		writeRegister(REG_OPMODE, (uint8_t)((readRegister(REG_OPMODE) & ~OPMODE_MASK) | mode));
+	}
 }
 
 // ----------------------------------------------------------------------------------------
@@ -316,7 +323,7 @@ void hop()
 	
 	// 4. Set spreading Factor
 	sf = SF7;													// Starting the new frequency 
-	setRate(sf, 0x40);											// set the sf to SF7 
+	setRate(sf, 0x04);											// set the sf to SF7, and CRC to 0x04
 		
 	// Low Noise Amplifier used in receiver
 	writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN);  			// 0x0C, 0x23
@@ -325,7 +332,7 @@ void hop()
 	writeRegister(REG_SYNC_WORD, (uint8_t) 0x34);				// set 0x39 to 0x34 LORA_MAC_PREAMBLE
 	
 	// prevent node to node communication
-	writeRegister(REG_INVERTIQ,0x27);							// 0x33, 0x27; to reset from TX
+	writeRegister(REG_INVERTIQ,0x27);							// set reg 0x33 to 0x27; to reset from TX
 	
 	// Max Payload length is dependent on 256 byte buffer. At startup TX starts at
 	// 0x80 and RX at 0x00. RX therefore maximized at 128 Bytes
@@ -367,8 +374,8 @@ void hop()
 // 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).
-// This is the "lowlevel" receive function called by stateMachine()
-// dealing with the radio specific LoRa functions.
+// This is the "lowlevel" receive function called by stateMachine() interrupt.
+// It deals with the radio specific LoRa functions.
 //
 // |         | CR = 4/8  | CR= Coding Rate      |             |
 // |Preamble |Header| CRC| Payload              | Payload CRC |
@@ -378,6 +385,8 @@ void hop()
 // valid for SF6 and only for Uplink so it does not work for LoraWAN.
 //
 // Parameters:
+//		Preamble:
+//		Header:
 //		Payload: uint8_t[] message. when message is read it is returned in payload.
 // Returns:
 //		Length of payload received
@@ -396,7 +405,6 @@ void hop()
 // ----------------------------------------------------------------------------------------
 uint8_t receivePkt(uint8_t *payload)
 {
-
     statc.msg_ttl++;													// Receive statistics counter
 
     uint8_t irqflags = readRegister(REG_IRQ_FLAGS);						// 0x12; read back flags											
@@ -486,7 +494,7 @@ uint8_t receivePkt(uint8_t *payload)
 #		if _MONITOR>=1
 		if ((debug>=1) && (pdebug & P_RX)) {
 		
-			String response = "UP receivePkt:: rxPkt: t=";
+			String response = "^ receivePkt:: rxPkt: t=";
 			stringTime(now(), response);
 			response += ", f=" + String(gwayConfig.ch) + ", sf=" + String(sf);
 			
@@ -504,20 +512,17 @@ uint8_t receivePkt(uint8_t *payload)
 
 			// 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
-
 #			if _TRUSTED_DECODE>=2
 			if (debug>=1)  {							// Must be 1 for operational use
 
 				int index;								// The index of the codex struct to decode
-				//String response="";
-
 				uint8_t data[receivedCount];
 			
 				if ((index = inDecodes((char *)(payload+1))) >=0 ) {	
 					mPrint(", Ind="+String(index));
 				}
-				else if (debug>=1) {	
-					mPrint(", ERROR No Index");
+				else {	
+					mPrint(", ERROR No Index for inDecodes");
 					return(receivedCount);
 				}	
 
@@ -549,17 +554,22 @@ uint8_t receivePkt(uint8_t *payload)
 				
 				Serial.print(F(", len="));
 				Serial.print(CodeLength);
-				Serial.print(F(", data="));
 
-				for (int i=0; i<receivedCount; i++) {
-					if (data[i]<=0xF) Serial.print('0');
-					Serial.print(data[i], HEX);
-					Serial.print(' ');
-				}
 			}
 #			endif //_TRUSTED_DECODE
-			
+
 			mPrint(response);							// Print response for Serial or mPrint
+			
+			// ONLY on USB
+			Serial.print(F(", paylength="));
+			Serial.print(receivedCount);
+			Serial.print(F(", payload="));
+			for (int i=0; i<receivedCount; i++) {
+					if (payload[i]<=0xF) Serial.print('0');
+					Serial.print(payload[i], HEX);
+					Serial.print(' ');
+			}
+			Serial.println();
 		}
 #		endif //_MONITOR
 		return(receivedCount);
@@ -579,22 +589,29 @@ uint8_t receivePkt(uint8_t *payload)
 	
 // ------------------------------ DOWN ----------------------------------------------------
 //
+// The DOWN function fills the payLoad buffer with the data for payLength
 // 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
 // 
 // NOTE:: writeRegister functions should not be used outside interrupts
+// The data fields are the following:
+//	Byte 0:
+//	Byte 1-4:	TTN Address LSB first
+//	Byte 5:		Node ID?
+//	Byte 6:
 // ----------------------------------------------------------------------------------------
 bool sendPkt(uint8_t *payLoad, uint8_t payLength)
 {
-#	if _MONITOR>=2
+#	if _MONITOR>=1
 	if (payLength>=128) {
-		if (debug>=1) {
-			mPrint("sendPkt Dwn:: len="+String(payLength));
+		if (debug>=2) {
+			mPrint("V sendPkt:: len="+String(payLength));
 		}
 		return false;
 	}
 #	endif //_MONITOR
-	
+
+	// MMM?
 	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_TX_BASE_AD));	// 0x0D, 0x0E
 	
 	writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) payLength);				// 0x22
@@ -622,39 +639,50 @@ bool sendPkt(uint8_t *payLoad, uint8_t payLength)
 // 
 // Parameter: uint32-t tmst gives the micros() value when transmission should start. (!!!)
 // 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.
+// Note: We assume LoraDown->sf contains the SF we will use for downstream message.
 //		gwayConfig.txDelay is the delay as specified in the GUI
+//
+//	Parameters:
+//
+//	Returns:
+//		1 if successful (There is sometings to wait for)
+//		0 if not successful (So if RX1 is in effect, we go to RX2)
 // ----------------------------------------------------------------------------------------
 
-void loraWait(struct LoraDown *LoraDown)
+int loraWait(struct LoraDown *LoraDown)
 {
 	if (LoraDown->imme == 1) {
 		if ((debug>=1) && (pdebug & P_TX)) {
 			mPrint("loraWait:: imme is 1");
 		}
-		return;
+		return(1);
 	}
 	
 	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 ((delayTmst > 8000000) || (delayTmst < 0)) {		// Delay is  > 8 secs or less than 0
 #		if _MONITOR>=1
 		if (delayTmst > 8000000) {
-			String response= "Dwn loraWait:: ERROR: ";
+			String response= "v loraWait:: ERROR: ";
 			printDwn(LoraDown,response);
 			mPrint(response);
 		}
+		else {
+			String response="v loraWait:: return 0: ";
+			printDwn(LoraDown, response);
+			mPrint(response);
+		}
 #		endif //_MONITOR
 		gwayConfig.waitErr++;
-		return;
+		
+		return(0);
 	}
 
 	// For larger delay times we use delay() since that is for > 15ms
 	// This is the most efficient way.
 	while (delayTmst > 15000) {
 		delay(15);										// ms delay including yield, slightly shorter
-//		delayMicroseconds(15000);						// ms delay including yield, slightly shorter
 		delayTmst -= 15000;
 	}
 	
@@ -664,7 +692,7 @@ void loraWait(struct LoraDown *LoraDown)
 	delayMicroseconds(delayTmst);
 
 	gwayConfig.waitOk++;
-	return;
+	return (1);
 }
 
 
@@ -674,7 +702,7 @@ void loraWait(struct LoraDown *LoraDown)
 // After successful transmission (dio0==1) TxDone re-init the receiver
 //
 //	crc is set to 0x00 for TX
-//	iiq is set to 0x27 (or 0x40 based on ipol value in txpkt)
+//	iiq is set to 0x40 down (or 0x40 up based on ipol value in txpkt)
 //
 //	1. opmode Lora (only in Sleep mode)
 //	2. opmode StandBY
@@ -691,17 +719,17 @@ void loraWait(struct LoraDown *LoraDown)
 // 13. write REG LoRa Payload Length
 // 14. Write buffer (byte by byte)
 // 15. Wait until the right time to transmit has arrived
-// 16. opmode TX
+// 16. opmode TX, start actual transmission
 //
 // Transmission to the device not is not done often, but is time critical.
 // ----------------------------------------------------------------------------------------
 
 void txLoraModem(struct LoraDown *LoraDown)
 {
-
 	_state = S_TX;
 		
 	// 1. Select LoRa modem from sleep mode
+	//opmode(OPMODE_SLEEP);
 	//opmode(OPMODE_LORA);									// set register 0x01 to 0x80
 	
 	// Assert the value of the current mode
@@ -711,7 +739,7 @@ void txLoraModem(struct LoraDown *LoraDown)
 	opmode(OPMODE_STANDBY);									// set 0x01 to 0x01
 	
 	// 3. Init spreading factor and other Modem setting
-	setRate(LoraDown->sfTx, LoraDown->crc);
+	setRate(LoraDown->sf, LoraDown->crc);
 	
 	// Frequency hopping
 	//writeRegister(REG_HOP_PERIOD, (uint8_t) 0x00);		// set 0x24 to 0x00 only for receivers
@@ -723,14 +751,14 @@ void txLoraModem(struct LoraDown *LoraDown)
 	setPow(LoraDown->powe);
 	
 	// 7. prevent node to node communication
-	writeRegister(REG_INVERTIQ, (uint8_t) (LoraDown->iiq));	// 0x33, (0x27 or 0x40)
+	writeRegister(REG_INVERTIQ, readRegister(REG_INVERTIQ) | (uint8_t)(LoraDown->iiq));	// 0x33, (0x27 up or |0x40 down)
 	
 	// 8. set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP (or less for 1ch gateway)
     writeRegister(REG_DIO_MAPPING_1, (uint8_t)(
 		MAP_DIO0_LORA_TXDONE | 
 		MAP_DIO1_LORA_NOP | 
 		MAP_DIO2_LORA_NOP |
-		MAP_DIO3_LORA_CRC));
+		MAP_DIO3_LORA_NOP));								// was MAP_DIO3_LORA_CRC
 	
 	// 9. clear all radio IRQ flags
     writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
@@ -739,32 +767,39 @@ void txLoraModem(struct LoraDown *LoraDown)
     writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~IRQ_LORA_TXDONE_MASK);
 	
 	// txLora
-	opmode(OPMODE_FSTX);									// set 0x01 to 0x02 (actual value becomes 0x82)
+	opmode(OPMODE_FSTX);													// set reg 0x01 to 0x02 (actual value becomes 0x82)
 	
 	// 11, 12, 13, 14. write the buffer to the FiFo
-	sendPkt(LoraDown->payLoad, LoraDown->payLength);
+	sendPkt(LoraDown->payLoad, LoraDown->size);
 	
 	//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) LoraDown->payLength);		// set 0x22, max 0x40==64Byte long
+	writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) LoraDown->size);			// set reg 0x22 to  0x40==64Byte long
 	
 	//For TX we have to set the MAX_PAYLOAD_LENGTH
-	writeRegister(REG_MAX_PAYLOAD_LENGTH, (uint8_t) MAX_PAYLOAD_LENGTH);	// set 0x22, max 0x40==64Byte long
+	writeRegister(REG_MAX_PAYLOAD_LENGTH, (uint8_t) MAX_PAYLOAD_LENGTH);	// set reg 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) 0xFF);// set 0x12 to 0xFF, clear TXDONE and others
+	writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);						// Clear the mask
+//	writeRegister(REG_IRQ_FLAGS, (uint8_t) IRQ_LORA_TXDONE_MASK);			// set reg 0x12 to 0x08, clear TXDONE
+	writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);							// set reg 0x12 to 0xFF, clear TXDONE and others
 	
 	// 16. Initiate actual transmission of FiFo
-	opmode(OPMODE_TX);											// set 0x01 to 0x03 (actual value becomes 0x83)
+	opmode(OPMODE_TX);														// set reg 0x01 to 0x03 (actual value becomes 0x83)
+
+	// After message transmitted the sender switches to STANDBY state, and issues TXDONE
 
 #	if _MONITOR>=1
 	if ((debug>=1) && (pdebug & P_TX)) {
-		String response = "Dwn txLoraModem:: end: ";
+		String response = "v txLoraModem:: end=";
 		printDwn(LoraDown, response);
+		response += ", size=" + String(LoraDown->size);
+		response += ", data=";
+		for(int i=0; i<LoraDown->size; i++) {
+			response += String(LoraDown->payLoad[i],HEX) + " ";
+		}
 		mPrint(response);
 	}
 #	endif //_MONITOR
@@ -936,7 +971,7 @@ void cadScanner()
 //	4.	Set rate and Spreading Factor
 //	5.	Set chip version
 //	6.	Set SYNC word
-//	7.	Set ranp-up time
+//	7.	Set ramp-up time
 //	8.	Set interrupt masks
 //	9.	Clear INT flags
 // ----------------------------------------------------------------------------------------
@@ -957,35 +992,33 @@ void initLoraModem()
 	delayMicroseconds(10000);
 #endif
 	// 1. Set radio to sleep
-	opmode(OPMODE_SLEEP);										// set register 0x01 to 0x00
+	opmode(OPMODE_SLEEP);										// set reg 0x01 to 0x00
 
 	// 2 Set LoRa Mode
-	opmode(OPMODE_LORA);										// set register 0x01 to 0x80
+	opmode(OPMODE_LORA);										// set reg 0x01 to 0x80
 	
 	// 3. Set frequency based on value in gwayConfig.ch
 	setFreq(freqs[gwayConfig.ch].upFreq);						// set to 868.1MHz or the last saved frequency
 	
 	// 4. Set spreading Factor
-    setRate(sf, 0x04);
+    setRate(sf, 0x04);											//
 	
 	// Low Noise Amplifier used in receiver
-    writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN);  			// 0x0C, 0x23
+    writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN);  			// set reg 0x0C to 0x23
 #	if _PIN_OUT==4
 		delay(1);
 #	endif
 
 	// 5. Set chip type/version
-    uint8_t version = readRegister(REG_VERSION);				// Read the LoRa chip version id
-    if (version == 0x22) {
-        // sx1272
+    uint8_t version = readRegister(REG_VERSION);				// read reg 0x34 the LoRa chip version id
+    if (version == 0x22) {										// sx1272==0x22
 #		if _DUSB>=2
 			Serial.println(F("WARNING:: SX1272 detected"));
 #		endif
         sx1272 = true;
     } 
 	
-	else if (version == 0x12) {
-        // sx1276?
+	else if (version == 0x12) {									// sx1276==0x12
 #			if _DUSB>=2
             if (debug >=1) 
 				Serial.println(F("SX1276 starting"));
@@ -1008,31 +1041,36 @@ void initLoraModem()
 			Serial.println();
 			Serial.flush();
 #		endif
-		die("");												// Maybe first try another kind of receiver
+		die("initLoraModem, unknown transceiver?");				// Maybe first try another kind of receiver
     }
 	// If we are here, the chip is recognized successfully
 	
 	// 6. set sync word
-	writeRegister(REG_SYNC_WORD, (uint8_t) 0x34);				// set 0x39 to 0x34 LORA_MAC_PREAMBLE
+	writeRegister(REG_SYNC_WORD, (uint8_t) 0x34);				// set reg 0x39 to 0x34 LORA_MAC_PREAMBLE
 	
 	// prevent node to node communication
-	writeRegister(REG_INVERTIQ,0x27);							// 0x33, 0x27; to reset from TX
+	writeRegister(REG_INVERTIQ,0x27);							// set reg 0x33 to 0x27; reset from TX
 	
 	// Max Payload length is dependent on 256 byte buffer. At startup TX starts at
 	// 0x80 and RX at 0x00. RX therefore maximized at 128 Bytes
-	writeRegister(REG_MAX_PAYLOAD_LENGTH,MAX_PAYLOAD_LENGTH);	// set 0x23 to 0x80==128 bytes
-	writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH);			// 0x22, 0x40==64Byte long
+	writeRegister(REG_MAX_PAYLOAD_LENGTH,PAYLOAD_LENGTH);		// set reg 0x23 to 0x80==128 bytes
+	//writeRegister(REG_MAX_PAYLOAD_LENGTH,MAX_PAYLOAD_LENGTH);	// set reg 0x23 to 0x80==128 bytes
+	writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH);			// set reg 0x22,to 0x40==64Byte long
 	
 	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_RX_BASE_AD));	// set reg 0x0D to 0x0F
-	writeRegister(REG_HOP_PERIOD,0x00);							// reg 0x24, set to 0x00
+	writeRegister(REG_HOP_PERIOD,0x00);							// set reg 0x24, to 0x00
 
 	// 7. Config PA Ramp up time								// set reg 0x0A  
 	writeRegister(REG_PARAMP, (readRegister(REG_PARAMP) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec
 	
 	// Set 0x4D PADAC for SX1276 ; XXX register is 0x5a for sx1272
-	writeRegister(REG_PADAC_SX1276,  0x84); 					// set 0x4D (PADAC) to 0x84
+	//Semtech rev. 5, Aug 2016, para 
+	writeRegister(REG_PADAC_SX1276,  0x84); 					// set reg 0x4D (PADAC) to 0x84
 	//writeRegister(REG_PADAC, readRegister(REG_PADAC) | 0x4);
-	
+
+	// Set treshold according to sx1276 specs
+	writeRegister(REG_DET_TRESH, 0x0A);
+
 	// 8. Reset interrupt Mask, enable all interrupts
 	writeRegister(REG_IRQ_FLAGS_MASK, 0x00);
 	

+ 79 - 14
src/_repeater.ino

@@ -18,28 +18,93 @@
 		
 #if _REPEATER==1
 
-// Define input channel and output channel
-#define _ICHAN 0
-#define _OCHAN 1
-
 #ifdef _TTNSERVER
-#error "Please undefined _THINGSERVER, for REPEATER shutdown WiFi"
-#endif
+#	error "Please undefine _TTNSERVER, for _REPEATER"
+#endif //_TTNSERVER
+#ifdef _THINGSERVER
+#	error "Please undefine _THINGSERVER, for _REPEATER"
+#endif //_THINGSERVER
+
+
+// ------------------------ UP / DOWN -----------------------------	
+// REPEATLORA()
+// This function picks up a message upstream coming from the chip
+// and sends it again downstream at a different frequency
+// Parameters:
+//	LUP:	Lora Up message coming int at freq IN
+//	LDWN:	Lora Down message going to the transmitter at a 
+//			different frequancy
+//
+// Incoming Message Format:
+//	Byte 0:		// MHDR 0x40 == unconfirmed up message,
+//	Byte 1-4:	Dev Address in LSBF sequence
+//	Byte 5-
+//
+// Return:
+//	1 when succesful
+//	-<code> when failure
+// ----------------------------------------------------------------
+int repeatLora(struct LoraUp * LUP) {
+
+	struct LoraDown lDown;								// We send ALMOST the same message down
+	struct LoraDown * LDWN = & lDown;
+	
+	LDWN->tmst 		= LUP->tmst;
+	LDWN->freq 		= freqs[(gwayConfig.ch-1)%3].dwnFreq;
+	LDWN->size		= LUP->size;
+	LDWN->sf		= LUP->sf;
+	LDWN->powe		= 15;								// Default for normal frequencies
+	LDWN->crc		= 1;
+	LDWN->iiq		= 0x27;								// 0x40 when true or 0x27 when ipol false
+	LDWN->imme		= false;
+
+	//strncpy((char *)((* LDWN).payLoad), (char *)((* LUP).payLoad), (int)((* LUP).size));
+	
+	LDWN->payLoad	= LUP->payLoad;
+
+	yield();										// During development, clean kernel.
+
+	Serial.print("repeatLora:: size="); Serial.print(LDWN->size);
+	Serial.print(", sf="); Serial.print(LDWN->sf);
+	Serial.print(", iiq="); Serial.print(LDWN->iiq,HEX);
+	Serial.print(", UP ch="); Serial.print(gwayConfig.ch,HEX);
+	Serial.print(", DWN ch="); Serial.print((gwayConfig.ch-1)%3,HEX);
+	Serial.print(", freq="); Serial.print(LDWN->freq);
+	Serial.println();
+	
+	// Set the new channel to transmit on
+	//setFreq(freqs[(gwayConfig.ch-1)%3].dwnFreq);	// Set OFREQ One lower (initially 0)
+
+	// Once the frequency is set, we can transmit
+
+	writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);			// MMM 200407 Reset
+	writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);				// reset interrupt flags
+	txLoraModem(LDWN);
+	_event=1;
+	_state=S_TXDONE;
+
+	yield();
+	startReceiver();											// (re)Start the receiver function
 
-// Send a LoRa message out from the gateway transmitter
-// XXX Maybe we should block the received ontul the message is transmitter
 
-int sendLora(char *msg, int len) {
 	// Check when len is not exceeding maximum length
-	Serial.print("sendLora:: ");
+	// char s[129];
+	// sprintf(s,"repeatLora:: ch=%d, to=%d, data=", gwayConfig.ch, (gwayConfig.ch-1)%3) );
+	Serial.print("repeatLora:: ch=");
+	Serial.print(gwayConfig.ch);
+	Serial.print(", to ch=");
+	Serial.print((gwayConfig.ch-1)%3);
+	Serial.print(", data=");
 	
-	for (int i=0; i< len; i++) {
-		Serial.print(msg[1],HEX);
+	for (int i=0; i< LDWN->size; i++) {
+		Serial.print(LDWN->payLoad[i],HEX);
 		Serial.print('.');
 	}
-	
+	Serial.println();
 	if (debug>=2) Serial.flush();
+	
 	return(1);
 }
 
-#endif //_REPEATER==1
+
+#endif //_REPEATER

+ 9 - 7
src/_sensor.ino

@@ -504,12 +504,14 @@ 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;
-	struct LoraUp LUP;
 	uint8_t NwkSKey[16] = _NWKSKEY;
 	uint8_t AppSKey[16] = _APPSKEY;
 	uint8_t DevAddr[4]  = _DEVADDR;
 	
 	// Init the other LoraUp fields
+	
+	struct LoraUp LUP;
+	
 	LUP.sf = 8;											// Send with SF8
 	LUP.prssi = -50;
 	LUP.rssicorr = 139;
@@ -541,7 +543,7 @@ int sensorPacket() {
 	// FPort, either 0 or 1 bytes. Must be != 0 for non MAC messages such as user payload
 	//
 	LUP.payLoad[8] = 0x01;								// FPort must not be 0
-	LUP.payLength  = 9;
+	LUP.size  = 9;
 	
 	// FRMPayload; Payload will be AES128 encoded using AppSKey
 	// See LoRa spec para 4.3.2
@@ -549,7 +551,7 @@ int sensorPacket() {
 	//
 	
 	// Payload bytes in this example are encoded in the LoRaCode(c) format
-	uint8_t PayLength = LoRaSensors((uint8_t *)(LUP.payLoad + LUP.payLength));
+	uint8_t PayLength = LoRaSensors((uint8_t *)(LUP.payLoad + LUP.size));
 
 #if _DUSB>=1
 	if ((debug>=2) && (pdebug & P_RADIO)) {
@@ -564,7 +566,7 @@ int sensorPacket() {
 #endif	//_DUSB
 	
 	// we have to include the AES functions at this stage in order to generate LoRa Payload.
-	uint8_t CodeLength = encodePacket((uint8_t *)(LUP.payLoad + LUP.payLength), PayLength, (uint16_t)frameCount, DevAddr, AppSKey, 0);
+	uint8_t CodeLength = encodePacket((uint8_t *)(LUP.payLoad + LUP.size), PayLength, (uint16_t)frameCount, DevAddr, AppSKey, 0);
 
 #if _DUSB>=1
 	if ((debug>=2) && (pdebug & P_RADIO)) {
@@ -577,7 +579,7 @@ int sensorPacket() {
 	}
 #endif //_DUSB
 
-	LUP.payLength += CodeLength;								// length inclusive sensor data
+	LUP.size += CodeLength;								// length inclusive sensor data
 	
 	// MIC, Message Integrity Code
 	// As MIC is used by TTN (and others) we have to make sure that
@@ -585,12 +587,12 @@ int sensorPacket() {
 	// Note: Until MIC is done correctly, TTN does not receive these messages
 	//		 The last 4 bytes are MIC bytes.
 	//
-	LUP.payLength += micPacket((uint8_t *)(LUP.payLoad), LUP.payLength, (uint16_t)frameCount, NwkSKey, 0);
+	LUP.size += micPacket((uint8_t *)(LUP.payLoad), LUP.size, (uint16_t)frameCount, NwkSKey, 0);
 
 #if _DUSB>=1
 	if ((debug>=2) && (pdebug & P_RADIO)) {
 		Serial.print(F("mic: "));
-		for (int i=0; i<LUP.payLength; i++) {
+		for (int i=0; i<LUP.size; i++) {
 			Serial.print(LUP.payLoad[i],HEX);
 			Serial.print(' ');
 		}

+ 13 - 14
src/_stateMachine.ino

@@ -526,7 +526,7 @@ void stateMachine()
 		}
 		
 		// else we do not recognize the interrupt. We print an error
-		// and restart scanning. If hop we start at gwayConfig.ch==1
+		// and restart scanning. If hop we start at gwayConfig.ch==0
 		//
 		else {
 #			if _MONITOR>=1
@@ -567,7 +567,7 @@ void stateMachine()
 			if (intr & IRQ_LORA_CRCERR_MASK) {
 #				if _MONITOR>=1
 				if ((debug>=0) && (pdebug & P_RX)) {
-					String response = "UP CRC ERROR:: ";
+					String response = "^ CRC ERROR:: ";
 					mStat(intr, response);
 				}
 #				endif //_MONITOR
@@ -612,11 +612,10 @@ void stateMachine()
 			// - break
 			// NOTE: receivePacket also increases .ok0 - .ok2 counter
 			
-			if((LoraUp.payLength = receivePkt(LoraUp.payLoad)) <= 0) {
+			if((LoraUp.size = receivePkt(LoraUp.payLoad)) <= 0) {
 #				if _MONITOR>=1
 				if ((debug>=0) && (pdebug & P_RX)) {
-					String response = "sMachine:: ERROR S-RX: payLength=";
-					response += String(LoraUp.payLength);
+					String response = "sMachine:: ERROR S-RX: size=" + String(LoraUp.size);
 					mPrint(response);
 				}
 #				endif //_MONITOR
@@ -797,21 +796,21 @@ void stateMachine()
 		}
 
 #		if _MONITOR>=1
-		if ((debug>=1) && (pdebug & P_MAIN)) {
-			mPrint("TX stateMachine:: calling loraWait");
-		}
+		//if ((debug>=1) && (pdebug & P_MAIN)) {
+		//	mPrint("TX stateMachine:: calling loraWait");
+		//}
 #		endif
 
-		loraWait(&LoraDown);
+		//loraWait(&LoraDown);
 	
 		// Set state to transmit
 		// Clear interrupt flags and masks
-		writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
-		writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);				// reset interrupt flags
+		//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)
+	  	// Initiate the transmission of the buffer (in User space)
 		// We react on ALL interrupts if we are in TX state.
-		txLoraModem(&LoraDown);
+		//txLoraModem(&LoraDown);
 		
 		// After filling the buffer we only react on TXDONE interrupt
 		// So, more or less start at the "case TXDONE:" below 
@@ -842,7 +841,7 @@ void stateMachine()
 
 #			if _MONITOR>=1
 			if ((debug>=1) && (pdebug & P_TX)) {
-				String response =  "Dwn stateMachine: TXDONE OK: rcvd=";
+				String response =  "v OK, stateMachine TXDONE: rcvd=";
 				printInt(micros(),response);
 				if (micros() < LoraDown.tmst) {
 					response += ", diff=- " ;

+ 1 - 1
src/_tcpTTN.ino

@@ -42,7 +42,7 @@
 // int sendTtn(IPAddress server, int port, uint8_t *msg, int length)
 // bool connectTtn()
 // void pullData()
-// void sendstat();
+// void sendStat();
 
 // Add gateway code of functions here
 

+ 156 - 105
src/_txRx.ino

@@ -19,7 +19,7 @@
 
 
 // ------------------------------- DOWN ----------------------------------------
-//
+// SENDPACKET()
 // Prepare DOWN a LoRa packet in down buffer. 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,40 +29,56 @@
 // 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 (Node start reading).
+//
+// Parameters:
+//	char * buf			Total buffer including first 4 bytes
+//	uint8_t len;		Length of the buf unsigned 0-255 bytes, including first four bytes
+// Return:
+//	Int = 1 when success
 // ----------------------------------------------------------------------------
-int sendPacket(uint8_t *buf, uint8_t length) 
+int sendPacket(uint8_t *buf, uint8_t len) 
 {
-	// Received package with Meta Data (for example):
+	// Received from server with Meta Data (for example):
+	//
 	// codr	: "4/5"
 	// data	: "Kuc5CSwJ7/a5JgPHrP29X9K6kf/Vs5kU6g=="	// for example
-	// freq	: 868.1 									// 868100000 default
+	// size	: 21
+	// freq	: 868.1 									// 868.100000 MHz default
 	// ipol	: true/false
 	// modu : "LORA"
-	// powe	: 14										// Set by default
+	// powe	: 15										// Set by default
 	// rfch : 0											// Set by default
-	// size	: 21
 	// tmst : 1800642 									// for example
 	// datr	: "SF7BW125"
-	// imme :											// Immediately transfer
-	
+	// imme : false										// Immediately transfer
+	// fdev	: FSK frequency deviation (unsigned integer, in Hz) // NOT USED
+	// prea	:
+	// ncrc	:
+	// time Y: Mandatory time
+
 	// 12-byte header;
 	//		HDR (1 byte)
 	//		
-	// Data Reply for JOIN_ACCEPT as sent by server:
+	// JOIN_ACCEPT: Data Reply sent by server:
 	//		AppNonce (3 byte)
 	//		NetID (3 byte)
 	//		DevAddr (4 byte) [ 31..25]:NwkID , [24..0]:NwkAddr
  	//		DLSettings (1 byte)
 	//		RxDelay (1 byte)
 	//		CFList (fill to 16 bytes)
-			
-	int i=0;
-	char * bufPtr = (char *) (buf);
-	buf[length] = 0;
+	//
+	// PULL_RESP: Data sent by server to node
+	//		Byte 0:	Protocol Version (==PROTOCOL_VERSION)
+	//		Byte 1-2:	Token (LSB first).
+	//		Byte 3:		Command code
 	
+	int i=0;
+	char * bufPtr = (char *) (buf+4);							// Correct for first 4 bytes sent to function
+	bufPtr[len-4] = 0;											// Correct for the full string sent to function
+
 #	if _MONITOR>=1
-	if (( debug>=1) && (pdebug & P_TX)) {
-		mPrint("Dwn sendPacket:: " + String((char *)buf));
+	if ((debug>=1) && (pdebug & P_TX)) {
+		mPrint("v sendPacket:: token=" + String(buf[2]*256+buf[1]) + ", " + String((char *)bufPtr));
 	}
 #	endif //_MONITOR
 
@@ -72,12 +88,12 @@ int sendPacket(uint8_t *buf, uint8_t length)
 	if (error) {
 #		if _MONITOR>=1
 		if ((debug>=0) && (pdebug & P_TX)) {
-			mPrint("Dwn sendPacket:: ERROR: Json Decode: " + String(bufPtr) );
+			mPrint("v sendPacket:: ERROR: Json Decode: " + String(bufPtr) );
 		}
 #		endif //_MONITOR
 		return(-1);
 	}
-	
+
 	// 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"}}
 
@@ -88,43 +104,56 @@ int sendPacket(uint8_t *buf, uint8_t length)
 	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"];
 
 	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"
-	if (root["txpk"].containsKey("imme") ) {
-		LoraDown.imme = root["txpk"]["imme"];			// Immediate Transmit (tmst don't care)
-	}
-	if (root["txpk"].containsKey("powe") ) {
-		uint8_t powe	= root["txpk"]["powe"];			// power, e.g. 14 or 27
-	}
+	const char * codr	= root["txpk"]["codr"];			// "4/5"
+	const char * modu	= root["txpk"]["modu"];
+	const char * time	= root["txpk"]["time"];			// Time is a string in UTC
+	
+	//LoraDown.modu		= modu;
+	LoraDown.modu		= (char *) modu;				// =="LORA"
+	LoraDown.codr		= (char *) codr;				// e.g. "4/5"
+	
+	LoraDown.ipol		= root["txpk"]["ipol"];			// =0x01 for downlink
+	LoraDown.imme 		= root["txpk"]["imme"];			// Immediate Transmit (tmst don't care)
+	LoraDown.powe		= root["txpk"]["powe"];			// power, e.g. 14 or 27
+	LoraDown.prea		= root["txpk"]["prea"];
+	LoraDown.ncrc		= root["txpk"]["ncrc"];			// Cancel CRC for outgoing packets
+	LoraDown.rfch		= root["txpk"]["rfch"];			// ==0X00 first antenna
+	LoraDown.iiq 		= (LoraDown.ipol==true ? 0x40: 0x27);		// if ipol==true 0x40 else 0x27
+	LoraDown.crc		= 0x00;							// switch CRC off for TX
+
+	LoraDown.sf 		= atoi(datr+2);					// Convert "SF9BW125" or what is received from gateway to number
+	int j; for (j=3; *(datr+j)!='W'; j++); 
+	LoraDown.bw 		= atoi(datr+j+1);
+	
+	LoraDown.size		= base64_dec_len((char *) data, strlen(data));	// Length of the Payload data	
+	base64_decode((char *) payLoad, (char *) data, strlen(data));	// Fill payload w decoded message
+	LoraDown.payLoad	= payLoad;
 
-	if ( data != NULL ) {
+//	MMM Print some text
+	LoraDown.prea 		= 8;									// Change one time
+	mPrint("time="+String(time)+", ipol="+String(LoraDown.ipol)+", codr="+String(codr)+", prea="+String(LoraDown.prea) );
+	
+	// Compute wait time in microseconds
+	//int32_t w = (int32_t) (LoraDown.tmst - micros());	// Wait Time compute
+
+	if (data != NULL) {
 #		if _MONITOR>=1
-		if ((debug>=2) && (pdebug & P_TX)) { 
-			mPrint("sendPacket:: data=" + String(data)); 
+		if ((debug>=1) && (pdebug & P_TX)) { 
+			mPrint("v sendPacket:: LoraDown.size="+String(LoraDown.size)+", psize="+String(psize)+", strlen(data)="+String(strlen(data))+", data=" + String(data)); 
 		}
 #		endif //_MONITOR
 	}
 	else {												// There is data!
 #		if _MONITOR>=1
 		if ((debug>=0) && (pdebug & P_TX)) {
-			mPrint("sendPacket:: ERROR: data is NULL");
+			mPrint("v sendPacket:: ERROR: data is NULL");
 		}
 #		endif //_MONITOR
 		return(-1);
 	}
 
-	LoraDown.sfTx = atoi(datr+2);						// Convert "SF9BW125" or what is received from gateway to number
-	LoraDown.iiq = (ipol? 0x40: 0x27);					// if ipol==true 0x40 else 0x27
-	LoraDown.crc = 0x00;								// switch CRC off for TX
-	LoraDown.payLength = base64_dec_len((char *) data, strlen(data));// Length of the Payload data	
-	base64_decode((char *) payLoad, (char *) data, strlen(data));	// Fill payload w decoded message
-
-	// Compute wait time in microseconds
-	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.
@@ -135,10 +164,17 @@ int sendPacket(uint8_t *buf, uint8_t length)
 	// 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 (868.5 MHz)
+	// Do not use RX2 or JOIN2 as they contain other frequencies (aka 868.5 MHz)
+
+	if (LoraDown.powe>15) LoraDown.powe=15;				// On all freqs except 869.5MHz power is limited
+	LoraDown.freq	= ((double)freqs[gwayConfig.ch].dwnFreq)/1000000;		// Use the requestor Down frequency (always)
 
-	LoraDown.powe	= 14;								// On all freqs except 869.5MHz power is limited
-	LoraDown.freq	= freqs[gwayConfig.ch].dwnFreq;		// Use the requestor Down frequency (always)
+#elif _STRICT_1CH == 2
+// Semi:: We transmit only on ONE channel but the sensor end-node listens to
+// the special channel too.
+//	So, not change to SF (arranged by server)or frequency, as long as it is CH.
+
+	LoraDown.freq = (double)root["txpk"]["freq"];
 
 #else
 // elif _STRICT_1CH == 0, we will receive messags from the TTN gateway presumably on SF9/869.5MHz
@@ -148,35 +184,29 @@ int sendPacket(uint8_t *buf, uint8_t length)
 // than for gateways.
 // We will probably answer in RX with RF==12 and use special answer frequency
 //
-	LoraDown.powe	= root["txpk"]["powe"];
+	LoraDown.powe	= root["txpk"]["powe"];				// The server determines the power
 	const float ff	= root["txpk"]["freq"];				// eg 869.525
 	// convert double frequency (MHz) into uint32_t frequency in Hz.
-	LoraDown.freq = (uint32_t) ((uint32_t)((ff+0.000035)*1000)) * 1000;
+	LoraDown.freq = (uint32_t) ((uint32_t)((ff+0.000035)*1000)) * 1000;		// MMM Not correct
 
 #endif //_STRICT_1CH
 
-	yield();
-	
-	LoraDown.payLoad = payLoad;				
+	yield();			
 
-#	if _MONITOR>=1
-	if ((debug>=2) && (pdebug & P_TX)) {
-		mPrint("Dwn sendPacket:: TX tmst=" + String(LoraDown.tmst));
-	}
-#	endif //_MONITOR
-
-	if (LoraDown.payLength != psize) {
+	if (LoraDown.size != psize) {
 #		if _MONITOR>=1
 		if (debug>=0) {
-			mPrint("Dwn sendPacket:: WARNING payLength=" + String(LoraDown.payLength) + ", psize=" + String(psize) );
+			mPrint("v sendPacket:: WARNING size=" + String(LoraDown.size) + ", psize=" + String(psize) );
 		}
 #		endif //_MONITOR
 	}
+
 #	if _MONITOR>=1
-	else if ((debug >= 2) && (pdebug & P_TX)) {
+
+	else if ((debug >= 2) && (pdebug & P_TX)) {	
 		Serial.print(F("T Payload="));
-		for (i=0; i<LoraDown.payLength; i++) {
-			Serial.print(payLoad[i],HEX); 
+		for (i=0; i<LoraDown.size; i++) {
+			Serial.print(LoraDown.payLoad[i],HEX); 
 			Serial.print(':'); 
 		}
 		Serial.println();
@@ -196,21 +226,24 @@ int sendPacket(uint8_t *buf, uint8_t length)
 	_state = S_TX;										// _state set to transmit
 
 	return 1;
+	
 } //sendPacket DOWN
 
 
 
 
 // --------------------------------- UP ---------------------------------------
-//
+// buildPacket()
 // 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).
 //
+// buildPacket() will send PUSH_DATA to the server.
+//
 // parameters:
-// 	tmst: Timestamp to include in the upstream message
-// 	buff_up: The buffer that is generated for upstream
-//	LoraUP: Structure describing the message received from device
-// 	internal: Boolean value to indicate whether the local sensor is processed
+// 	tmst:		Timestamp to include in the upstream message
+// 	buff_up:	The buffer that is generated for upstream for the server
+//	LoraUP:		ptr to Structure describing the message received from device
+// 	internal:	Boolean value to indicate whether the local sensor is processed
 //
 // returns:
 //	buff_index:
@@ -226,7 +259,7 @@ int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal)
 	char b64[256];
 	
 	uint8_t *message = LoraUp->payLoad;
-	char messageLength = LoraUp->payLength;
+	char messageLength = LoraUp->size;
 		
 #if _CHECK_MIC==1
 	unsigned char NwkSKey[16] = _NWKSKEY;
@@ -261,7 +294,7 @@ int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal)
 
 		uint16_t frameCount=LoraUp->payLoad[7]*256 + LoraUp->payLoad[6];
 		
-		for (int k=0; (k<LoraUp->payLength) && (k<23); k++) {
+		for (int k=0; (k<LoraUp->size) && (k<23); k++) {
 			statr[0].data[k] = LoraUp->payLoad[k+9];
 		};
 		
@@ -275,7 +308,7 @@ int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal)
 		DevAddr[3]= LoraUp->payLoad[1];
 
 		statr[0].datal = encodePacket((uint8_t *)(statr[0].data), 
-								LoraUp->payLength -9 -4, 
+								LoraUp->size -9 -4, 
 								(uint16_t)frameCount, 
 								DevAddr, 
 								decodes[index].appKey, 
@@ -347,7 +380,7 @@ int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal)
 	}
 #endif //_STATISTICS >= 3
 
-#endif //_STATISTICS >= 2
+#endif //_STATISTICS >= 1
 
 #if _MONITOR>=1	
 	if ((debug>=2) && (pdebug & P_RADIO)) {
@@ -400,10 +433,9 @@ int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal)
     display.display();
 
 #endif //_OLED>=1
-			
-//	int j;
-	
-	// XXX Base64 library is nopad. So we may have to add padding characters until
+
+
+	// XXX Base64 library is no-pad. 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
@@ -416,17 +448,26 @@ int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal)
 #	endif //_MONITOR
 
 	base64_encode(b64, (char *) message, messageLength);// max 341
-	// start composing datagram with the header 
+	// start composing datagram with the header
+	
 	uint8_t token_h = (uint8_t)rand(); 					// random token
 	uint8_t token_l = (uint8_t)rand(); 					// random token
 	
 	// pre-fill the data buffer with fixed fields
+	// There are several types of messages that can be sent Up, but here PUSH_DATA is used:
+	//	PUSH_DATA,		0x00, Up,	Send data from gateway to server done in this function, random token
+	//	PUSH_ACK,		0x01, Down,	Ack to Gateway, use token of PUSH_DATA
+	//	PULL_DATA,		0x02, Up,	Send to server using a random token
+	//	PULL_ACK,		0x04, Down, Ack to Gateway use token of PULL_DATA
+	//	PULL_RESP,		0x03, Down, handled by sendPacket(), use token of last PULL_ACK
+	//	TX_ACK,			0x05, Up,	handled by sendPacket() in response when protocol>=2
+
+	// PUSH DATA, see para. 5.2.1 of Semtech Gateway to Server Interface document
+	// 	Mind that we only PUSH here
 	buff_up[0] = PROTOCOL_VERSION;						// 0x01 still
-
-	buff_up[1] = token_h;
-	buff_up[2] = token_l;
-	
-	buff_up[3] = PKT_PUSH_DATA;							// 0x00
+	buff_up[1] = (uint8_t)token_h;						// Arbitrary token
+	buff_up[2] = (uint8_t)token_l;
+	buff_up[3] = PUSH_DATA;								// 0x00
 	
 	// READ MAC ADDRESS OF ESP8266, and insert 0xFF 0xFF in the middle
 	buff_up[4]  = MAC_array[0];
@@ -441,6 +482,7 @@ int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal)
 	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;	// Does not work with TTN
 	memcpy((void *)(buff_up + buff_index), (void *)"{\"rxpk\":[{", 10); buff_index += 10;
 	
 // More versions are defined for the moment, in order to keep timing as low as [possible. 
@@ -452,15 +494,16 @@ int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal)
 	
 	//doc["time"] = ""+now();
 
-	doc["chan"] = "0";
-	doc["rfch"] = "0";
+	doc["chan"] = "" + gwayConfig.ch;				// This could be any defined channel
+	doc["rfch"] = "0";								// First Antenna
 	doc["freq"] = "" + (freqs[gwayConfig.ch].upFreq / 1000000);
-	doc["stat"] = "1";
+	doc["stat"] = "1";								// Always OK for CRC
 	doc["modu"] = "LORA";
 	doc["datr"] = "SF" + String(LoraUp->sf) + "BW" + String(freqs[gwayConfig.ch].upBW);
 	doc["rssi"] = "" +(prssi-rssicorr);
 	doc["lsnr"] = "" +(long)SNR;
-	doc["codr"] = "4/5";
+	doc["codr"] = "4/5";							// MMM needs to be dynamic
+	//doc["ipol"] = "false";						// For UP not needed
 
 	// Use gBase64 library to fill in the data string
 	encodedLen = base64_enc_len(messageLength);			// max 341	
@@ -468,30 +511,33 @@ int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal)
 
 	int len= base64_encode(doc["data"], (char *)message, messageLength);
 
-	LoraUp->tmst = doc["tmst"] = "" + (uint32_t) micros() + _RXDELAY1;		// Tmst correction							
-	
+	LoraUp->tmst = doc["tmst"] = "" + (uint32_t) micros() + _RXDELAY1;		// Tmst correction when necessary						
+
+	// Write string inclusing first 12 chars to the buffer
 	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, this is default
-// -----------------
+#else 
+//_JSONENCODE undefined, 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 */
+	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, 
+//		RX_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, 
+		RX_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
+
+	// MMM Make codr more dynamic, just like datr
+	buff_index += snprintf((char *)(buff_up + buff_index), RX_BUFF_SIZE-buff_index
 		, ",\"datr\":\"SF%uBW%u\",\"codr\":\"4/5\",\"lsnr\":%li,\"rssi\":%d,\"size\":%u,\"data\":\""
 		, LoraUp->sf, freqs[gwayConfig.ch].upBW, (long)SNR, prssi-rssicorr, messageLength);
 
@@ -506,19 +552,23 @@ int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal)
 	// Get rid of this code when ready	
 
 	buff_index += snprintf((char *)(buff_up + buff_index), 
-		TX_BUFF_SIZE-buff_index, "\",\"tmst\":%u", 
+		RX_BUFF_SIZE-buff_index, "\",\"tmst\":%u", 
 		LoraUp->tmst);	
 
 #endif //_JSONENCODE undefined or ==0
 // ---------------------
 
-
 	// End of packet serialization
-	buff_up[buff_index]   = '}'; 
-	buff_up[buff_index+1] = ']'; 
+
+	buff_up[buff_index]   = '}';
+	buff_up[buff_index+1] = ']';						// According to specs, this] can remove
 	buff_up[buff_index+2] = '}'; 
 	buff_index += 3;
 	
+	//buff_up[buff_index]   = '}';
+	//buff_up[buff_index+1] = '}'; 
+	//buff_index += 2;									// Decrease of we need no ]
+	
 	buff_up[buff_index] = 0; 							// add string terminator, for safety
 
 	// When we have the node address and the SF, fill the listSeen array
@@ -536,7 +586,7 @@ int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal)
 
 #	if _MONITOR>=1
 	if ((debug>=1) && (pdebug & P_RX)) {			// debug: display JSON payload
-		mPrint("UP RXPK:: "+String((char *)(buff_up + 12))+" , length="+String(buff_index));		
+		mPrint("^ PUSH_DATA:: token="+String(token_h*256+token_l)+", data="+String((char *)(buff_up + 12))+", Buff_up Length="+String(buff_index));		
 	}
 #	endif
 
@@ -563,7 +613,7 @@ int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal)
 // ----------------------------------------------------------------------------
 int receivePacket()
 {
-	uint8_t buff_up[TX_BUFF_SIZE]; 						// buffer to compose the upstream packet to backend server
+	uint8_t buff_up[RX_BUFF_SIZE]; 						// buffer to compose the upstream packet to backend server
 
 	// Regular message received, see SX1276 spec table 18
 	// Next statement could also be a "while" to combine several messages received
@@ -572,7 +622,7 @@ int receivePacket()
 
 
 		// Handle the physical data read from LoraUp
-		if (LoraUp.payLength > 0) {
+		if (LoraUp.size > 0) {
 
 #			ifdef _PROFILER
 				int32_t startTime = micros();
@@ -583,13 +633,14 @@ int receivePacket()
             int build_index = buildPacket(buff_up, &LoraUp, false);
 
 			// 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
+			// message on incoming channel and transmits to outgoing channel.
+			// Note:: For the moment incoming channel is not allowed to be same as outgoing channel.
+			//
 #			if _REPEATER==1
-			if (!sendLora(LoraUp.payLoad, LoraUp.payLength)) {
-				return(-3);
+			if (!repeatLora(&LoraUp)) {
+				return(-3);												// Return when message repeated
 			}
-#			endif
+#			endif //_REPEATER
 
 #			ifdef _TTNSERVER	
 			// This is one of the potential problem areas.
@@ -607,7 +658,7 @@ int receivePacket()
 #			ifdef _PROFILER
 			if ((debug>=1) && (pdebug & P_RX)) {
 				int32_t endTime = micros();
-				String response = "UP receivePacket:: end="; printInt(endTime,response);
+				String response = "^ receivePacket:: end="; printInt(endTime,response);
 				response += ", start="; printInt(startTime, response);
 				response += ", diff=" +String(endTime-startTime) + " uSec";
 				mPrint(response);
@@ -659,7 +710,7 @@ int receivePacket()
 					Serial.print(F("UP receivePacket:: Ind="));
 					Serial.print(index);
 					Serial.print(F(", Len="));
-					Serial.print(LoraUp.payLength);
+					Serial.print(LoraUp.size);
 					Serial.print(F(", A="));
 					for (int i=0; i<4; i++) {
 						if (DevAddr[i]<0x0F) Serial.print('0');
@@ -685,7 +736,7 @@ int receivePacket()
 #endif //_LOCALSERVER
 
 			// Reset the message area
-			LoraUp.payLength = 0;
+			LoraUp.size = 0;
 			LoraUp.payLoad[0] = 0x00;
 
 			return(build_index);

+ 163 - 130
src/_udpSemtech.ino

@@ -31,7 +31,7 @@
 // int sendUdp(IPAddress server, int port, uint8_t *msg, uint16_t length)
 // bool connectUdp();
 // void pullData();
-// void sendstat();
+// void sendStat();
 
 // ----------------------------------------------------------------------------
 // connectUdp()
@@ -75,7 +75,7 @@ bool connectUdp()
 // ----------------------------------- DOWN -----------------------------------
 //
 // readUdp()
-// Read DOWN a package from UDP socket, can come from any server
+// Read DOWN a package from UDP socket from server (it 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 sents us a message!
@@ -97,21 +97,21 @@ bool connectUdp()
 // ----------------------------------------------------------------------------
 int readUdp(int packetSize)
 { 
-	uint8_t buff[32]; 						// General buffer to use for UDP, set to 32
-	uint8_t buff_down[RX_BUFF_SIZE];		// Buffer for downstream
+	uint8_t buff[64]; 						// General buffer to use for UDP, set to 64 characters
+	uint8_t buff_down[TX_BUFF_SIZE];		// Buffer for downstream
 
 	// Make sure we are connected over WiFI
 	if (WlanConnect(10) < 0) {
 #		if _MONITOR>=1
-			mPrint("Dwn readUdp:: ERROR connecting to WLAN");
+			mPrint("v readUdp:: ERROR connecting to WLAN");
 #		endif //_MONITOR
 		Udp.flush();
 		return(-1);
 	}
 	
-	if (packetSize > RX_BUFF_SIZE) {
+	if (packetSize > TX_BUFF_SIZE) {
 #		if _MONITOR>=1
-			mPrint("Dwn readUdp:: ERROR package of size: " + String(packetSize));
+			mPrint("v readUdp:: ERROR package of size: " + String(packetSize));
 #		endif //_MONITOR
 		Udp.flush();
 		return(-1);
@@ -121,7 +121,7 @@ int readUdp(int packetSize)
 	// In practice however this can be any sender!
 	if (Udp.read(buff_down, packetSize) < packetSize) {
 #		if _MONITOR>=1
-			mPrint("Dwn readUdp:: Reading less chars");
+			mPrint("v readUdp:: Reading less chars");
 #		endif //_MONITOR
 		return(-1);
 	}
@@ -138,7 +138,7 @@ int readUdp(int packetSize)
 		// This is an NTP message arriving
 #		if _MONITOR>=1
 		if (debug>=0) {
-			mPrint("Dwn readUdp:: NTP msg rcvd");
+			mPrint("v readUdp:: NTP msg rcvd");
 		}
 #		endif //_MONITOR
 		gwayConfig.ntpErr++;
@@ -150,14 +150,16 @@ int readUdp(int packetSize)
 	// If it is not NTP it must be a LoRa message for gateway or node
 	
 	else {
+		// First 4 butes are very important, rest is data
+		// Especially the 2 token bytes should be watched.
 		uint8_t *data = (uint8_t *) ((uint8_t *)buff_down + 4);
-		//uint8_t protocol= buff_down[0];
-		//uint16_t token= buff_down[2]*256 + buff_down[1];
+		uint8_t protocol= buff_down[0];
+		uint16_t token= buff_down[2]*256 + buff_down[1];			// LSB first [1], MSB [2] comes after
 		uint8_t ident= buff_down[3];
 
 #		if _MONITOR>=1
 		if ((debug>=3) && (pdebug & P_TX)) {
-			mPrint("Dwn readUdp:: message ident="+String(ident));
+			mPrint("v readUdp:: message ident="+String(ident));
 		}
 #		endif //_MONITOR
 
@@ -166,17 +168,19 @@ int readUdp(int packetSize)
 
 
 		// 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
+		// As this function is used for downstream only, this option will never 
+		// be executed by this function but is included as a reference only
 		// Para 5.2.1, Semtech Gateway to Server Interface document
-		//	Byte 0:		Version
-		//	byte 1+2:	Token
-		//	byte 3: 	Command code (0x00)
+		//	Byte 0:		Protocol version (0x01 or 0x02)
+		//	byte 1+2:	Token, random
+		//	byte 3: 	Command code (=0x00)
+		//	Byte 4-11:	Gateway EUI
+		//	Byte 12-n:	JSON data
 		//
-		case PKT_PUSH_DATA: 							// 0x00 UP
+		case PUSH_DATA: 								// 0x00 UP
 #			if _MONITOR>=1
-			if (debug>=1) {
-				mPrint("Dwn PKT_PUSH_DATA:: size "+String(packetSize)+" From "+String(remoteIpNo.toString()));
+			if ((debug>=1) && (pdebug & P_RX)) {
+				mPrint("v PUSH_DATA:: size "+String(packetSize)+" From "+String(remoteIpNo.toString()));
 			}
 #			endif //_MONITOR
 			Udp.flush();
@@ -184,21 +188,23 @@ int readUdp(int packetSize)
 
 
 		// This message is sent DOWN by the server to acknowledge receipt of a
-		// (sensor) PKT_PUSH_DATA message sent with the code above.
+		// (sensor) PUSH_DATA message sent with the code above.
 		// Para 5.2.2, Semtech Gateway to Server Interface document
 		// The length of this package is 4 bytes:
 		//	byte 0:		Protol version (0x01 or 0x02)
 		//	byte 1+2:	Token copied from requestor
-		//	byte 3:		0x01, ack PKT_PUSH_ACK
+		//	byte 3:		ident = 0x01, ack PUSH_ACK
 		//
-		case PKT_PUSH_ACK:								// 0x01 DOWN
+		case PUSH_ACK:								// 0x01 DOWN
 #			if _MONITOR>=1
-			if ((debug>=2) && (pdebug & P_TX)) {
+			if ((debug>=1) && (pdebug & P_TX)) {
 				char res[128];				
-				sprintf(res, "Dwn PKT_PUSH_ACK:: size=%u, IP=%d.%d.%d.%d, port=%d ", 
+				sprintf(res, "v PUSH_ACK:: token=%u, size=%u, IP=%d.%d.%d.%d, port=%d, protocol=%u ", 
+					(buff_down[2]*256+buff_down[1]),
 					packetSize, 
 					remoteIpNo[0], remoteIpNo[1], remoteIpNo[2],remoteIpNo[3], 
-					remotePortNo);
+					remotePortNo,
+					protocol);
 				mPrint(res);
 			}
 #			endif //_MONITOR
@@ -206,51 +212,59 @@ int readUdp(int packetSize)
 		break;
 
 
-		// PULL DATA message
+		// PULL DATA message (Up)
 		// 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
+		// Byte 0		contains Protocol Version (0x01 or 0x02)
+		// Byte 1-2		Random Token
+		// Byte 3		PULL_DATA ident == 0x02
+		// Byte 4-11	Gateway EUI
+		//
+		case PULL_DATA:								// 0x02 UP
 #			if _MONITOR>=1
 			if ((debug>=1) && (pdebug & P_RX)) {
-				mPrint("Dwn PKT_PULL_DATA");
+				mPrint("v PULL_DATA");
 			}
 #			endif //_MONITOR
 			Udp.flush();								// MMM 200419 Added
 		break;
 
 
-		// PULL_ACK message
-		// This is the response to PKT_PULL_DATA message
+		// PULL_ACK message (Down)
+		// This is the (immediate!) response to 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 1-2		Token as issued from the gatway when requesting
 		// Byte 3		PULL_ACK ident == 0x04
+		// Byte 4-11:	Gateway EUI
 		//
-		case PKT_PULL_ACK:								// 0x04 DOWN
+		case PULL_ACK:									// 0x04 DOWN
 #			if _MONITOR>=1
-			if ((debug>=2) && (pdebug & P_TX)) {
+			if ((debug>=1) && (pdebug & P_TX)) {
 				char res[128];				
-				sprintf(res, "Dwn PKT_PULL_ACK:: size=%u, IP=%d.%d.%d.%d, port=%d ", 
+				sprintf(res, "v PULL_ACK:: token=%u, size=%u, IP=%d.%d.%d.%d, port=%d, protocol=%u ", 
+					(buff_down[2]*256+buff_down[1]),
 					packetSize, 
 					remoteIpNo[0], remoteIpNo[1], remoteIpNo[2],remoteIpNo[3], 
-					remotePortNo);
+					remotePortNo,
+					protocol);
 				mPrint(res);
 			}
 #			endif //_MONITOR
 			
-			yield();			
-			
-			Udp.flush();								// MMM 200419 
+			yield();				
+			Udp.flush();								// MMM 200419
+			// No response is needed
 		break;
 
 
-
+		// PULL_RESP (Down)
 		// 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:
@@ -258,29 +272,51 @@ int readUdp(int packetSize)
 		//	RECEIVE_DELAY2		2 s (is RECEIVE_DELAY1+1)
 		//	JOIN_ACCEPT_DELAY1	5 s
 		//	JOIN_ACCEPT_DELAY2	6 s
+		//
+		// buff_down[0]:		Version number (==PROTOCOL_VERSION)
+		// buff_down[1-2]:		Token: If Protocol version==0, make 0. If version==2 arbitrary?
+		// buff_down[3]:		PULL_RESP: ident = 0x03
+		// buff_down[4-n]:		payLoad data
+		//
 		// Para 5.2.5, Semtech Gateway to Server Interface document
 		// or https://github.com/Lora-net/packet_forwarder/blob/master/PROTOCOL.TXT
 		//
-		case PKT_PULL_RESP:								// 0x03 DOWN
+		case PULL_RESP:									// 0x03 DOWN
 
+			if (protocol==0x01) {						// If protocol version is 0x01
+				token = 0;								// Use token 0 in that case
+				buff_down[2]=0;
+				buff_down[1]=0;
+			}
+			
 			// Define when we start with the response to node
 #			ifdef _PROFILER
-			if ((debug>=2) && (pdebug & P_TX)) {
-				mPrint("Dwn PKT_PULL_RESP:: start sendPacket: micros="+String(micros() ));
+			if ((debug>=1) && (pdebug & P_TX)) {
+				mPrint("v PULL_RESP:: start sendPacket: micros="+String(micros() ));
+				char res[128];				
+				sprintf(res, "v PULL_RESP:: token=%u, size=%u, IP=%d.%d.%d.%d, port=%d, protocol=%u, micros=%lu", 
+					token,
+					packetSize, 
+					remoteIpNo[0], remoteIpNo[1], remoteIpNo[2],remoteIpNo[3], 
+					remotePortNo,
+					protocol,
+					(unsigned long) micros() 
+				);
+				mPrint(res);
 			}
-#			endif //_PROFILER
+#			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
-
+// XXX
 			// 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) {
+			// as described in the specs. This function fills LoraDown struct.
+			if (sendPacket(buff_down, packetSize) < 0) {
 #				if _MONITOR>=1
 				if (debug>=0) {
-					mPrint("Dwn readUdp:: ERROR: PKT_PULL_RESP sendPacket failed");
+					mPrint("v readUdp:: ERROR: PULL_RESP sendPacket failed");
 				}
 #				endif //_MONITOR
 				Udp.flush();
@@ -288,20 +324,22 @@ int readUdp(int packetSize)
 			}
 
 
-			// 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)
+			// We need a timeout for this case. During transmission we should not accept
+			// another package receiving/sending
 
-			loraWait(&LoraDown);
+			if (loraWait(&LoraDown) == 0) {
+				_state=S_CAD;							// Maybe call TXDONE and wait 1 sec
+				_event=1;
+				break;
+			}
 
-			// 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);
+			txLoraModem(&LoraDown);										// Calling sendPkt() in turn
 
 #			if _MONITOR>=1
 			if ((debug>=2) && (pdebug & P_TX)) {
@@ -310,7 +348,7 @@ int readUdp(int packetSize)
 				uint8_t intr  = flags & ( ~ mask );
 
 
-				String response = "Dwn readUdp:: PKT_PULL_RESP from IP="+String(remoteIpNo.toString())
+				String response = "v readUdp:: PULL_RESP from IP="+String(remoteIpNo.toString())
 					+", micros=" + String(micros())
 					+", wait=";
 				if (sendTime < micros()) {
@@ -337,75 +375,69 @@ int readUdp(int packetSize)
 
 			// No break!! so next secton will be executed
 
-#		ifdef _PROFILER
-		// measure the total time for transmissioon here
+#			ifdef _PROFILER
+			// measure the total time for transmissioon here
 			if ((debug>=2) && (pdebug & P_TX)) {
-				mPrint("Dwn PKT_PULL_RESP:: finit: micros="+String(micros() ));
+				mPrint("v PULL_RESP:: finit: micros="+String(micros() ));
 			}
-#		endif //_PROFILER
+#			endif //_PROFILER
+
 
-		// This is the response to the PKT_PULL_RESP message by the sensor device
+		// TX_ACK (Up)
+		// This is the response to the PULL_RESP message by the sensor device (above)
 		// it is sent by the gateway UP to the server to confirm the PULL_RESP message.
-		//	byte 0:		Protocol version
-		//	byte 1+2:	Port number of originator
-		//	byte 3:		Message ID TX_ACK == 0x06
-		//	byte 4-11:	Gateway ident
-		//	byte 12-:	Optional Error Data
+		//	byte 0:		Protocol version (0x01 or 0x02)
+		//	byte 1+2:	Token number of UP sender
+		//	byte 3:		Message ID TX_ACK == 0x05
+		//	byte 4-n:	Optional Error Data, {"errno":xxxx}
 		//
-		case PKT_TX_ACK:									// Message id: 0x05 UP
+		case TX_ACK:									// Message id: 0x05 UP
 
-			if (buff_down[0]== 1) {
+			if (protocol == 1) {							// Got from the downstream message
 #				if _MONITOR>=1
-				if ((debug>=3) && (pdebug & P_TX)) {
-					mPrint("UP readUdp:: PKT_TX_ACK: protocol version 1");
-					
-					data = buff_down + 4;
-					data[packetSize] = 0;
+				if ((debug>=1) && (pdebug & P_TX)) {
+					mPrint("^ TX_ACK:: readUdp: protocol version 1");
+					//data = buff_down + 4;
+					//data[packetSize-4] = 0;
 				}
 #				endif
 				break;										// return
 			}
 
 #			if _MONITOR>=1
-			if ((debug>=2) && (pdebug & P_TX)) {
-				mPrint("UP readUDP:: TX_ACK protocol version 2+");
+			if ((debug>=1) && (pdebug & P_TX)) {
+				mPrint("^ TX_ACK:: readUDP: protocol version 2+");
 			}
 #			endif //_MONITOR
 
 
-			// Now respond with an PKT_TX_ACK; UP 
+			// UP: Now respond with an TX_ACK
 			// 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[1]= buff_down[1];							// Token 1, copied from downstream
 			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"
+			buff[3]= TX_ACK;								// ident == 0x05;
+			buff[4]= 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!!!
+			// Only send the PULL_ACK to the UDP socket that just sent the data!!!
 			Udp.beginPacket(remoteIpNo, remotePortNo);
 			
+			// XXX We should format the message before sending up with UDP
+			
 			if (Udp.write((unsigned char *)buff, 12) != 12) {
 #				if _MONITOR>=1
 				if (debug>=0) {
-					mPrint("UP readUdp:: ERROR: PKT_PULL_ACK write");
+					mPrint("^ readUdp:: ERROR: PULL_ACK write");
 				}
 #				endif //_MONITOR
 			}
 			else {
 #				if _MONITOR>=1
 				if ((debug>=2) && (pdebug & P_TX)) {
-					mPrint("UP readUdp:: PKT_TX_ACK: micros="+String(micros()));
+					mPrint("^ readUdp:: TX_ACK: micros="+String(micros()));
 				}
 #				endif //_MONITOR
 			}
@@ -413,19 +445,19 @@ int readUdp(int packetSize)
 			if (!Udp.endPacket()) {
 #				if _MONITOR>=1
 				if ((debug>=0) && (pdebug & P_TX)) {
-					mPrint("UP readUdp:: PKT_PULL_DATALL: ERROR Udp.endPacket");
+					mPrint("^ readUdp:: PULL_ACK: ERROR Udp.endPacket");
 				}
 #				endif //_MONITOR
 			}
 			
 			yield();
 
-			// ONLY NOW WE START TO MONITOR THE PKT_PULL_RESP MESSAGE.
+			// ONLY NOW WE START TO MONITOR THE PULL_RESP MESSAGE.
 #			if _MONITOR>=1
 			if ((debug>=1) && (pdebug & P_TX)) {
 				data = buff_down + 4;
-				data[packetSize] = 0;
-				mPrint("Dwn readUdp:: PKT_PULL_RESP: size="+String(packetSize)+", data="+String((char *)data)); 
+				data[packetSize-4] = 0;
+				mPrint("v readUdp:: PULL_RESP: size="+String(packetSize)+", data="+String((char *)data)); 
 			}
 #			endif //_MONITOR
 
@@ -435,8 +467,6 @@ int readUdp(int packetSize)
 #			if _GATEWAYMGT==1
 				// For simplicity, we send the first 4 bytes too
 				gateway_mgt(packetSize, buff_down);
-			else
-
 #			endif
 #			if _MONITOR>=1
 				mPrint(", ERROR ident not recognized="+String(ident));
@@ -446,10 +476,10 @@ int readUdp(int packetSize)
 		
 #		if _MONITOR>=1
 		if ((debug>=3) && (pdebug & P_TX)) {
-			String response= "Dwn readUdp:: ident="+String(ident,HEX);
+			String response= "v readUdp:: ident="+String(ident,HEX);
 			response+= ", tmst=" + String(LoraDown.tmst);
 			response+= ", imme=" + String(LoraDown.imme);
-			response+= ", sfTx=" + String(LoraDown.sfTx);
+			response+= ", sf=" + String(LoraDown.sf);
 			response+= ", freq=" + String(LoraDown.freq);
 			if (debug>=3) {
 				if (packetSize > 4) {
@@ -465,12 +495,12 @@ int readUdp(int packetSize)
 		// For downstream messages
 		return packetSize;
 	}
-}//readUdp
+} //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 
 // before sending.
@@ -543,27 +573,30 @@ int sendUdp(IPAddress server, int port, uint8_t *msg, uint16_t length)
 
 // --------------------------------- UP ---------------------------------------
 // pullData()
+// Sending a PULL_DATA request to the server
 // 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)
-//	- Random Token (2 bytes)
-//	- PULL_DATA identifier (1 byte) = 0x02
-//	- Gateway unique identifier (8 bytes) = MAC address
+// *2, par. 5.2.3
+//
+//	Byte 0:		Protocol Version (1 byte)
+//	Byte 1-2:	Random Token (2 bytes)
+//	Byte 3:		PULL_DATA identifier (1 byte) = 0x02
+//	Byte 4-11:	8 bytes Gateway unique identifier (8 bytes) = MAC address
 // ----------------------------------------------------------------------------
 void pullData()
 {
-    uint8_t pullDataReq[12]; 								// status report as a JSON object
-    int pullIndex=0;
+    uint8_t pullDataReq[13]; 								// status report as a JSON object
+    int pullIndex	=0;
 	
 	uint8_t token_h = (uint8_t)rand(); 						// random token
     uint8_t token_l = (uint8_t)rand();						// random token
 	
     // pre-fill the data buffer with fixed fields
     pullDataReq[0]  = PROTOCOL_VERSION;						// 0x01
-    pullDataReq[1]  = token_h;
-    pullDataReq[2]  = token_l;
-    pullDataReq[3]  = PKT_PULL_DATA;						// 0x02
+    pullDataReq[1]  = token_l;								// random
+    pullDataReq[2]  = token_h;								// random
+    pullDataReq[3]  = PULL_DATA;							// 0x02
+	
 	// READ MAC ADDRESS OF ESP8266, and return unique Gateway ID consisting of MAC address and 2bytes 0xFF
     pullDataReq[4]  = MAC_array[0];
     pullDataReq[5]  = MAC_array[1];
@@ -573,33 +606,33 @@ void pullData()
     pullDataReq[9]  = MAC_array[3];
     pullDataReq[10] = MAC_array[4];
     pullDataReq[11] = MAC_array[5];
-    //pullDataReq[12] = 0/00; 								// add string terminator, for safety
+	
+    pullDataReq[12] = 0; 									// add string terminator, for safety
 	
     pullIndex = 12;											// 12-byte header
+
+	uint8_t *pullPtr = pullDataReq;
 	
     //send the update
-	
-	uint8_t *pullPtr;
-	pullPtr = pullDataReq,
-#ifdef _TTNSERVER
-    sendUdp(ttnServer, _TTNPORT, pullDataReq, pullIndex);
-	yield();
-#endif
+#	ifdef _TTNSERVER
+		sendUdp(ttnServer, _TTNPORT, pullDataReq, pullIndex);
+		yield();
+#	endif //_TTNSERVER
+
+#	ifdef _THINGSERVER
+		sendUdp(thingServer, _THINGPORT, pullDataReq, pullIndex);
+#	endif
+
 
 #	if _MONITOR>=1
 	if (pullPtr != pullDataReq) {
 		mPrint("pullPtr != pullDatReq");
 	}
-#	endif //_MONITOR
-
-#ifdef _THINGSERVER
-	sendUdp(thingServer, _THINGPORT, pullDataReq, pullIndex);
-#endif
 
-#if _MONITOR>=1
-    if ((debug>=2) && (pdebug & P_MAIN)) {
+    if ((debug>=1) && (pdebug & P_RX)) {
 		yield();
-		mPrint("M PKT_PULL_DATA request, len=" + String(pullIndex) );
+		mPrint("^ PULL_DATA:: token=" +String(token_h*256+token_l) +", len=" + String(pullIndex) );
+		Serial.print("v Gateway EUI=");
 		for (int i=0; i<pullIndex; i++) {
 			Serial.print(pullDataReq[i],HEX);				// debug: display JSON stat
 			Serial.print(':');
@@ -614,13 +647,13 @@ void pullData()
 
 
 // ---------------------------------- UP --------------------------------------
-// sendstat()
+// sendStat()
 // Send UP periodic status message to server even when we do not receive any
 // data. 
 // Parameters:
 //	- <none>
 // ----------------------------------------------------------------------------
-void sendstat()
+void sendStat()
 {
 
     uint8_t status_report[STATUS_SIZE]; 					// status report as a JSON object
@@ -636,7 +669,7 @@ void sendstat()
     status_report[0]  = PROTOCOL_VERSION;					// 0x01
 	status_report[1]  = token_h;
     status_report[2]  = token_l;
-    status_report[3]  = PKT_PUSH_DATA;						// 0x00
+    status_report[3]  = PUSH_DATA;							// 0x00
 	
 	// READ MAC ADDRESS OF ESP8266, and return unique Gateway ID consisting of MAC address and 2bytes 0xFF
     status_report[4]  = MAC_array[0];
@@ -677,7 +710,7 @@ void sendstat()
 
 	if (stat_index > STATUS_SIZE) {
 #		if _MONITOR>=1
-			mPrint("sendstat:: ERROR buffer too big");
+			mPrint("sendStat:: ERROR buffer too big");
 #		endif //_MONITOR
 		return;
 	}
@@ -693,7 +726,7 @@ void sendstat()
 #	endif
 	return;
 
-} // sendstat()
+} // sendStat()
 
 
 #endif //_UDPROUTER

+ 27 - 6
src/_utils.ino

@@ -25,8 +25,8 @@
 // The function printInt prints a number with Thousands seperator
 // Paraneters:
 //	i:			Integer containing Microseconds
-//	response:	String & value containig the converted number
-// Retur:
+//	response:	String & value containing the converted number
+// Return:
 //	<none>
 // --------------------------------------------------------------------------------
 void printInt (uint32_t i, String & response)
@@ -35,9 +35,13 @@ void printInt (uint32_t i, String & response)
 }
 
 // --------------------------------------------------------------------------------
-// PRINT Dwn
-// IN a uniform way, this function prints the timstamp, the current time and the 
+// PRINT Down
+// 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
+// Parameters:
+//
+// Returns:
+//	<none>
 // --------------------------------------------------------------------------------
 void printDwn(struct LoraDown *LoraDown, String & response)
 {
@@ -57,8 +61,25 @@ void printDwn(struct LoraDown *LoraDown, String & response)
 		response += ")";
 	}
 
-	response += ", SF="		+String(LoraDown->sfTx);
-	response += ", Freq="	+String(LoraDown->freq);
+	//inval	=int(LoraDown->freq);
+	//response += ", freq="	+String(intval) +".";
+	//fraqval	=int((LoraDown->freq-intval)*10000);
+	//response += String(fraqval);
+
+	char cfreq[12] = {0};
+	ftoa(LoraDown->freq, cfreq, 3);
+	response += ", freq="	+String(cfreq);
+	response += ", sf="		+String(LoraDown->sf);
+	response += ", bw="		+String(LoraDown->bw);
+	response += ", powe="	+String(LoraDown->powe);
+	response += ", crc="	+String(LoraDown->crc);
+	response += ", imme="	+String(LoraDown->imme);
+	response += ", iiq="	+String(LoraDown->iiq, HEX);
+	response += ", prea="	+String(LoraDown->prea);
+	response += ", rfch="	+String(LoraDown->rfch);
+	response += ", ncrc="	+String(LoraDown->ncrc);
+	response += ", size="	+String(LoraDown->size);
+	response += ", strict="	+String(_STRICT_1CH);
 
 	response += ", a=";
 	uint8_t DevAddr [4];

+ 40 - 27
src/_wwwServer.ino

@@ -34,7 +34,7 @@
 
 
 //
-// The remainder of the file ONLY works is _SERVER=1 is set.
+// The remainder of the file ONLY works if _SERVER=1 is set.
 //
 #if _SERVER==1
 
@@ -450,7 +450,7 @@ static void openWebPage()
 
 	response += "<style>.thead {background-color:green; color:white;} ";
 	response += ".cell {border: 1px solid black;}";
-	response += ".config_table {max_width:100%; min-width:400px; width:98%; border:1px solid black; border-collapse:collapse;}";
+	response += ".config_table {max_width:100%; min-width:300px; width:98%; border:1px solid black; border-collapse:collapse;}";
 	response += "</style></HEAD><BODY>";
 	
 	response +="<h1>ESP Gateway Config</h1>";
@@ -488,20 +488,20 @@ static void openWebPage()
 // allowing the user to set CAD, HOP, Debug and several other operating parameters
 //
 // --------------------------------------------------------------------------------
-static void gatewaySettings() 
+static void gatewaySettings()
 {
 	String response="";
 	String bg="";
-	
+
 	response +="<h2>Gateway Settings</h2>";
-	
+
 	response +="<table class=\"config_table\">";
 	response +="<tr>";
 	response +="<th class=\"thead\">Setting</th>";
 	response +="<th colspan=\"2\" style=\"background-color: green; color: white; width:120px;\">Value</th>";
 	response +="<th colspan=\"2\" style=\"background-color: green; color: white; width:100px;\">Set</th>";
 	response +="</tr>";
-	
+
 	bg = " background-color: ";
 	bg += ( gwayConfig.cad ? "LightGreen" : "orange" );
 	response +="<tr><td class=\"cell\">CAD</td>";
@@ -510,7 +510,7 @@ static void gatewaySettings()
 	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: ";
 	bg += ( gwayConfig.hop ? "LightGreen" : "orange" );
 	response +="<tr><td class=\"cell\">HOP</td>";
@@ -519,7 +519,8 @@ static void gatewaySettings()
 	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\">";
 	if (gwayConfig.cad) {
 		response += "AUTO</td>";
@@ -530,7 +531,7 @@ static void gatewaySettings()
 		response +="<td class=\"cell\"><a href=\"SF=1\"><button>+</button></a></td>";
 	}
 	response +="</tr>";
-	
+
 	// Channel
 	response +="<tr><td class=\"cell\">Channel</td>";
 	response +="<td class=\"cell\" colspan=\"2\">"; 
@@ -564,16 +565,16 @@ static void gatewaySettings()
 	response +="<td class=\"cell\"><a href=\"DEBUG=-1\"><button>-</button></a></td>";
 	response +="<td class=\"cell\"><a href=\"DEBUG=1\"><button>+</button></a></td>";
 	response +="</tr>";
-	
+
 	// Debug Pattern
 	response +="<tr><td class=\"cell\">Debug pattern</td>"; 
-	
+
 	bg = ( (pdebug & P_SCAN) ? "LightGreen" : "orange" ); 
 	response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
 	response += bg;	response += "\">";
 	response +="<a href=\"PDEBUG=SCAN\">";
 	response +="<button>SCN</button></a></td>";
-	
+
 	bg = ( (pdebug & P_CAD) ? "LightGreen" : "orange" ); 
 	response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
 	response += bg;	response += "\">";
@@ -592,7 +593,7 @@ static void gatewaySettings()
 	response +="<a href=\"PDEBUG=TX\">";
 	response +="<button>TX</button></a></td>";
 	response += "</tr>";
-	
+
 	// Use a second Line
 	response +="<tr><td class=\"cell\"></td>";
 	bg = ( (pdebug & P_PRE) ? "LightGreen" : "orange" ); 
@@ -606,7 +607,7 @@ static void gatewaySettings()
 	response += bg;	response += "\">";
 	response +="<a href=\"PDEBUG=MAIN\">";
 	response +="<button>MAI</button></a></td>";
-	
+
 	bg = ( (pdebug & P_GUI) ? "LightGreen" : "orange" ); 
 	response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
 	response += bg;	response += "\">";
@@ -643,7 +644,7 @@ static void gatewaySettings()
 	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>";
 	response +="<td class=\"cell\" colspan=\"2\">";
@@ -651,7 +652,7 @@ static void gatewaySettings()
 	response +="</td><td colspan=\"2\" style=\"border: 1px solid black;\">";
 	response +="<button><a href=\"/FCNT\">RESET   </a></button></td>";
 	response +="</tr>";
-	
+
 	bg = " background-color: ";
 	bg += ( (gwayConfig.isNode == 1) ? "LightGreen" : "orange" );
 	response +="<tr><td class=\"cell\">Gateway Node</td>";
@@ -662,6 +663,17 @@ static void gatewaySettings()
 	response +="</tr>";
 #endif
 
+#if _REPEATER>=0
+	bg = " background-color: ";
+	bg += ( _REPEATER==1 ? "LightGreen" : "orange" );
+	response +="<tr><td class=\"cell\">REPEATER</td>";
+	response +="<td colspan=\"2\" style=\"border: 1px solid black;"; response += bg; response += "\">";
+	response += ( _REPEATER==1 ? "ON" : "OFF" );
+	//response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"REPT=0\"><button>OFF</button></a></td>";
+	//response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"REPT=1\"><button>ON</button></a></td>";
+	response +="</tr>";
+#endif
+
 	// Format the Filesystem
 	response +="<tr><td class=\"cell\">Format SPIFFS</td>";
 	response +=String() + "<td class=\"cell\" colspan=\"2\" >"+gwayConfig.formatCntr+"</td>";
@@ -678,10 +690,9 @@ static void gatewaySettings()
 	response +=String() + "<td class=\"cell\" colspan=\"2\" >"+gwayConfig.boots+"</td>";
 	response +="<td style=\"width:30px;\" colspan=\"2\" class=\"cell\" ><input type=\"button\" value=\"BOOT    \" onclick=\"ynDialog(\'Do you want to reset boots?\',\'BOOT\')\" /></td></tr>";
 #endif //_STATISTICS
-	
+
 	response +="</table>";
-	
-	
+
 	server.sendContent(response);
 }
 
@@ -1124,18 +1135,20 @@ static void wifiConfig()
 		response +="<tr><td class=\"cell\">IP Gateway</td><td class=\"cell\">"; 
 		printIP((IPAddress)WiFi.gatewayIP(),'.',response); 
 		response +="</tr>";
+#ifdef _TTNSERVER
 		response +="<tr><td class=\"cell\">NTP Server</td><td class=\"cell\">"; response+=NTP_TIMESERVER; response+="</tr>";
 		response +="<tr><td class=\"cell\">LoRa Router</td><td class=\"cell\">"; response+=_TTNSERVER; response+="</tr>";
 		response +="<tr><td class=\"cell\">LoRa Router IP</td><td class=\"cell\">"; 
 		printIP((IPAddress)ttnServer,'.',response); 
 		response +="</tr>";
+#endif //_TTNSERVER
 #ifdef _THINGSERVER
 		response +="<tr><td class=\"cell\">LoRa Router 2</td><td class=\"cell\">"; response+=_THINGSERVER; 
 		response += String() + ":" + _THINGPORT + "</tr>";
 		response +="<tr><td class=\"cell\">LoRa Router 2 IP</td><td class=\"cell\">"; 
 		printIP((IPAddress)thingServer,'.',response);
 		response +="</tr>";
-#endif
+#endif //_THINGSERVER
 
 		response +="</table>";
 
@@ -1353,14 +1366,14 @@ void setupWWW()
 		server.send( 302, "text/plain", "");
 	});
 
-#if _MONITOR>=1
+#	if _MONITOR>=1
 	// Display Monitor Console or not
 	server.on("/MONITOR", []() {
 		server.sendHeader("Location", String("/"), true);
 		gwayConfig.monitor = bool(1 - (int) gwayConfig.monitor) ;
 		server.send( 302, "text/plain", "");
 	});
-#endif //_MONITOR
+#	endif //_MONITOR
 	
 	// Display the SEEN statistics
 	server.on("/SEEN", []() {
@@ -1512,11 +1525,11 @@ void setupWWW()
 		gwayConfig.reents = 0;					// Re-entrance
 		
 		writeGwayCfg(_CONFIGFILE, &gwayConfig );
-#if _MONITOR>=1
+#if		_MONITOR>=1
 		if ((debug>=2) && (pdebug & P_GUI)) {
 			mPrint("wwwServer:: BOOT: config written");
 		}
-#endif
+#		endif //_MONITOR
 		server.sendHeader("Location", String("/"), true);
 		server.send( 302, "text/plain", "");
 	});
@@ -1769,9 +1782,9 @@ void setupWWW()
 	// Display LOGging information
 	server.on("/LOG", []() {
 		server.sendHeader("Location", String("/"), true);
-#if _MONITOR>=1
-		mPrint("LOG button");
-#endif //_MONITOR
+#		if _MONITOR>=1
+			mPrint("LOG button");
+#		endif //_MONITOR
 		buttonLog();
 		server.send( 302, "text/plain", "");
 	});

+ 25 - 12
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.5.EU868; PlatformIO 200524i"
+#define VERSION "V.6.2.6.EU868; PlatformIO 200908 d"
 
 //
 // Based on work done by Thomas Telkamp for Raspberry PI 1ch gateway and many others.
@@ -104,6 +104,10 @@
 //#define _TTNROUTER 1
 
 
+#if !defined _CHANNEL
+#	define _CHANNEL 0
+#endif
+
 // The spreading factor is the most important parameter to set for a single channel
 // gateway. It specifies the speed/datarate in which the gateway and node communicate.
 // As the name says, in principle the single channel gateway listens to one channel/frequency
@@ -111,8 +115,9 @@
 // This parameters contains the default value of SF, the actual version can be set with
 // the webserver and it will be stored in SPIFF
 // NOTE: The frequency is set in the loraModem.h file and is default 868.100000 MHz.
-#define _SPREADING SF9
-
+#if !defined _SPREADING
+#	define _SPREADING SF9
+#endif
 
 // Channel Activity Detection
 // This function will scan for valid LoRa headers and determine the Spreading 
@@ -180,13 +185,18 @@
 #endif
 
 
-//
-// Also, normally the server will respond with SF12 in the RX2 timeslot.
-// For TTN, the RX2 timeslot is SF9, so we should use that one for TTN
+// Extra Microseconds delay added by the Up receiver from the sensor to the server.
+// As the tmst is also corrected, this will add to the downlink messages also.
+// The server will use this value to compute the receive window.
 #if !defined _RXDELAY1
-#	define _RXDELAY1 1000
+//#	define _RXDELAY1 1000
+#	define _RXDELAY1 0
 #endif
 
+
+//
+// Also, normally the server will respond with SF12 in the RX2 timeslot.
+// For TTN, the RX2 timeslot is SF9, so we should use that one for TTN
 #if !defined _RX2_SF
 #	define _RX2_SF 9
 #endif
@@ -323,13 +333,13 @@
 
 
 // Timing
-#define _PULL_INTERVAL 20					// PULL_DATA messages to server to get downstream in seconds
+#define _PULL_INTERVAL 16					// 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 _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
+#define _RST_INTERVAL 97					// Reset interval in seconds, total chip reset
 
 
 // Define the correct radio type that you are using
@@ -347,6 +357,9 @@
 
 
 // MQTT definitions, these settings should be standard for TTN
-// and need no changing
-#define _TTNSERVER "router.eu.thethings.network"
-#define _TTNPORT 1700							// Standard port for TTN
+// and need no changing. When _REPEATER function is selected, we do not
+// use the backend function to send message to server over MQTT.
+#if _REPEATER==0
+#	define _TTNSERVER "router.eu.thethings.network"
+#	define _TTNPORT 1700							// Standard port for TTN
+#endif

+ 7 - 7
src/configNode.h

@@ -30,9 +30,9 @@
 #	define _SENSOR_INTERVAL 300
 
 	// Sensor and app address information
-#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 _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 }
 
 	// For ESP32 based T_BEAM/TTGO boards these two are normally included
 	// If included make value 1, else if not, make them 0
@@ -63,7 +63,7 @@ nodex nodes[] = {
 // Although this is probably overkill in normal gateway situations, it greatly helps
 // in debugging the node messages before they reach the TTN severs.
 //
-#if _LOCALSERVER==1
+#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
@@ -136,9 +136,9 @@ wpas wpa[] = {
 #define _DESCRIPTION "ESP Gateway"			// Name of the gateway
 #define _EMAIL "mw12554@hotmail.com"		// Owner
 #define _PLATFORM "ESP8266"
-#define _LAT 52.237367
-#define _LON 5.978654
-#define _ALT 14								// Altitude
+#define _LAT 52.200000
+#define _LON 5.90000
+#define _ALT 1								// Altitude
 
 
 // For asserting and testing the following defines are used.

+ 1 - 1
src/loraFiles.h

@@ -75,7 +75,7 @@ struct espGwayConfig {
 	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 ch;					// index to freqs array, freqs[gwayConfig.ch]=868100000 default
 	uint8_t sf;					// range from SF7 to SF12
 	uint8_t debug;				// range 0 to 4
 	uint8_t pdebug;				// pattern debug, 

+ 65 - 34
src/loraModem.h

@@ -87,7 +87,7 @@ vector freqs [] =
 	{ 867700000, 125, 7, 12, 867700000, 125, 7, 12},			// Channel 6, 867.7 MHz/125 Optional 
 	{ 867900000, 125, 7, 12, 867900000, 125, 7, 12},			// Channel 7, 867.9 MHz/125 Optional 
 	{ 868800000, 125, 7, 12, 868800000, 125, 7, 12},			// Channel 8, 868.9 MHz/125 FSK Only										
-	{ 0,         0  , 0,  0, 869525000, 125, 9, 9}				// Channel 9, 869.5 MHz/125 for RX2 responses SF9(10%)
+	{ 0,         0  , 0,  0, 869525000, 125, 9, 9}				// Channel 9, 869.525 MHz/125 for RX2 responses SF9(10%)
 	// TTN defines an additional channel at 869.525 MHz using SF9 for class B. Not used
 };
 
@@ -321,12 +321,13 @@ struct stat_c {
 	uint32_t msg_down;
 	uint32_t msg_sens;
 
+	// Of statistics == 2 we add spreading factor data to the statistics
 #if _STATISTICS >= 2						// Only if we explicitly set it higher	
 	uint32_t sf7, sf8, sf9;					// Spreading factor 7, 8, 9 statistics/Count
 	uint32_t sf10, sf11, sf12;				// Spreading factor 10, 11, 12
 	
 	// If _STATISTICS is 3, we add statistics about the channel 
-	// When only one channel is used, we normally know the spread of
+	// When only one channel is used, we normally know the spread of these
 	// statistics, but when HOP mode is selected we migth want to add this info
 #if _STATISTICS >=3
 	uint32_t msg_ok_0,   msg_ok_1,   msg_ok_2;
@@ -349,43 +350,69 @@ struct stat_c {
 } stat_c;
 struct stat_c statc;
 
-
-
 // History of received uplink and downlink messages from nodes
 struct stat_t * statr;
 
 
-
-
 #else //_STATISTICS==0
 struct stat_t	statr[1];					// Always have at least one element to store in
 #endif
 
+
 // Define the payload structure used to separate interrupt and SPI
 // processing from the loop() part
 uint8_t payLoad[128];						// Payload i
+
+
+
+// ====================================================================
+// PACKET FORWARDER
+// Smetech Specification
+// https://github.com/Lora-net/packet_forwarder/blob/master/PROTOCOL.TXT
+// Some parts are included both at Upstram and Downstream since
+// the gateway can also be used as a repeater
+// ====================================================================
 struct LoraDown {
-	uint32_t	tmst;						//
-	uint32_t	freq;
-	uint8_t		payLength;
-	uint8_t		sfTx;
-	uint8_t		powe;
+	uint32_t	tmst;						// Timestamp (will ignore time)
+	uint32_t	tmms;						// Timestamp according to GPS (sync required)
+	uint32_t	time;
+	double_t	freq;						// Frequency
+	uint8_t		size;
+	//			chan = <NOT USED>
+	bool		ipol;
+	uint8_t		powe;						// transmit power, normally 14, except when using special channel
 	uint8_t		crc;
-	uint8_t		iiq;
-	uint8_t		imme;
+	uint8_t		iiq;						// message inverted or not for node-node communiction
+	uint8_t		imme;						// Immediate transfer execution
+	uint8_t		sf;							// through datr
+	uint8_t		bw;							// through datr
+	uint8_t		ncrc;						// no CRC check
+	uint8_t		prea;						// preamble
+	uint8_t		rfch;						// Concentrator "RF chain" used for TX (unsigned integer)
+	char *		modu;						//	"LORA" os "FSCK"
+	char *		datr;						// = "SF12BW125", contains both .sf and .bw parts
+	char *		codr;
+	
+
+	
 	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 {
+	uint32_t	tmst;						// Timestamp of message
+	uint32_t	tmms;						// <not used at the moment>
+	uint32_t	time;						// <not used at the moment>
+	double_t	freq;						// frequency used in HZ
+	uint8_t		size;						// Length of the message Payload
+	uint8_t		chan;						// Channel "IF" used for RX
+	uint8_t		sf;							// Spreading Factor
+	//			modu not used
+	//			datr = "SF12BW125", contains both .sf and .bw parts
 	int32_t		snr;
-	uint32_t	tmst;
 	int16_t		prssi; 
 	int16_t		rssicorr;
-	uint8_t		payLength;
-	uint8_t		sf;
 	uint8_t		payLoad[128];
 } LoraUp;
 
@@ -399,8 +426,8 @@ struct LoraUp {
 // need to set in the program.
 
 #define REG_FIFO                    0x00		// rw FIFO address
-#define REG_OPMODE                  0x01
-// Register 2 to 5 are unused for LoRa
+#define REG_OPMODE                  0x01		// Operation Mode Register (Page 108)
+												// Register 2 to 5 are unused for LoRa
 #define REG_FRF_MSB					0x06
 #define REG_FRF_MID					0x07
 #define REG_FRF_LSB					0x08
@@ -419,14 +446,14 @@ struct LoraUp {
 #define REG_PKT_RSSI				0x1A		// latest package
 #define REG_RSSI					0x1B		// Current RSSI, section 6.4, or  5.5.5
 #define REG_HOP_CHANNEL				0x1C
-#define REG_MODEM_CONFIG1           0x1D
-#define REG_MODEM_CONFIG2           0x1E
+#define REG_MODEM_CONFIG1           0x1D		// LoRa: Modem PHY config 1
+#define REG_MODEM_CONFIG2           0x1E		// LoRa: Modem PHY config 2
 #define REG_SYMB_TIMEOUT_LSB  		0x1F
 
 #define REG_PAYLOAD_LENGTH          0x22
 #define REG_MAX_PAYLOAD_LENGTH 		0x23
 #define REG_HOP_PERIOD              0x24
-#define REG_MODEM_CONFIG3           0x26
+#define REG_MODEM_CONFIG3           0x26		// Modem PHY config 3
 #define REG_RSSI_WIDEBAND			0x2C
 
 #define REG_INVERTIQ				0x33
@@ -434,9 +461,9 @@ struct LoraUp {
 #define REG_SYNC_WORD				0x39
 #define REG_TEMP					0x3C
 
-#define REG_DIO_MAPPING_1           0x40
-#define REG_DIO_MAPPING_2           0x41
-#define REG_VERSION	  				0x42
+#define REG_DIO_MAPPING_1           0x40		// Mapping of pins DIO0 to DIO3
+#define REG_DIO_MAPPING_2           0x41		// Mapping of pins DIO4 and DIO5, ClkOut frequency
+#define REG_VERSION	  				0x42		// 0x12? Semtech def
 
 #define REG_PADAC					0x5A
 #define REG_PADAC_SX1272			0x5A
@@ -454,7 +481,10 @@ struct LoraUp {
 // ----------------------------------------
 // LMIC Constants for radio registers
 #define OPMODE_LORA      			0x80
-#define OPMODE_MASK      			0x07
+#define OPMODE_MASK      			0x0F		// Select LSB 8 bits 0, ignore LoRa bit for example
+
+#define OPMODE_LOWFREQ				0x08		// Should be - for 868.1 MHZ operation
+
 #define OPMODE_SLEEP     			0x00
 #define OPMODE_STANDBY   			0x01
 #define OPMODE_FSTX      			0x02
@@ -545,7 +575,7 @@ struct LoraUp {
 #define IRQ_LORA_RXDONE_MASK 		0x40	// RXDONE after receiving the header and CRC, we receive payload part
 #define IRQ_LORA_CRCERR_MASK 		0x20	// CRC error detected. Note that RXDONE will also be set
 #define IRQ_LORA_HEADER_MASK 		0x10	// valid HEADER mask. This interrupt is first when receiving a message
-#define IRQ_LORA_TXDONE_MASK 		0x08	// End of TRansmission
+#define IRQ_LORA_TXDONE_MASK 		0x08	// End of Transmission
 #define IRQ_LORA_CDDONE_MASK 		0x04	// CDDONE
 #define IRQ_LORA_FHSSCH_MASK 		0x02
 #define IRQ_LORA_CDDETD_MASK 		0x01	// Detect preamble channel
@@ -553,13 +583,14 @@ struct LoraUp {
 
 // ----------------------------------------
 // Definitions for UDP message arriving from server
-#define PROTOCOL_VERSION			0x01
-#define PKT_PUSH_DATA				0x00
-#define PKT_PUSH_ACK				0x01
-#define PKT_PULL_DATA				0x02
-#define PKT_PULL_RESP				0x03
-#define PKT_PULL_ACK				0x04
-#define PKT_TX_ACK                  0x05
+#define PROTOCOL_VERSION			0x02
+
+#define PUSH_DATA					0x00
+#define PUSH_ACK					0x01
+#define PULL_DATA					0x02
+#define PULL_RESP					0x03
+#define PULL_ACK					0x04
+#define TX_ACK						0x05
 
 #define MGT_RESET					0x15		// Not a LoRa Gateway Spec message
 #define MGT_SET_SF					0x16