// 1-channel LoRa Gateway for ESP8266 and ESP32
// Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
//
// 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
//	and many others.
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the MIT License
// which accompanies this distribution, and is available at
// https://opensource.org/licenses/mit-license.php
//
// NO WARRANTY OF ANY KIND IS PROVIDED
//
// Author: Maarten Westenberg (mw12554@hotmail.com) 
//
// This file contains the LoRa modem specific code enabling to receive
// and transmit packages/messages.
// ========================================================================================


// ------------------------------- DOWN ----------------------------------------
//
// Send DOWN a LoRa packet over the air to the node. This function does all the 
// decoding of the server message and prepares a Payload buffer.
// The payload is actually transmitted by the sendPkt() function.
// This function is used for regular downstream messages and for JOIN_ACCEPT
// messages.
// NOTE: This is not an interrupt function, but is started by loop().
// The _status is set an the end of the function to TX and in _stateMachine
// function the actual transmission function is executed.
// The LoraDown.tmst contains the timestamp that the tranmission should finish (Node start reading).
// ----------------------------------------------------------------------------
int sendPacket(uint8_t *buf, uint8_t length) 
{
	// Received package with Meta Data (for example):
	// codr	: "4/5"
	// data	: "Kuc5CSwJ7/a5JgPHrP29X9K6kf/Vs5kU6g=="	// for example
	// freq	: 868.1 									// 868100000 default
	// ipol	: true/false
	// modu : "LORA"
	// powe	: 14										// Set by default
	// rfch : 0											// Set by default
	// size	: 21
	// tmst : 1800642 									// for example
	// datr	: "SF7BW125"
	
	// 12-byte header;
	//		HDR (1 byte)
	//		
	// Data Reply for JOIN_ACCEPT as 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;
	//StaticJsonDocument<312> jsonBuffer;							// Use of arduinoJson version 6!
	char * bufPtr = (char *) (buf);
	buf[length] = 0;
	
#	if _MONITOR>=1
	if (( debug>=2) && (pdebug & P_TX)) {
		mPrint("Dwn sendPacket:: " + String((char *)buf) + "< ");
	}
#	endif //_MONITOR

	// Use JSON to decode the string after the first 4 bytes.
	// The data for the node is in the "data" field. This function destroys original buffer
	auto error = deserializeJson(jsonBuffer, bufPtr);		
	if (error) {
#		if _MONITOR>=1
		if ((debug>=0) && (pdebug & P_TX)) {
			mPrint("Dwn 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"}}

	// Used in the protocol of Gateway:

	JsonObject root		= jsonBuffer.as<JsonObject>();	// 191111 Avoid Crashes

	LoraDown.tmst		= (uint32_t) root["txpk"]["tmst"].as<unsigned long>();
	const char * data	= root["txpk"]["data"];			// Downstream Payload
	uint8_t psize		= root["txpk"]["size"];			// Payload size
	bool ipol			= root["txpk"]["ipol"];
//	uint8_t powe		= root["txpk"]["powe"];			// power, e.g. 14 or 27
	
	// Not used in the protocol of Gateway TTN:
	const char * datr	= root["txpk"]["datr"];			// eg "SF7BW125"
	//const char * modu	= root["txpk"]["modu"];			// =="LORA"
	//const char * codr	= root["txpk"]["codr"];			// e.g. "4/5"
	//if (root["txpk"].containsKey("imme") ) {
	//	const bool imme = root["txpk"]["imme"];			// Immediate Transmit (tmst don't care)
	//}

	if ( data != NULL ) {
#		if _MONITOR>=1
		if (( debug>=2 ) && ( pdebug & P_TX )) { 
			mPrint("sendPacket:: data=" + String(data)); 
		}
#		endif //_MONITOR
	}
	else {												// There is data!
#		if _MONITOR>=1
		if ((debug>=0) && (pdebug & P_TX)) {
			mPrint("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.
// We will anser in RX2 in the Spreading Factor set by _RX2_SF (set in configGway.h)
// This way, we can better communicate as a single gateway machine
//
#if _STRICT_1CH == 1
	// RX1 is requested frequency
	// RX2 is SF _RX2_SF probably SF9
	// If possible use RX1 timeslot as this is our frequency.
	// Do not use RX2 or JOIN2 as they contain other frequencies (868.5 MHz)

	LoraDown.powe	= 14;								// On all freqs except 869.5MHz power is limited
	LoraDown.freq	= freqs[gwayConfig.ch].dwnFreq;		// Use the requestor Down frequency (always)
	
	// Wait time RX1, between 1 and 2 seconds, or OTAA between 6 and 7 seconds
	if (((w>0000000) && (w<2000000)) ||
		((w>5000000) && (w<6000000)) )
	{ 													// LoraDown.sfTx set by initiator
#		ifdef _PROFILER
		if ((debug>=2) && (pdebug & P_TX)) {
			mPrint("loraPacket:: RX1: micros="+String(micros())); 
		}
#		endif //_PROFILER
	}

	// RX2. 
	else if (((w>2000000) && (w<3000000)) ||
			 ((w>6000000) && (w<7000000)) )
	{ 
		LoraDown.sfTx= _RX2_SF;							// Use the RX2 downstream SF (may be dedicated to TTN)
	}
	
	else {
#		if _MONITOR>=1
		if ((debug>=2) && (pdebug & P_TX)) {
			mPrint("_STRICT==1:: Not RX1 or RX2, wait= "+String(w/1000000)+"."+String(w%1000000)+", SF="+String(LoraDown.sfTx)+", Freq="+LoraDown.freq );
		}
#		endif //_MONITOR
		// And do not convert the down SF.
	}

#else
// elif _STRICT_1CH == 0, we will receive messags from the TTN gateway presumably on SF9/869.5MHz
// And since the Gateway is a single channel gateway, and its nodes are probably
// single channel too. They will not listen to that frequency at all.
// Pleae note that this parameter is more for nodes (that cannot change freqs)
// than for gateways.
// We will probably answer in RX with RF==12 and use special answer frequency
//
	LoraDown.powe	= root["txpk"]["powe"];
	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;
#endif //_STRICT_1CH

	yield();
	
	LoraDown.payLoad = payLoad;				

#	if _MONITOR>=1
	if (( debug>=2 ) && ( pdebug & P_TX)) {
		mPrint("Dwn sendPacket:: TX tmst=" + String(LoraDown.tmst));
	}
#	endif // _MONITOR

	if (LoraDown.payLength != psize) {
#		if _MONITOR>=1
		if (debug>=0) {
			mPrint("Dwn sendPacket:: WARNING payLength=" + String(LoraDown.payLength) + ", psize=" + String(psize) );
		}
#		endif //_MONITOR
	}
#	if _MONITOR>=1
	else if (( debug >= 2 ) && ( pdebug & P_TX )) {
		Serial.print(F("T Payload="));
		for (i=0; i<LoraDown.payLength; i++) {
			Serial.print(payLoad[i],HEX); 
			Serial.print(':'); 
		}
		Serial.println();
	}
#	endif //_MONITOR

	// Update downstream statistics
	statc.msg_down++;
	switch(statr[0].ch) {
		case 0: statc.msg_down_0++; break;
		case 1: statc.msg_down_1++; break;
		case 2: statc.msg_down_2++; break;
	}

	// All data is in Payload and parameters and need to be transmitted.
	// The function is called in user-space
	_state = S_TX;										// _state set to transmit
	
#	if _MONITOR>=1
	if ((debug>=2) && ( pdebug & P_TX)) {
		mPrint("sendPacket:: STRICT=" + String(_STRICT_1CH) );
	}
#	endif //_MONITOR
	
	return 1;
} //sendPacket DOWN




// --------------------------------- UP ---------------------------------------
//
// Based on the information read from the LoRa transceiver (or fake message)
// build a gateway message to send upstream (to the user somewhere on the web).
//
// 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
//
// returns:
//	buff_index:
// ----------------------------------------------------------------------------
int buildPacket(uint8_t *buff_up, struct LoraUp *LoraUp, bool internal) 
{
	int32_t SNR;
    int16_t rssicorr;
	int16_t prssi;										// packet rssi
	
	char cfreq[12] = {0};								// Character array to hold freq in MHz
	uint16_t buff_index=0;
	char b64[256];
	
	uint8_t *message = LoraUp->payLoad;
	char messageLength = LoraUp->payLength;
		
#if _CHECK_MIC==1
	unsigned char NwkSKey[16] = _NWKSKEY;
	checkMic(message, messageLength, NwkSKey);
#endif // _CHECK_MIC

	// Read SNR and RSSI from the register. Note: Not for internal sensors!
	// For internal sensor we fake these values as we cannot read a register
	if (internal) {
		SNR = 12;
		prssi = 50;
		rssicorr = 157;
	}
	else {
		SNR = LoraUp->snr;
		prssi = LoraUp->prssi;								// read register 0x1A, packet rssi
		rssicorr = LoraUp->rssicorr;
	}

#if _STATISTICS >= 1
	// Receive statistics, move old statistics down 1 position
	// and fill the new top line with the latest received sensor values.
	// This works fine for the sensor, EXCEPT when we decode data for _LOCALSERVER
	//
	for (int m=( _MAXSTAT -1); m>0; m--) statr[m]=statr[m-1];
	
	// From now on we can fill statr[0] with sensor data
#if _LOCALSERVER==1
	statr[0].datal=0;
	int index;
	if ((index = inDecodes((char *)(LoraUp->payLoad+1))) >=0 ) {

		uint16_t frameCount=LoraUp->payLoad[7]*256 + LoraUp->payLoad[6];
		
		for (int k=0; (k<LoraUp->payLength) && (k<23); k++) {
			statr[0].data[k] = LoraUp->payLoad[k+9];
		};
		
		// XXX Check that k<23 when leaving the for loop
		// XXX or we can not display in statr
		
		uint8_t DevAddr[4]; 
		DevAddr[0]= LoraUp->payLoad[4];
		DevAddr[1]= LoraUp->payLoad[3];
		DevAddr[2]= LoraUp->payLoad[2];
		DevAddr[3]= LoraUp->payLoad[1];

		statr[0].datal = encodePacket((uint8_t *)(statr[0].data), 
								LoraUp->payLength -9 -4, 
								(uint16_t)frameCount, 
								DevAddr, 
								decodes[index].appKey, 
								0);
	}
#endif //_LOCALSERVER

	statr[0].time = now();								// Not a real timestamp. but the current time
	statr[0].ch =	gwayConfig.ch;
	statr[0].prssi = prssi - rssicorr;
	statr[0].sf =	LoraUp->sf;
	
#	if RSSI==1
		statr[0].rssi = _rssi - rssicorr;
#	endif // RSII

	statr[0].node = ( message[1]<<24 | message[2]<<16 | message[3]<<8 | message[4] );

#if _STATISTICS >= 2
	// Fill in the statistics that we will also need for the GUI.
	// So 
	switch (statr[0].sf) {
		case SF7:  statc.sf7++;  break;
		case SF8:  statc.sf8++;  break;
		case SF9:  statc.sf9++;  break;
		case SF10: statc.sf10++; break;
		case SF11: statc.sf11++; break;
		case SF12: statc.sf12++; break;
	}
#endif // _STATISTICS >= 2



#if _STATISTICS >= 3
	if (statr[0].ch == 0) {
		statc.msg_ttl_0++;								// Increase #message received channel 0
		switch (statr[0].sf) {
			case SF7:  statc.sf7_0++;  break;
			case SF8:  statc.sf8_0++;  break;
			case SF9:  statc.sf9_0++;  break;
			case SF10: statc.sf10_0++; break;
			case SF11: statc.sf11_0++; break;
			case SF12: statc.sf12_0++; break;
		}
	}
	else 
	if (statr[0].ch == 1) {
		statc.msg_ttl_1++;
		switch (statr[0].sf) {
			case SF7:  statc.sf7_1++;  break;
			case SF8:  statc.sf8_1++;  break;
			case SF9:  statc.sf9_1++;  break;
			case SF10: statc.sf10_1++; break;
			case SF11: statc.sf11_1++; break;
			case SF12: statc.sf12_1++; break;
		}
	}
	else 
	if (statr[0].ch == 2) {
		statc.msg_ttl_2++;
		switch (statr[0].sf) {
			case SF7:  statc.sf7_2++;  break;
			case SF8:  statc.sf8_2++;  break;
			case SF9:  statc.sf9_2++;  break;
			case SF10: statc.sf10_2++; break;
			case SF11: statc.sf11_2++; break;
			case SF12: statc.sf12_2++; break;
		}
	}
#endif //_STATISTICS >= 3

#endif //_STATISTICS >= 2

#if _MONITOR>=1	
	if (( debug>=2 ) && ( pdebug & P_RADIO )){
		Serial.print(F("R buildPacket:: pRSSI="));
		Serial.print(prssi-rssicorr);
		Serial.print(F(" RSSI: "));
		Serial.print(_rssi - rssicorr);
		Serial.print(F(" SNR: "));
		Serial.print(SNR);
		Serial.print(F(" Length: "));
		Serial.print((int)messageLength);
		Serial.print(F(" -> "));
		int i;
		for (i=0; i< messageLength; i++) {
					Serial.print(message[i],HEX);
					Serial.print(' ');
		}
		Serial.println();
		yield();										// only if debug>=2
	}
#endif // _MONITOR

// Show received message status on Oled display
#if _OLED>=1
    char timBuff[20];
    sprintf(timBuff, "%02i:%02i:%02i", hour(), minute(), second());
	
    display.clear();
    display.setFont(ArialMT_Plain_16);
    display.setTextAlignment(TEXT_ALIGN_LEFT);
	
    display.drawString(0, 0, "Time: " );
    display.drawString(40, 0, timBuff);
	
    display.drawString(0, 16, "RSSI: " );
    display.drawString(40, 16, String(prssi-rssicorr));
	
    display.drawString(70, 16, ",SNR: " );
    display.drawString(110, 16, String(SNR) );
	  
	display.drawString(0, 32, "Addr: " );
	  
    if (message[4] < 0x10) display.drawString( 40, 32, "0"+String(message[4], HEX)); else display.drawString( 40, 32, String(message[4], HEX));
	if (message[3] < 0x10) display.drawString( 61, 32, "0"+String(message[3], HEX)); else display.drawString( 61, 32, String(message[3], HEX));
	if (message[2] < 0x10) display.drawString( 82, 32, "0"+String(message[2], HEX)); else display.drawString( 82, 32, String(message[2], HEX));
	if (message[1] < 0x10) display.drawString(103, 32, "0"+String(message[1], HEX)); else display.drawString(103, 32, String(message[1], HEX));
	  
    display.drawString(0, 48, "LEN: " );
    display.drawString(40, 48, String((int)messageLength) );
    display.display();

#endif // _OLED>=1
			
//	int j;
	
	// XXX Base64 library is nopad. So we may have to add padding characters until
	// 	message Length is multiple of 4!
	// Encode message with messageLength into b64
	int encodedLen = base64_enc_len(messageLength);		// max 341

#	if _MONITOR>=1
	if ((debug>=1) && (encodedLen>255) && (pdebug & P_RADIO)) {
		mPrint("R buildPacket:: b64 err, len=" + String(encodedLen));
		return(-1);
	}
#	endif // _MONITOR

	base64_encode(b64, (char *) message, messageLength);// max 341
	// start composing datagram with the header 
	uint8_t token_h = (uint8_t)rand(); 					// random token
	uint8_t token_l = (uint8_t)rand(); 					// random token
	
	// pre-fill the data buffer with fixed fields
	buff_up[0] = PROTOCOL_VERSION;						// 0x01 still

	buff_up[1] = token_h;
	buff_up[2] = token_l;
	
	buff_up[3] = PKT_PUSH_DATA;							// 0x00
	
	// READ MAC ADDRESS OF ESP8266, and insert 0xFF 0xFF in the middle
	buff_up[4]  = MAC_array[0];
	buff_up[5]  = MAC_array[1];
	buff_up[6]  = MAC_array[2];
	buff_up[7]  = 0xFF;
	buff_up[8]  = 0xFF;
	buff_up[9]  = MAC_array[3];
	buff_up[10] = MAC_array[4];
	buff_up[11] = MAC_array[5];

	buff_index	= 12; 									// 12-byte binary (!) header
	
	// start of JSON structure that will make payload
	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. 
// The serializeJson() version hopefully is quicker

#ifdef _JSONENCODE
//------------------
	StaticJsonDocument<400> doc;
	// MMM Get rid of this code when ready
	
	//doc["time"] = "";

	doc["chan"] = "0";
	doc["rfch"] = "0";
	doc["freq"] = "" + (freqs[gwayConfig.ch].upFreq / 1000000);
	doc["stat"] = "1";
	doc["modu"] = "LORA";
	doc["datr"] = "SF" + String(LoraUp->sf) + "BW125";
	doc["rssi"] = "" +(prssi-rssicorr);
	doc["lsnr"] = "" +(long)SNR;
	doc["codr"] = "4/5";

	// Use gBase64 library to fill in the data string
	encodedLen = base64_enc_len(messageLength);			// max 341	
	doc["size"] = "" + encodedLen;

	int len= base64_encode(doc["data"], (char *)message, messageLength);

	LoraUp->tmst = doc["tmst"] = "" + (uint32_t) micros() + _RXDELAY1;		// Tmst correction							
	
	const char	* p =  (const char *) & (buff_up [buff_index]);				// Start in buff where to put the serializedJson
	int written = serializeJson(doc, (const char *)p, buff_index+20 );		// size is buff_index + encoded data + some closing chars


#else // _JSONENCODE undefined or ==0, this is default
// -----------------
	ftoa((double)freqs[gwayConfig.ch].upFreq / 1000000, cfreq, 6);		// XXX This can be done better
	if ((LoraUp->sf<6) || (LoraUp->sf>12)) { 				// Lora datarate & bandwidth SF6-SF12, 16-19 useful chars */
		LoraUp->sf=7;
	}			

//	buff_index += snprintf((char *)(buff_up + buff_index), 
//		TX_BUFF_SIZE-buff_index, 
//		"%04d-%02d-%02d %02d:%02d:%02d CET", 
//		year(),month(),day(),hour(),minute(),second());

	buff_index += snprintf((char *)(buff_up + buff_index), 
		TX_BUFF_SIZE-buff_index, 
		"\"chan\":%1u,\"rfch\":%1u,\"freq\":%s,\"stat\":1,\"modu\":\"LORA\"" , 
		0, 0, cfreq);
	
	buff_index += snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index
		, ",\"datr\":\"SF%uBW125\",\"codr\":\"4/5\",\"lsnr\":%li,\"rssi\":%d,\"size\":%u,\"data\":\""
		, LoraUp->sf, (long)SNR, prssi-rssicorr, messageLength);

	// Use gBase64 library to fill in the data string
	encodedLen = base64_enc_len(messageLength);				// max 341
	buff_index += base64_encode((char *)(buff_up + buff_index), (char *) message, messageLength);

	
	LoraUp->tmst = (uint32_t) micros()+ _RXDELAY1; 			// MMM Correct timing with defined number,
															// https://github.com/TheThingsNetwork/lorawan-stack/issues/277

	// Get rid of this code when ready	

	buff_index += snprintf((char *)(buff_up + buff_index), 
		TX_BUFF_SIZE-buff_index, "\",\"tmst\":%u", 
		LoraUp->tmst);	

#endif // _JSONENCODE undefined or ==0
// ---------------------


	// End of packet serialization
	buff_up[buff_index]   = '}'; 
	buff_up[buff_index+1] = ']'; 
	buff_up[buff_index+2] = '}'; 
	buff_index += 3;
	
	buff_up[buff_index] = 0; 							// add string terminator, for safety

	// When we have the node address and the SF, fill the listSeen array
	// with the required data. _MAXSEEN must be >0 for this to happen.
	// statr[0] contains the statistics of the node last seen.
#	if  _MAXSEEN >= 1
		//yield();										// MMM 200316 Huge influence !!! on timing
		addSeen(listSeen, statr[0]);
#	endif

#	if _STAT_LOG == 1	
		// Do statistics logging. In first version we might only
		// write part of the record to files, later more
		addLog( (unsigned char *)(buff_up), buff_index );
#	endif //_STAT_LOG

#	if _MONITOR>=1
	if ((debug>=2) && (pdebug & P_RX)) {			// debug: display JSON payload
		mPrint("UP RXPK:: "+String((char *)(buff_up + 12))+" , length="+String(buff_index));		
	}
#	endif

	return(buff_index);
	
}// buildPacket()



// --------------------------------- UP ---------------------------------------
//
// Receive a LoRa package over the air, LoRa and deliver to server(s)
//
// Receive a LoRa message and fill the buff_up char buffer.
// returns values:
// - returns the length of string returned in buff_up
// - returns -1 or -2 when no message arrived, depending connection.
//
// This is the "highlevel" read function called by loop(). The receive function 
// is started in the _stateMachine.ini file after CDONE event by interrupt 
// functions.
// However, the actual read from the buffer (filled by interrupt) is done 
// by this function in the main loop() program.
// ----------------------------------------------------------------------------
int receivePacket()
{
	uint8_t buff_up[TX_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
	// in one UDP message as the Semtech Gateway spec does allow this.
	// XXX Bit ... Not yet supported

#ifdef _PROFILER
	if ((debug>=1) && (pdebug & P_RX)) {
		String response = "UP receivePacket:: start: micr=";
			printInt(micros(),response);
			response += ", tmst=";
			printInt(LoraUp.tmst,response);
			mPrint(response);
	}
#endif // _PROFILER
		
		// Handle the physical data read from LoraUp
		if (LoraUp.payLength > 0) {

			// externally received packet, so last parameter is false (==LoRa external)
			// Make a buffer to transmit later
            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
#			if _REPEATER==1
			if (!sendLora(LoraUp.payLoad, LoraUp.payLength)) {
				return(-3);
			}
#			endif

#			ifdef _TTNSERVER	
			// This is one of the potential problem areas.
			// If possible, USB traffic should be left out of interrupt routines
			// rxpk PUSH_DATA received from node is rxpk (*2, par. 3.2)

			if (!sendUdp(ttnServer, _TTNPORT, buff_up, build_index)) {
				return(-1); 							// received a message
			}
#			endif //_TTNSERVER

			yield();
			Udp.flush();								// MMM 200419

#ifdef _PROFILER
			if ((debug>=1) && (pdebug & P_RX)) {
				String response = "UP receivePacket:: sendUdp: micr=";
				printInt(micros(),response);
				response += ", tmst=";
				printInt(LoraUp.tmst,response);
				mPrint(response);
			}
#endif // _PROFILER

#			ifdef _THINGSERVER
			// Use our own defined server or a second well known server
			if (!sendUdp(thingServer, _THINGPORT, buff_up, build_index)) {
				return(-2); 							// received a message
			}
#			endif //_THINGSERVER

#if _LOCALSERVER==1
			// Or special case, we do not use a local server to receive
			// and decode the server. We use buildPacket() to call decode
			// and use statr[0] information to store decoded message

			//DecodePayload: para 4.3.1 of Lora 1.1 Spec
			// MHDR
			//	1 byte			Payload[0]
			// FHDR
			// 	4 byte Dev Addr Payload[1-4]
			// 	1 byte FCtrl  	Payload[5]
			// 	2 bytes FCnt	Payload[6-7]				
			// 		= Optional 0 to 15 bytes Options
			// FPort
			//	1 bytes, 0x00	Payload[8]
			// ------------
			// +=9 BYTES HEADER
			//
			// FRMPayload
			//	N bytes			(Payload )
			//
			// 4 bytes MIC trailer

			int index=0;
			if ((index = inDecodes((char *)(LoraUp.payLoad+1))) >=0 ) {

				uint8_t DevAddr[4]; 
				DevAddr[0]= LoraUp.payLoad[4];
				DevAddr[1]= LoraUp.payLoad[3];
				DevAddr[2]= LoraUp.payLoad[2];
				DevAddr[3]= LoraUp.payLoad[1];
				//uint16_t frameCount=LoraUp.payLoad[7]*256 + LoraUp.payLoad[6];

#if _DUSB>=1
				if ((debug>=2) && (pdebug & P_RX)) {
					Serial.print(F("R receivePacket:: Ind="));
					Serial.print(index);
					Serial.print(F(", Len="));
					Serial.print(LoraUp.payLength);
					Serial.print(F(", A="));
					for (int i=0; i<4; i++) {
						if (DevAddr[i]<0x0F) Serial.print('0');
						Serial.print(DevAddr[i],HEX);
						//Serial.print(' ');
					}
				
					Serial.print(F(", Msg="));
					for (int i=0; (i<statr[0].datal) && (i<23); i++) {
						if (statr[0].data[i]<0x0F) Serial.print('0');
						Serial.print(statr[0].data[i],HEX);
						Serial.print(' ');
					}
					Serial.println();
				}
#endif //DUSB

			}
#			if _MONITOR>=1
			else if (( debug>=2 ) && ( pdebug & P_RX )) {
					mPrint("receivePacket:: No Index");
			}
#			endif //_MONITOR
#endif // _LOCALSERVER

			// Reset the message area
			LoraUp.payLength = 0;
			LoraUp.payLoad[0] = 0x00;
			
			
			return(build_index);
        }
		
	return(0);											// failure no message read
	
}//receivePacket

