Skip to content
Snippets Groups Projects
Select Git revision
  • main default protected
  • sam
  • rodoshi
  • sep
  • master
5 results

_txRx.ino

Blame
  • _txRx.ino 25.47 KiB
    #include "Esp32MQTTClient.h"
    #include <HTTPClient.h>
    #include <Arduino.h>
    
    // Error codes
    #define ERROR_CODE_MISSING_PACKET     0x01
    #define ERROR_CODE_RECEPTION_FAILURE    0x02
    #define ERROR_CODE_WDT_EARLY_WARNING    0x04
    #define ERROR_CODE_INVALID_TEMPERATURE    0x08
    #define ERROR_CODE_MISMATCHED_PACKET_NUM  0x10
    
    /** Type Definitions **/
    struct Lab3Packet_t
    {
      uint8_t NodeID;
      uint16_t PacketID;
      uint32_t Timestamp;
      float AverageTemperature;
      uint8_t ErrorType;
    };
    
    
    // 1-channel LoRa Gateway for ESP8266
    // Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
    // Version 5.3.3
    // Date: 2018-08-25
    //
    // 	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 DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN 
    // Send DOWN a LoRa packet over the air to the node. This function does all the 
    // decoding of the server message and prepares a Payload buffer.
    // The payload is actually transmitted by the sendPkt() function.
    // 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.
    // ----------------------------------------------------------------------------
    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
    	// 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;
    	StaticJsonBuffer<312> jsonBuffer;
    	char * bufPtr = (char *) (buf);
    	buf[length] = 0;
    	
    #if DUSB>=1
    	if (debug>=2) {
    		Serial.println((char *)buf);
    		Serial.print(F("<"));
    		Serial.flush();
    	}
    #endif
    	// 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
    	JsonObject& root = jsonBuffer.parseObject(bufPtr);
    		
    	if (!root.success()) {
    #if DUSB>=1
    		if (( debug>=1) && (pdebug & P_TX)) {
    			Serial.print (F("T sendPacket:: ERROR Json Decode"));
    			if (debug>=2) {
    				Serial.print(':');
    				Serial.println(bufPtr);
    			}
    			Serial.flush();
    		}
    #endif
    		return(-1);
    	}
    	yield();
    	
    	// Meta Data sent by server (example)
    	// {"txpk":{"codr":"4/5","data":"YCkEAgIABQABGmIwYX/kSn4Y","freq":868.1,"ipol":true,"modu":"LORA","powe":14,"rfch":0,"size":18,"tmst":1890991792,"datr":"SF7BW125"}}
    
    	// Used in the protocol of Gateway:
    	const char * data	= root["txpk"]["data"];			// Downstream Payload
    	uint8_t psize		= root["txpk"]["size"];
    	bool ipol			= root["txpk"]["ipol"];
    	uint8_t powe		= root["txpk"]["powe"];			// e.g. 14 or 27
    	LoraDown.tmst		= (uint32_t) root["txpk"]["tmst"].as<unsigned long>();
    	const float ff		= root["txpk"]["freq"];			// eg 869.525
    	
    	// 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 DUSB>=1
    		if (( debug>=2 ) && ( pdebug & P_TX )) { 
    			Serial.print(F("T data: ")); 
    			Serial.println((char *) data);
    			if (debug>=2) Serial.flush();
    		}
    #endif
    	}
    	else {												// There is data!
    #if DUSB>=1
    		if ((debug>0) && ( pdebug & P_TX )) {
    			Serial.println(F("T sendPacket:: ERROR: data is NULL"));
    			if (debug>=2) Serial.flush();
    		}
    #endif
    		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
    	uint32_t w = (uint32_t) (LoraDown.tmst - micros());	// Wait Time compute
    
    // _STRICT_1CH determines ho we will react on downstream messages.
    // If STRICT==0, we will receive messags from the TTN gateway presumably on SF12/869.5MHz
    // And since the Gateway is a single channel gateway, and its nodes are probably
    // single channle too. They will not listen to that frequency.
    // When STRICT==1, we will answer (in the RX1 timeslot) on the frequency we receive on.
    //
    #if _STRICT_1CH == 1
    	// If possible use RX1 timeslot as this is our frequency.
    	// Do not use RX2 or JOIN2 as they contain other frequencies
    	
    	if ((w>1000000) && (w<3000000)) { 
    		LoraDown.tmst-=1000000; 
    	}	// Is tmst correction necessary
    	else if ((w>6000000) && (w<7000000)) { 
    		LoraDown.tmst-=500000; 
    	}
    	LoraDown.powe = 14;										// On all freqs except 869.5MHz power is limited
    	//LoraDown.sfTx = sfi;									// Take care, TX sf not to be mixed with SCAN
    	LoraDown.fff = freq;									// Use the current frequency
    #else
    	LoraDown.powe = powe;
    
    	// convert double frequency (MHz) into uint32_t frequency in Hz.
    	LoraDown.fff = (uint32_t) ((uint32_t)((ff+0.000035)*1000)) * 1000;
    #endif
    	
    	LoraDown.payLoad = payLoad;				
    
    #if DUSB>=1
    	if (( debug>=1 ) && ( pdebug & P_TX)) {
    	
    		Serial.print(F("T LoraDown tmst="));
    		Serial.print(LoraDown.tmst);
    		//Serial.print(F(", w="));
    		//Serial.print(w);
    		
    		if ( debug>=2 ) {
    			Serial.print(F(" Request:: "));
    			Serial.print(F(" tmst=")); Serial.print(LoraDown.tmst); Serial.print(F(" wait=")); Serial.println(w);
    		
    			Serial.print(F(" strict=")); Serial.print(_STRICT_1CH);
    			Serial.print(F(" datr=")); Serial.println(datr);
    			Serial.print(F(" Rfreq=")); Serial.print(freq); Serial.print(F(", Request=")); Serial.print(freq); Serial.print(F(" ->")); Serial.println(LoraDown.fff);
    			Serial.print(F(" sf  =")); Serial.print(atoi(datr+2)); Serial.print(F(" ->")); Serial.println(LoraDown.sfTx);
    		
    			Serial.print(F(" modu=")); Serial.println(modu);
    			Serial.print(F(" powe=")); Serial.println(powe);
    			Serial.print(F(" codr=")); Serial.println(codr);
    
    			Serial.print(F(" ipol=")); Serial.println(ipol);
    		}
    		Serial.println();
    	}
    #endif
    
    	if (LoraDown.payLength != psize) {
    #if DUSB>=1
    		Serial.print(F("sendPacket:: WARNING payLength: "));
    		Serial.print(LoraDown.payLength);
    		Serial.print(F(", psize="));
    		Serial.println(psize);
    		if (debug>=2) Serial.flush();
    #endif
    	}
    #if DUSB>=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();
    		if (debug>=2) Serial.flush();
    	}
    #endif
    	cp_up_pkt_fwd++;
    
    #if DUSB>=1
    	if (( debug>=2 ) && ( pdebug & P_TX )) {
    		Serial.println(F("T sendPacket:: fini OK"));
    	}
    #endif // DUSB
    
    	// 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
    	
    	return 1;
    }//sendPacket
    
    
    
    
    // ----------------------------------------------------------------------------
    // UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP
    // Based on the information read from the LoRa transceiver (or fake message)
    // build a gateway message to send upstream (to the user somewhere on the web).
    //
    // parameters:
    // 	tmst: Timestamp to include in the upstream message
    // 	buff_up: The buffer that is generated for upstream
    // 	message: The payload message to include in the the buff_up
    //	messageLength: The number of bytes received by the LoRa transceiver
    // 	internal: Boolean value to indicate whether the local sensor is processed
    //
    // returns:
    //	buff_index
    // ----------------------------------------------------------------------------
    int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool internal) 
    {
    	long SNR;
        int rssicorr;
    	int prssi;											// packet rssi
    	
    	char cfreq[12] = {0};								// Character array to hold freq in MHz
    	//lastTmst = tmst;									// Following/according to spec
    	int 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=( MAX_STAT -1); m>0; m--) statr[m]=statr[m-1];
    	
    	// From now on we can fill start[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].tmst = now();
    	statr[0].ch= ifreq;
    	statr[0].prssi = prssi - rssicorr;
    #if RSSI==1
    	statr[0].rssi = _rssi - rssicorr;
    #endif // RSII
    	statr[0].sf = LoraUp.sf;
    #if DUSB>=2
    	if (debug>=0) {
    		if ((message[4] != 0x26) || (message[1]==0x99)) {
    			Serial.print(F("addr="));
    			for (int i=messageLength; i>0; i--) {
    				if (message[i]<0x10) Serial.print('0');
    				Serial.print(message[i],HEX);
    				Serial.print(' ');
    			}
    			Serial.println();
    		}
    	}
    #endif //DUSB
    	statr[0].node = ( message[1]<<24 | message[2]<<16 | message[3]<<8 | message[4] );
    
    #if STATISTICS >= 2
    	// 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) 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) 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) 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 DUSB>=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();
    	}
    #endif // DUSB
    
    // 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);
    
    //	msg_oLED(timBuff, prssi-rssicorr, SNR, message)
    	
        display.drawString(0, 0, "Time: " );
        display.drawString(40, 0, timBuff);
    	
        display.drawString(0, 16, "RSSI: " );
        display.drawString(40, 16, String(prssi-rssicorr));
    	
        display.drawString(70, 16, ",SNR: " );
        display.drawString(110, 16, String(SNR) );
    	  
    	display.drawString(0, 32, "Addr: " );
    	  
        if (message[4] < 0x10) display.drawString( 40, 32, "0"+String(message[4], HEX)); else display.drawString( 40, 32, String(message[4], HEX));
    	if (message[3] < 0x10) display.drawString( 61, 32, "0"+String(message[3], HEX)); else display.drawString( 61, 32, String(message[3], HEX));
    	if (message[2] < 0x10) display.drawString( 82, 32, "0"+String(message[2], HEX)); else display.drawString( 82, 32, String(message[2], HEX));
    	if (message[1] < 0x10) display.drawString(103, 32, "0"+String(message[1], HEX)); else display.drawString(103, 32, String(message[1], HEX));
    	  
        display.drawString(0, 48, "LEN: " );
        display.drawString(40, 48, String((int)messageLength) );
        display.display();
    	//yield();
    
    #endif //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 DUSB>=1
    	if ((debug>=1) && (encodedLen>255) && ( pdebug & P_RADIO )) {
    		Serial.print(F("R buildPacket:: b64 err, len="));
    		Serial.println(encodedLen);
    		if (debug>=2) Serial.flush();
    		return(-1);
    	}
    #endif // DUSB
    	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\":[", 9);
    	buff_index += 9;
    	buff_up[buff_index] = '{';
    	++buff_index;
    	j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, "\"tmst\":%u", tmst);
    #if DUSB>=1
    	if ((j<0) && ( debug>=1 ) && ( pdebug & P_RADIO )) {
    		Serial.println(F("buildPacket:: Error "));
    	}
    #endif
    	buff_index += j;
    	ftoa((double)freq/1000000,cfreq,6);					// XXX This can be done better
    	j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%s", 0, 0, cfreq);
    	buff_index += j;
    	memcpy((void *)(buff_up + buff_index), (void *)",\"stat\":1", 9);
    	buff_index += 9;
    	memcpy((void *)(buff_up + buff_index), (void *)",\"modu\":\"LORA\"", 14);
    	buff_index += 14;
    	
    	/* Lora datarate & bandwidth, 16-19 useful chars */
    	switch (LoraUp.sf) {
    		case SF6:
    			memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF6", 12);
    			buff_index += 12;
    			break;
    		case SF7:
    			memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF7", 12);
    			buff_index += 12;
    			break;
    		case SF8:
                memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF8", 12);
                buff_index += 12;
                break;
    		case SF9:
                memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF9", 12);
                buff_index += 12;
                break;
    		case SF10:
                memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF10", 13);
                buff_index += 13;
                break;
    		case SF11:
                memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF11", 13);
                buff_index += 13;
                break;
    		case SF12:
                memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF12", 13);
                buff_index += 13;
                break;
    		default:
                memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF?", 12);
                buff_index += 12;
    	}
    	memcpy((void *)(buff_up + buff_index), (void *)"BW125\"", 6);
    	buff_index += 6;
    	memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"4/5\"", 13);
    	buff_index += 13;
    	j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"lsnr\":%li", SNR);
    	buff_index += j;
    	j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"rssi\":%d,\"size\":%u", prssi-rssicorr, messageLength);
    	buff_index += j;
    	memcpy((void *)(buff_up + buff_index), (void *)",\"data\":\"", 9);
    	buff_index += 9;
    
    	// Use gBase64 library to fill in the data string
    	encodedLen = base64_enc_len(messageLength);			// max 341
    	j = base64_encode((char *)(buff_up + buff_index), (char *) message, messageLength);
    
    	buff_index += j;
    	buff_up[buff_index] = '"';
    	++buff_index;
    
    	// End of packet serialization
    	buff_up[buff_index] = '}';
    	++buff_index;
    	buff_up[buff_index] = ']';
    	++buff_index;
    	
    	// end of JSON datagram payload */
    	buff_up[buff_index] = '}';
    	++buff_index;
    	buff_up[buff_index] = 0; 							// add string terminator, for safety
    
    //#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 );
      Serial.println((char *)message);
      for(int idx=0;idx<messageLength;idx++)
      {
        Serial.print(message[idx],HEX);
        Serial.print(" ");
      }
      Serial.println("");
      Serial.println("start sending events.");
      char buff[256];
    
      struct Lab3Packet_t* packet;
      packet = (struct Lab3Packet_t*)message;
      String packetString = packetToString(packet, false);
    
      //Send the in JSON format
      String res = "{\"Message\": \"";
      //res.concat((char *)message);
      res.concat(packetString);
      res.concat("\"}");
      // Replace the following line with your data sent to Azure IoTHub
      snprintf(buff, 256, res.c_str());
      Serial.println(res);
      if (Esp32MQTTClient_SendEvent(buff))
      {
        Serial.println("Sending data succeed");
      }
      else
      {
        Serial.println("Failure...");
      }
    //#endif	
    	
    #if DUSB>=1
    	if (( debug>=2 ) && ( pdebug & P_RX )) {
    		Serial.print(F("R RXPK:: "));
    		Serial.println((char *)(buff_up + 12));			// debug: display JSON payload
    		Serial.print(F("R RXPK:: package length="));
    		Serial.println(buff_index);
    	}
    #endif
    	return(buff_index);
    }// buildPacket
    
    
    
    
    
    // ----------------------------------------------------------------------------
    // UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP 
    // 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" function called by loop()
    // ----------------------------------------------------------------------------
    int receivePacket()
    {
    	uint8_t buff_up[TX_BUFF_SIZE]; 						// buffer to compose the upstream packet to backend server
    	long SNR;
    	uint8_t message[128] = { 0x00 };					// MSG size is 128 bytes for rx
    	uint8_t messageLength = 0;
    	
    	// 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 Not yet supported
    
    		// Take the timestamp as soon as possible, to have accurate reception timestamp
    		// TODO: tmst can jump if micros() overflow.
    		uint32_t tmst = (uint32_t) micros();			// Only microseconds, rollover in 5X minutes
    		//lastTmst = tmst;								// Following/according to spec
    		
    		// Handle the physical data read from LoraUp
    		if (LoraUp.payLength > 0) {
    
    			// externally received packet, so last parameter is false (==LoRa external)
                int build_index = buildPacket(tmst, buff_up, LoraUp, false);
    
    			// REPEATER is a special function where we retransmit 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
    			
    			// This is one of the potential problem areas.
    			// If possible, USB traffic should be left out of interrupt routines
    			// rxpk PUSH_DATA received from node is rxpk (*2, par. 3.2)
    #ifdef _TTNSERVER
    			if (!sendUdp(ttnServer, _TTNPORT, buff_up, build_index)) {
    				return(-1); 							// received a message
    			}
    			yield();
    #endif
    			// Use our own defined server or a second well kon server
    #ifdef _THINGSERVER
    			if (!sendUdp(thingServer, _THINGPORT, buff_up, build_index)) {
    				return(-2); 							// received a message
    			}
    #endif
    
    #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>=1 ) && ( 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();
    				}
    			}
    			else if (( debug>=2 ) && ( pdebug & P_RX )) {
    					Serial.println(F("receivePacket:: No Index"));
    			}
    #endif //DUSB
    #endif // _LOCALSERVER
    
    			// Reset the message area
    			LoraUp.payLength = 0;
    			LoraUp.payLoad[0] = 0x00;
    			return(build_index);
            }
    		
    	return(0);											// failure no message read
    	
    }//receivePacket
    
    
    
    
    bool IsErrorDetect(struct Lab3Packet_t* packet)
    {
      if (packet->ErrorType == 0)
      {
        return false;
      }
      
      uint32_t errorCode = 0
        | ((uint32_t)packet->ErrorType) << 24 // ErrorType occupies bits 31 downto 24
        | ((uint32_t)packet->NodeID) << 16    // NodeID occupies bits 23 downto 16
        | ((uint32_t)packet->PacketID)      // PacketID occupies bits 15 downto 0
      ;
      
      // Print an error message
      printError(errorCode);
      
      return true;
    }
    
    void printError(uint32_t errorCode)
    {
      uint8_t errorType = errorCode >> 24;
      uint8_t nodeID = errorCode >> 16;
      uint16_t packetID = errorCode;
      
      Serial.print("Error(s) on Node ID ");
      Serial.print(": ");
      
      if (errorType == 0)
      {
        Serial.println("No errors");
        return;
      }
      
      Serial.print("Error(s): ");
      if (errorType & ERROR_CODE_MISSING_PACKET)
      {
        Serial.print("{Missing packet} ");
      }
      if (errorType & ERROR_CODE_RECEPTION_FAILURE)
      {
        Serial.print("{Reception failure} ");
      }
      if (errorType & ERROR_CODE_WDT_EARLY_WARNING)
      {
        Serial.print("{WDT early warning} ");
      }
      if (errorType & ERROR_CODE_INVALID_TEMPERATURE)
      {
        Serial.print("{Invalid temperature} ");
      }
      Serial.println("");
    }
    
    String packetToString(struct Lab3Packet_t* packet, bool printThisDeviceTag)
    {
      String s = String("Node ID = ")
        + String(packet->NodeID)
        + String(", Packet ID = ")
        + String(packet->PacketID)
        + String(", Timestamp = ")
        + String(packet->Timestamp)
        + String(", Average Temperature = ")
        + String(packet->AverageTemperature)
        + String(", Error Type = 0x")
        + String(packet->ErrorType, HEX)
      ;
    
      return s;
    }
    
    void printPacket(struct Lab3Packet_t* packet)
    {
      Serial.println(packetToString(packet, true));
    }