// 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.
// ========================================================================================
//
//
//
// SPI AND INTERRUPTS
// The RFM96/SX1276 communicaties with the ESP8266 by means of interrupts 
// and SPI interface. The SPI interface is bidirectional and allows both
// parties to simultaneous write and read to registers.
// Major drawback is that access is not protected for interrupt and non-
// interrupt access. This means that when a program in loop() and a program
// in interrupt do access the readregister and writeRegister() function
// at teh same time that probably an error will occur.
// Therefore it is best to Either not use interrupts AT all (like LMIC)
// or only use these functions in inteerupts and to further processing
// in the main loop() program.
//
// ============================================================================


// ----------------------------------------------------------------------------
// Mutex definitiona
//
// ----------------------------------------------------------------------------
#if MUTEX==1
	void CreateMutux(int *mutex) {
		*mutex=1;
	}

#define LIB_MUTEX 1
#if LIB_MUTEX==1
	bool GetMutex(int *mutex) {
		//noInterrupts();
		if (*mutex==1) { 
			*mutex=0; 
			//interrupts(); 
			return(true); 
		}
		//interrupts();
		return(false);
	}
#else	
	bool GetMutex(int *mutex) {

	int iOld = 1, iNew = 0;

	asm volatile (
		"rsil a15, 1\n"    // read and set interrupt level to 1
		"l32i %0, %1, 0\n" // load value of mutex
		"bne %0, %2, 1f\n" // compare with iOld, branch if not equal
		"s32i %3, %1, 0\n" // store iNew in mutex
		"1:\n"             // branch target
		"wsr.ps a15\n"     // restore program state
		"rsync\n"
		: "=&r" (iOld)
		: "r" (mutex), "r" (iOld), "r" (iNew)
		: "a15", "memory"
	);
	return (bool)iOld;
}
#endif

	void ReleaseMutex(int *mutex) {
		*mutex=1;
	}
	
#endif //MUTEX==1

// ----------------------------------------------------------------------------
// Read one byte value, par addr is address
// Returns the value of register(addr)
// 
// The SS (Chip select) pin is used to make sure the RFM95 is selected
// The variable is for obvious reasons valid for read and write traffic at the
// same time. Since both read and write mean that we write to the SPI interface.
// Parameters:
//	Address: SPI address to read from. Type uint8_t
// Return:
//	Value read from address
// ----------------------------------------------------------------------------

// define the SPI settings for reading messages
SPISettings readSettings(SPISPEED, MSBFIRST, SPI_MODE0);

uint8_t readRegister(uint8_t addr)
{

	SPI.beginTransaction(readSettings);				
    digitalWrite(pins.ss, LOW);					// Select Receiver
	SPI.transfer(addr & 0x7F);
	uint8_t res = (uint8_t) SPI.transfer(0x00);
    digitalWrite(pins.ss, HIGH);				// Unselect Receiver
	SPI.endTransaction();
    return((uint8_t) res);
}


// ----------------------------------------------------------------------------
// Write value to a register with address addr. 
// Function writes one byte at a time.
// Parameters:
//	addr: SPI address to write to
//	value: The value to write to address
// Returns:
//	<void>
// ----------------------------------------------------------------------------

// define the settings for SPI writing
SPISettings writeSettings(SPISPEED, MSBFIRST, SPI_MODE0);

void writeRegister(uint8_t addr, uint8_t value)
{
	SPI.beginTransaction(writeSettings);
	digitalWrite(pins.ss, LOW);					// Select Receiver
	
	SPI.transfer((addr | 0x80) & 0xFF);
	SPI.transfer(value & 0xFF);
	//delayMicroseconds(10);
	
    digitalWrite(pins.ss, HIGH);				// Unselect Receiver
	
	SPI.endTransaction();
}


// ----------------------------------------------------------------------------
// Write a buffer to a register with address addr. 
// Function writes one byte at a time.
// Parameters:
//	addr: SPI address to write to
//	value: The value to write to address
// Returns:
//	<void>
// ----------------------------------------------------------------------------

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++) {
		SPI.transfer(buf[i] & 0xFF);
	}
    digitalWrite(pins.ss, HIGH);				// Unselect Receiver
	
	SPI.endTransaction();
}

// ----------------------------------------------------------------------------
//  setRate is setting rate and spreading factor and CRC etc. for transmission
//		Modem Config 1 (MC1) == 0x72 for sx1276
//		Modem Config 2 (MC2) == (CRC_ON) | (sf<<4)
//		Modem Config 3 (MC3) == 0x04 | (optional SF11/12 LOW DATA OPTIMIZE 0x08)
//		sf == SF7 default 0x07, (SF7<<4) == SX72_MC2_SF7
//		bw == 125 == 0x70
//		cr == CR4/5 == 0x02
//		CRC_ON == 0x04
// ----------------------------------------------------------------------------

void setRate(uint8_t sf, uint8_t crc) 
{
	uint8_t mc1=0, mc2=0, mc3=0;
#if DUSB>=2
	if ((sf<SF7) || (sf>SF12)) {
		if (( debug>=1 ) && ( pdebug & P_RADIO )) {
			Serial.print(F("setRate:: SF="));
			Serial.println(sf);
		}
		return;
	}
#endif
	// Set rate based on Spreading Factor etc
    if (sx1272) {
		mc1= 0x0A;				// SX1276_MC1_BW_250 0x80 | SX1276_MC1_CR_4_5 0x02
		mc2= ((sf<<4) | crc) % 0xFF;
		// SX1276_MC1_BW_250 0x80 | SX1276_MC1_CR_4_5 0x02 | SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01
        if (sf == SF11 || sf == SF12) { mc1= 0x0B; }			        
    }
	
	// For sx1276 chips is the CRC ON is 
	else {
		if (sf==SF8) {
			mc1= 0x78;				// SX1276_MC1_BW_125==0x70 | SX1276_MC1_CR_4_8==0x08
		}
		else {
			mc1= 0x72;				// SX1276_MC1_BW_125==0x70 | SX1276_MC1_CR_4_5==0x02
		}
		mc2= ((sf<<4) | crc) & 0xFF; // crc is 0x00 or 0x04==SX1276_MC2_RX_PAYLOAD_CRCON
		mc3= 0x04;				// 0x04; SX1276_MC3_AGCAUTO
        if (sf == SF11 || sf == SF12) { mc3|= 0x08; }		// 0x08 | 0x04
    }
	
	// Implicit Header (IH), for class b beacons (&& SF6)
	//if (getIh(LMIC.rps)) {
    //   mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON;
    //    writeRegister(REG_PAYLOAD_LENGTH, getIh(LMIC.rps)); // required length
    //}
	
	writeRegister(REG_MODEM_CONFIG1, (uint8_t) mc1);
	writeRegister(REG_MODEM_CONFIG2, (uint8_t) mc2);
	writeRegister(REG_MODEM_CONFIG3, (uint8_t) mc3);
	
	// Symbol timeout settings
    if (sf == SF10 || sf == SF11 || sf == SF12) {
        writeRegister(REG_SYMB_TIMEOUT_LSB, (uint8_t) 0x05);
    } else {
        writeRegister(REG_SYMB_TIMEOUT_LSB, (uint8_t) 0x08);
    }
	return;
}


// ----------------------------------------------------------------------------
// Set the frequency for our gateway
// The function has no parameter other than the freq setting used in init.
// Since we are usin a 1ch gateway this value is set fixed.
// ----------------------------------------------------------------------------

void  setFreq(uint32_t freq)
{
    // set frequency
    uint64_t frf = ((uint64_t)freq << 19) / 32000000;
    writeRegister(REG_FRF_MSB, (uint8_t)(frf>>16) );
    writeRegister(REG_FRF_MID, (uint8_t)(frf>> 8) );
    writeRegister(REG_FRF_LSB, (uint8_t)(frf>> 0) );
	
	return;
}


// ----------------------------------------------------------------------------
//	Set Power for our gateway
// ----------------------------------------------------------------------------
void setPow(uint8_t powe)
{
	if (powe >= 16) powe = 15;
	//if (powe >= 15) powe = 14;
	else if (powe < 2) powe =2;
	
	ASSERT((powe>=2)&&(powe<=15));
	
	uint8_t pac = (0x80 | (powe & 0xF)) & 0xFF;
	writeRegister(REG_PAC, (uint8_t)pac);								// set 0x09 to pac
	
	// XXX Power settings for CFG_sx1272 are different
	
	return;
}


// ----------------------------------------------------------------------------
// Used to set the radio to LoRa mode (transmitter)
// Please note that this mode can only be set in SLEEP mode and not in Standby.
// Also there should be not need to re-init this mode is set in the setup() 
// function.
// For high freqs (>860 MHz) we need to & with 0x08 otherwise with 0x00
// ----------------------------------------------------------------------------

//void ICACHE_RAM_ATTR opmodeLora()
//{
//#ifdef CFG_sx1276_radio
//       uint8_t u = OPMODE_LORA | 0x80;   					// TBD: sx1276 high freq 
//#else // SX-1272
//	    uint8_t u = OPMODE_LORA | 0x08;
//#endif
//    writeRegister(REG_OPMODE, (uint8_t) u);
//}


// ----------------------------------------------------------------------------
// Set the opmode to a value as defined on top
// 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)
{
	if (mode == OPMODE_LORA) 
		writeRegister(REG_OPMODE, (uint8_t) mode);
	else
		writeRegister(REG_OPMODE, (uint8_t)((readRegister(REG_OPMODE) & ~OPMODE_MASK) | mode));
}

// ----------------------------------------------------------------------------
// Hop to next frequency as defined by NUM_HOPS
// This function should only be used for receiver operation. The current
// receiver frequency is determined by ifreq index like so: freqs[ifreq] 
// ----------------------------------------------------------------------------
void hop() {

	// 1. Set radio to standby
	opmode(OPMODE_STANDBY);
		
	// 3. Set frequency based on value in freq		
	ifreq = (ifreq + 1) % NUM_HOPS ;							// Increment the freq round robin
	freq = freqs[ifreq];
	setFreq(freqs[ifreq]);

	// 4. Set spreading Factor
	sf = SF7;													// Starting the new frequency 
	setRate(sf, 0x40);											// set the sf to SF7 
		
	// Low Noise Amplifier used in receiver
	writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN);  			// 0x0C, 0x23
	
	// 7. set sync word
	writeRegister(REG_SYNC_WORD, (uint8_t) 0x34);				// set 0x39 to 0x34 LORA_MAC_PREAMBLE
	
	// prevent node to node communication
	writeRegister(REG_INVERTIQ,0x27);							// 0x33, 0x27; to reset from TX
	
	// Max Payload length is dependent on 256 byte buffer. At startup TX starts at
	// 0x80 and RX at 0x00. RX therefore maximized at 128 Bytes
	writeRegister(REG_MAX_PAYLOAD_LENGTH,MAX_PAYLOAD_LENGTH);	// set 0x23 to 0x80==128 bytes
	writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH);			// 0x22, 0x40==64Byte long
	
	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_RX_BASE_AD));	// set reg 0x0D to 0x0F
	writeRegister(REG_HOP_PERIOD,0x00);							// reg 0x24, set to 0x00

	// 5. Config PA Ramp up time								// set reg 0x0A  
	writeRegister(REG_PARAMP, (readRegister(REG_PARAMP) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec
	
	// Set 0x4D PADAC for SX1276 ; XXX register is 0x5a for sx1272
	writeRegister(REG_PADAC_SX1276,  0x84); 					// set 0x4D (PADAC) to 0x84
	//writeRegister(REG_PADAC, readRegister(REG_PADAC) | 0x4);
	
	// 8. Reset interrupt Mask, enable all interrupts
	writeRegister(REG_IRQ_FLAGS_MASK, 0x00);
		
	// 9. clear all radio IRQ flags
    writeRegister(REG_IRQ_FLAGS, 0xFF);
	
	// Be aware that micros() has increased significantly from calling 
	// the hop function until printed below
	//
#if DUSB>=1
	if (( debug>=2 ) && ( pdebug & P_RADIO )){
			Serial.print(F("hop:: hopTime:: "));
			Serial.print(micros() - hopTime);
			Serial.print(F(", "));
			SerialStat(0);
	}
#endif
	// Remember the last time we hop
	hopTime = micros();									// At what time did we 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).
// UP function
// This is the "lowlevel" receive function called by stateMachine()
// dealing with the radio specific LoRa functions
//
// Parameters:
//		Payload: uint8_t[] message. when message is read it is returned in payload.
// Returns:
//		Length of payload received
//
// 9 bytes header
// followed by data N bytes
// 4 bytes MIC end
// ----------------------------------------------------------------------------
uint8_t receivePkt(uint8_t *payload)
{
    uint8_t irqflags = readRegister(REG_IRQ_FLAGS);			// 0x12; read back flags

    cp_nb_rx_rcv++;											// Receive statistics counter

	uint8_t crcUsed = readRegister(REG_HOP_CHANNEL);
	if (crcUsed & 0x40) {
#if DUSB>=1
		if (( debug>=2) && (pdebug & P_RX )) {
			Serial.println(F("R rxPkt:: CRC used"));
		}
#endif
	}
	
    //  Check for payload IRQ_LORA_CRCERR_MASK=0x20 set
    if (irqflags & IRQ_LORA_CRCERR_MASK)
    {
#if DUSB>=1
        if (( debug>=0) && ( pdebug & P_RADIO )) {
			Serial.print(F("rxPkt:: Err CRC, ="));
			SerialTime();
			Serial.println();
		}
#endif
		return 0;
    }
	
	// Is header OK?
	// Please note that if we reset the HEADER interrupt in RX,
	// that we would here conclude that ther eis no HEADER
	else if ((irqflags & IRQ_LORA_HEADER_MASK) == false)
    {
#if DUSB>=1
        if (( debug>=0) && ( pdebug & P_RADIO )) {
			Serial.println(F("rxPkt:: Err HEADER"));
		}
#endif
		// Reset VALID-HEADER flag 0x10
        writeRegister(REG_IRQ_FLAGS, (uint8_t)(IRQ_LORA_HEADER_MASK  | IRQ_LORA_RXDONE_MASK));	// 0x12; clear HEADER (== 0x10) flag
        return 0;
    }
	
	// If there are no error messages, read the buffer from the FIFO
	// This means "Set FifoAddrPtr to FifoRxBaseAddr"
	else {
        cp_nb_rx_ok++;													// Receive OK statistics counter

		if (readRegister(REG_FIFO_RX_CURRENT_ADDR) != readRegister(REG_FIFO_RX_BASE_AD)) {
			if (( debug>=0 ) && ( pdebug & P_RADIO )) {
				Serial.print(F("RX BASE <"));
				Serial.print(readRegister(REG_FIFO_RX_BASE_AD));
				Serial.print(F("> != RX CURRENT <"));
				Serial.print(readRegister(REG_FIFO_RX_CURRENT_ADDR));
				Serial.print(F(">"));
				Serial.println();
			}
		}
		
        //uint8_t currentAddr = readRegister(REG_FIFO_RX_CURRENT_ADDR);	// 0x10
		uint8_t currentAddr = readRegister(REG_FIFO_RX_BASE_AD);		// 0x0F
        uint8_t receivedCount = readRegister(REG_RX_NB_BYTES);			// 0x13; How many bytes were read
#if DUSB>=1
		if ((debug>=0) && (currentAddr > 64)) {
			Serial.print(F("rxPkt:: Rx addr>64"));
			Serial.println(currentAddr);
		}
#endif
        writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) currentAddr);		// 0x0D 

		if (receivedCount > PAYLOAD_LENGTH) {
#if DUSB>=1
			if (( debug>=0 ) & ( pdebug & P_RADIO )) {
				Serial.print(F("rxPkt:: receivedCount="));
				Serial.println(receivedCount);
			}
#endif
			receivedCount=PAYLOAD_LENGTH;
		}

        for(int i=0; i < receivedCount; i++)
        {
            payload[i] = readRegister(REG_FIFO);			// 0x00, FIFO will auto shift register
        }

		writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);		// Reset ALL interrupts
		
		// A long as DUSB is enabled, and RX debug messages are selected,
		//the received packet is displayed on the output.
#if DUSB>=1
		if (( debug>=0 ) && ( pdebug & P_RX )){
		
			Serial.print(F("rxPkt:: t="));
			SerialTime();
			
			Serial.print(F(", f="));
			Serial.print(ifreq);
			Serial.print(F(", sf="));
			Serial.print(sf);
			Serial.print(F(", a="));
			if (payload[4]<0x10) Serial.print('0'); Serial.print(payload[4], HEX);
			if (payload[3]<0x10) Serial.print('0'); Serial.print(payload[3], HEX);
			if (payload[2]<0x10) Serial.print('0'); Serial.print(payload[2], HEX);
			if (payload[1]<0x10) Serial.print('0'); Serial.print(payload[1], HEX);
			Serial.print(F(", flags="));
			Serial.print(irqflags,HEX);
			Serial.print(F(", addr="));
			Serial.print(currentAddr);
			Serial.print(F(", len="));
			Serial.print(receivedCount);

			// If debug level 1 is specified, we display the content of the message as well
			// We need to decode the message as well will it make any sense

			if (debug>=1)  {							// Must be 1 for operational use
#if _TRUSTED_DECODE==2
				int index;								// The index of the codex struct to decode
				String response="";

				uint8_t data[receivedCount];
				
				uint8_t DevAddr [4];
					DevAddr[0] = payload[4];
					DevAddr[1] = payload[3];
					DevAddr[2] = payload[2];
					DevAddr[3] = payload[1];
				
				if ((index = inDecodes((char *)(payload+1))) >=0 ) {
					Serial.print(F(", Ind="));
					Serial.print(index);
					//Serial.println();
				}
				else if (debug>=1) {
					Serial.print(F(", No Index"));
					Serial.println();
					return(receivedCount);
				}	

				// ------------------------------
				
				Serial.print(F(", data="));
				for (int i=0; i<receivedCount; i++) { data[i] = payload[i]; }		// Copy array
				
				//for (int i=0; i<receivedCount; i++) {
				//	if (payload[i]<=0xF) Serial.print('0');
				//	Serial.print(payload[i], HEX);
				//	Serial.print(' ');
				//}



				uint16_t frameCount=payload[7]*256 + payload[6];
				
				// The message received has a length, but data starts at byte 9, and stops 4 bytes
				// before the end since those are MIC bytes
				uint8_t CodeLength = encodePacket((uint8_t *)(data + 9), receivedCount-9-4, (uint16_t)frameCount, DevAddr, decodes[index].appKey, 0);

				Serial.print(F("- NEW fc="));
				Serial.print(frameCount);
				Serial.print(F(", addr="));
				
				for (int i=0; i<4; i++) {
					if (DevAddr[i]<=0xF) Serial.print('0');
					Serial.print(DevAddr[i], HEX);
					Serial.print(' ');
				}
				
				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
			}
			
			Serial.println();
			
			if (debug>=2) Serial.flush();
		}
#endif //DUSB
		return(receivedCount);
    }

	writeRegister(REG_IRQ_FLAGS, (uint8_t) (
		IRQ_LORA_RXDONE_MASK | 
		IRQ_LORA_RXTOUT_MASK |
		IRQ_LORA_HEADER_MASK | 
		IRQ_LORA_CRCERR_MASK));							// 0x12; Clear RxDone IRQ_LORA_RXDONE_MASK
    return 0;
} //receivePkt
	
	
	
// ----------------------------------------------------------------------------
// 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
// ----------------------------------------------------------------------------
bool sendPkt(uint8_t *payLoad, uint8_t payLength)
{
#if DUSB>=2
	if (payLength>=128) {
		if (debug>=1) {
			Serial.print("sendPkt:: len=");
			Serial.println(payLength);
		}
		return false;
	}
#endif
	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_TX_BASE_AD));	// 0x0D, 0x0E
	
	writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) payLength);				// 0x22
	payLoad[payLength] = 0x00;
	writeBuffer(REG_FIFO, (uint8_t *) payLoad, payLength);
	return true;
}

// ----------------------------------------------------------------------------
// loraWait()
// This function implements the wait protocol needed for downstream transmissions.
// Note: Timing of downstream and JoinAccept messages is VERY critical.
//
// As the ESP8266 watchdog will not like us to wait more than a few hundred
// milliseconds (or it will kick in) we have to implement a simple way to wait
// time in case we have to wait seconds before sending messages (e.g. for OTAA 5 or 6 seconds)
// Without it, the system is known to crash in half of the cases it has to wait for 
// JOIN-ACCEPT messages to send.
//
// This function uses a combination of delay() statements and delayMicroseconds().
// As we use delay() only when there is still enough time to wait and we use micros()
// to make sure that delay() did not take too much time this works.
// 
// Parameter: uint32-t tmst gives the micros() value when transmission should start. (!!!)
// Note: We assume LoraDown.sfTx contains the SF we will use for downstream message.
// ----------------------------------------------------------------------------

void loraWait(const uint32_t timestamp)
{
	uint32_t startMics = micros();						// Start of the loraWait function
	uint32_t tmst = timestamp;
// XXX
	int32_t adjust=0;
	switch (LoraDown.sfTx) {
		case 7: adjust= 60000; break;					// Make time for SF7 longer 
		case 8: break;									// Around 60ms
		case 9: break;
		case 10: break;
		case 11: break;
		case 12: break;
		default:
#if DUSB>=1
		if (( debug>=1 ) && ( pdebug & P_TX )) {
			Serial.print(F("T loraWait:: unknown SF="));
			Serial.print(LoraDown.sfTx);
		}
#endif
	}
	tmst = tmst + txDelay + adjust;						// tmst based on txDelay and spreading factor
	uint32_t waitTime = tmst - micros();
	if (waitTime<0) {
		Serial.println(F("loraWait:: Error wait time < 0"));
		return;
	}
	
	// For larger delay times we use delay() since that is for > 15ms
	// This is the most efficient way
	while (waitTime > 16000) {
		delay(15);										// ms delay including yield, slightly shorter
		waitTime= tmst - micros();
	}
	// The remaining wait time is less tan 15000 uSecs
	// And we use delayMicroseconds() to wait
	if (waitTime>0) delayMicroseconds(waitTime);

#if DUSB>=1
	else if ((waitTime+20) < 0) {
		Serial.println(F("loraWait:: TOO LATE"));		// Never happens
	}

	if (( debug>=2 ) && ( pdebug & P_TX )) { 
		Serial.print(F("T start: ")); 
		Serial.print(startMics);
		Serial.print(F(", tmst: "));					// tmst
		Serial.print(tmst);
		Serial.print(F(", end: "));						// This must be micros(), and equal to tmst
		Serial.print(micros());
		Serial.print(F(", waited: "));
		Serial.print(tmst - startMics);
		Serial.print(F(", delay="));
		Serial.print(txDelay);
		Serial.println();
		if (debug>=2) Serial.flush();
	}
#endif
}


// ----------------------------------------------------------------------------
// txLoraModem
// Init the transmitter and transmit the buffer
// 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)
//
//	1. opmode Lora (only in Sleep mode)
//	2. opmode StandBY
//	3. Configure Modem
//	4. Configure Channel
//	5. write PA Ramp
//	6. config Power
//	7. RegLoRaSyncWord LORA_MAC_PREAMBLE
//	8. write REG dio mapping (dio0)
//	9. write REG IRQ flags
// 10. write REG IRQ mask
// 11. write REG LoRa Fifo Base Address
// 12. write REG LoRa Fifo Addr Ptr
// 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
// ----------------------------------------------------------------------------

void txLoraModem(uint8_t *payLoad, uint8_t payLength, uint32_t tmst, uint8_t sfTx,
						uint8_t powe, uint32_t freq, uint8_t crc, uint8_t iiq)
{
#if DUSB>=2
	if (debug>=1) {
		// Make sure that all serial stuff is done before continuing
		Serial.print(F("txLoraModem::"));
		Serial.print(F("  powe: ")); Serial.print(powe);
		Serial.print(F(", freq: ")); Serial.print(freq);
		Serial.print(F(", crc: ")); Serial.print(crc);
		Serial.print(F(", iiq: 0X")); Serial.print(iiq,HEX);
		Serial.println();
		if (debug>=2) Serial.flush();
	}
#endif
	_state = S_TX;
		
	// 1. Select LoRa modem from sleep mode
	//opmode(OPMODE_LORA);									// set register 0x01 to 0x80
	
	// Assert the value of the current mode
	ASSERT((readRegister(REG_OPMODE) & OPMODE_LORA) != 0);
	
	// 2. enter standby mode (required for FIFO loading))
	opmode(OPMODE_STANDBY);									// set 0x01 to 0x01
	
	// 3. Init spreading factor and other Modem setting
	setRate(sfTx, crc);
	
	// Frquency hopping
	//writeRegister(REG_HOP_PERIOD, (uint8_t) 0x00);		// set 0x24 to 0x00 only for receivers
	
	// 4. Init Frequency, config channel
	setFreq(freq);

	// 6. Set power level, REG_PAC
	setPow(powe);
	
	// 7. prevent node to node communication
	writeRegister(REG_INVERTIQ, (uint8_t) iiq);						// 0x33, (0x27 or 0x40)
	
	// 8. set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP (or lesss for 1ch gateway)
    writeRegister(REG_DIO_MAPPING_1, (uint8_t)(
		MAP_DIO0_LORA_TXDONE | 
		MAP_DIO1_LORA_NOP | 
		MAP_DIO2_LORA_NOP |
		MAP_DIO3_LORA_CRC));
	
	// 9. clear all radio IRQ flags
    writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
	
	// 10. mask all IRQs but TxDone
    writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~IRQ_LORA_TXDONE_MASK);
	
	// txLora
	opmode(OPMODE_FSTX);									// set 0x01 to 0x02 (actual value becomes 0x82)
	
	// 11, 12, 13, 14. write the buffer to the FiFo
	sendPkt(payLoad, payLength);

	// 15. wait extra delay out. The delayMicroseconds timer is accurate until 16383 uSec.
	loraWait(tmst);
	
	//Set the base addres of the transmit buffer in FIFO
	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_TX_BASE_AD));	// set 0x0D to 0x0F (contains 0x80);	
	
	//For TX we have to set the PAYLOAD_LENGTH
	writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) payLength);		// set 0x22, max 0x40==64Byte long
	
	//For TX we have to set the MAX_PAYLOAD_LENGTH
	writeRegister(REG_MAX_PAYLOAD_LENGTH, (uint8_t) MAX_PAYLOAD_LENGTH);	// set 0x22, max 0x40==64Byte long
	
	// Reset the IRQ register
	writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);			// Clear the mask
	writeRegister(REG_IRQ_FLAGS, (uint8_t) IRQ_LORA_TXDONE_MASK);// set 0x12 to 0x08, clear TXDONE
	
	// 16. Initiate actual transmission of FiFo
	opmode(OPMODE_TX);											// set 0x01 to 0x03 (actual value becomes 0x83)
	
}// txLoraModem


// ----------------------------------------------------------------------------
// Setup the LoRa receiver on the connected transceiver.
// - Determine the correct transceiver type (sx1272/RFM92 or sx1276/RFM95)
// - Set the frequency to listen to (1-channel remember)
// - Set Spreading Factor (standard SF7)
// The reset RST pin might not be necessary for at least the RGM95 transceiver
//
// 1. Put the radio in LoRa mode
// 2. Put modem in sleep or in standby
// 3. Set Frequency
// 4. Spreading Factor
// 5. Set interrupt mask
// 6. Clear all interrupt flags
// 7. Set opmode to OPMODE_RX
// 8. Set _state to S_RX
// 9. Reset all interrupts
// ----------------------------------------------------------------------------

void rxLoraModem()
{
	// 1. Put system in LoRa mode
	//opmode(OPMODE_LORA);										// Is already so
	
	// 2. Put the radio in sleep mode
	opmode(OPMODE_STANDBY);									// CAD set 0x01 to 0x00
	
	// 3. Set frequency based on value in freq
	setFreq(freqs[ifreq]);										// set to 868.1MHz

	// 4. Set spreading Factor and CRC
    setRate(sf, 0x04);
	
	// prevent node to node communication
	writeRegister(REG_INVERTIQ, (uint8_t) 0x27);				// 0x33, 0x27; to reset from TX
	
	// Max Payload length is dependent on 256 byte buffer. 
	// At startup TX starts at 0x80 and RX at 0x00. RX therefore maximized at 128 Bytes
	//For TX we have to set the PAYLOAD_LENGTH
    //writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) PAYLOAD_LENGTH);	// set 0x22, 0x40==64Byte long

	// Set CRC Protection or MAX payload protection
	//writeRegister(REG_MAX_PAYLOAD_LENGTH, (uint8_t) MAX_PAYLOAD_LENGTH);	// set 0x23 to 0x80==128
	
	//Set the start address for the FiFO (Which should be 0)
	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_RX_BASE_AD));	// set 0x0D to 0x0F (contains 0x00);
	
	// Low Noise Amplifier used in receiver
	writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN);  						// 0x0C, 0x23
	
	// Accept no interrupts except RXDONE, RXTOUT en RXCRC
	writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(
		IRQ_LORA_RXDONE_MASK | 
		IRQ_LORA_RXTOUT_MASK | 
		IRQ_LORA_HEADER_MASK | 
		IRQ_LORA_CRCERR_MASK));

	// set frequency hopping
	if (_hop) {
		//writeRegister(REG_HOP_PERIOD, 0x01);					// 0x24, 0x01 was 0xFF
		writeRegister(REG_HOP_PERIOD,0x00);						// 0x24, 0x00 was 0xFF
	}
	else {
		writeRegister(REG_HOP_PERIOD,0x00);						// 0x24, 0x00 was 0xFF
	}
	// Set RXDONE interrupt to dio0
	writeRegister(REG_DIO_MAPPING_1, (uint8_t)(
			MAP_DIO0_LORA_RXDONE | 
			MAP_DIO1_LORA_RXTOUT |
			MAP_DIO2_LORA_NOP |			
			MAP_DIO3_LORA_CRC));

	// Set the opmode to either single or continuous receive. The first is used when
	// every message can come on a different SF, the second when we have fixed SF
	if (_cad) {
		// cad Scanner setup, set _state to S_RX
		// Set Single Receive Mode, goes in STANDBY mode after receipt
		_state= S_RX;
		opmode(OPMODE_RX_SINGLE);								// 0x80 | 0x06 (listen one message)
	}
	else {
		// Set Continous Receive Mode, usefull if we stay on one SF
		_state= S_RX;
		if (_hop) Serial.println(F("rxLoraModem:: ERROR continuous receive in hop mode"));
		opmode(OPMODE_RX);										// 0x80 | 0x05 (listen)
	}
	
	// 9. clear all radio IRQ flags
    writeRegister(REG_IRQ_FLAGS, 0xFF);
	
	return;
}// rxLoraModem


// ----------------------------------------------------------------------------
// function cadScanner()
//
// CAD Scanner will scan on the given channel for a valid Symbol/Preamble signal.
// So instead of receiving continuous on a given channel/sf combination
// we will wait on the given channel and scan for a preamble. Once received
// we will set the radio to the SF with best rssi (indicating reception on that sf).
// The function sets the _state to S_SCAN
// NOTE: DO not set the frequency here but use the frequency hopper
// ----------------------------------------------------------------------------
void cadScanner()
{
	// 1. Put system in LoRa mode (which destroys all other nodes(
	//opmode(OPMODE_LORA);
	
	// 2. Put the radio in sleep mode
	opmode(OPMODE_STANDBY);										// Was old value
	
	// 3. Set frequency based on value in ifreq					// XXX New, might be needed when receiving down
	setFreq(freqs[ifreq]);

	// For every time we start the scanner, we set the SF to the begin value
	//sf = SF7;													// XXX 180501 Not by default
	
	// 4. Set spreading Factor and CRC
	setRate(sf, 0x04);
	
	// listen to LORA_MAC_PREAMBLE
	writeRegister(REG_SYNC_WORD, (uint8_t) 0x34);				// set reg 0x39 to 0x34
	
	// Set the interrupts we want to listen to
	writeRegister(REG_DIO_MAPPING_1, (uint8_t)(
		MAP_DIO0_LORA_CADDONE | 
		MAP_DIO1_LORA_CADDETECT | 
		MAP_DIO2_LORA_NOP | 
		MAP_DIO3_LORA_CRC ));
	
	// Set the mask for interrupts (we do not want to listen to) except for
	writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(
		IRQ_LORA_CDDONE_MASK | 
		IRQ_LORA_CDDETD_MASK | 
		IRQ_LORA_CRCERR_MASK | 
		IRQ_LORA_HEADER_MASK));
	
	// Set the opMode to CAD
	opmode(OPMODE_CAD);

	// Clear all relevant interrupts
	//writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF );						// May work better, clear ALL interrupts
	
	// If we are here. we either might have set the SF or we have a timeout in which
	// case the receive is started just as normal.
	return;
	
}// cadScanner


// ----------------------------------------------------------------------------
// First time initialisation of the LoRa modem
// Subsequent changes to the modem state etc. done by txLoraModem or rxLoraModem
// After initialisation the modem is put in rx mode (listen)
// ----------------------------------------------------------------------------
void initLoraModem()
{
	_state = S_INIT;
#if ESP32_ARCH==1
	digitalWrite(pins.rst, LOW);
	delayMicroseconds(10000);
    digitalWrite(pins.rst, HIGH);
	delayMicroseconds(10000);
	digitalWrite(pins.ss, HIGH);
#if DUSB>=1

#endif

#else
	// Reset the transceiver chip with a pulse of 10 mSec
	digitalWrite(pins.rst, HIGH);
	delayMicroseconds(10000);
    digitalWrite(pins.rst, LOW);
	delayMicroseconds(10000);
#endif
	// 2. Set radio to sleep
	opmode(OPMODE_SLEEP);										// set register 0x01 to 0x00

	// 1 Set LoRa Mode
	opmode(OPMODE_LORA);										// set register 0x01 to 0x80
	
	// 3. Set frequency based on value in freq
	//ifreq = 0; 												// XXX 180326
	freq=freqs[ifreq];
	setFreq(freq);												// set to 868.1MHz or the last saved frequency
	
	// 4. Set spreading Factor
    setRate(sf, 0x04);
	
	// Low Noise Amplifier used in receiver
    writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN);  			// 0x0C, 0x23
#if _PIN_OUT==4
	delay(1);
#endif
    uint8_t version = readRegister(REG_VERSION);				// Read the LoRa chip version id
    if (version == 0x22) {
        // sx1272
#if DUSB>=2
        Serial.println(F("WARNING:: SX1272 detected"));
#endif
        sx1272 = true;
    } 
	
	else if (version == 0x12) {
        // sx1276?
#if DUSB>=2
            if (debug >=1) 
				Serial.println(F("SX1276 starting"));
#endif
            sx1272 = false;
	}
	else {
		// Normally this means that we connected the wrong type of board and
		// therefore specified the wrong type of wiring/pins to the software
		// Maybe this issue can be resolved of we try one of the other defined 
		// boards. (Comresult or Hallard or ...)
#if DUSB>=1
		Serial.print(F("Unknown transceiver="));
		Serial.print(version,HEX);
		Serial.print(F(", pins.rst =")); Serial.print(pins.rst);
		Serial.print(F(", pins.ss  =")); Serial.print(pins.ss);
		Serial.print(F(", pins.dio0 =")); Serial.print(pins.dio0);
		Serial.print(F(", pins.dio1 =")); Serial.print(pins.dio1);
		Serial.print(F(", pins.dio2 =")); Serial.print(pins.dio2);
		Serial.println();
		Serial.flush();
#endif
		die("");												// Maybe first try another kind of receiver
    }
	// If we are here, the chip is recognized successfully
	
	// 7. set sync word
	writeRegister(REG_SYNC_WORD, (uint8_t) 0x34);				// set 0x39 to 0x34 LORA_MAC_PREAMBLE
	
	// prevent node to node communication
	writeRegister(REG_INVERTIQ,0x27);							// 0x33, 0x27; to reset from TX
	
	// Max Payload length is dependent on 256 byte buffer. At startup TX starts at
	// 0x80 and RX at 0x00. RX therefore maximized at 128 Bytes
	writeRegister(REG_MAX_PAYLOAD_LENGTH,MAX_PAYLOAD_LENGTH);	// set 0x23 to 0x80==128 bytes
	writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH);			// 0x22, 0x40==64Byte long
	
	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_RX_BASE_AD));	// set reg 0x0D to 0x0F
	writeRegister(REG_HOP_PERIOD,0x00);							// reg 0x24, set to 0x00

	// 5. Config PA Ramp up time								// set reg 0x0A  
	writeRegister(REG_PARAMP, (readRegister(REG_PARAMP) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec
	
	// Set 0x4D PADAC for SX1276 ; XXX register is 0x5a for sx1272
	writeRegister(REG_PADAC_SX1276,  0x84); 					// set 0x4D (PADAC) to 0x84
	//writeRegister(REG_PADAC, readRegister(REG_PADAC) | 0x4);
	
	// Reset interrupt Mask, enable all interrupts
	writeRegister(REG_IRQ_FLAGS_MASK, 0x00);
	
	// 9. clear all radio IRQ flags
    writeRegister(REG_IRQ_FLAGS, 0xFF);
}// initLoraModem


// ----------------------------------------------------------------------------
// Void function startReceiver.
// This function starts the receiver loop of the LoRa service.
// It starts the LoRa modem with initLoraModem(), and then starts
// the receiver either in single message (CAD) of in continuous
// reception (STD).
// ----------------------------------------------------------------------------
void startReceiver() {
	initLoraModem();								// XXX 180326, after adapting this function 
	if (_cad) {
#if DUSB>=1
		if (( debug>=1 ) && ( pdebug & P_SCAN )) {
			Serial.println(F("S PULL:: _state set to S_SCAN"));
			if (debug>=2) Serial.flush();
		}
#endif
		_state = S_SCAN;
		sf = SF7;
		cadScanner();
	}
	else {
		_state = S_RX;
		rxLoraModem();
	}
	writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
	writeRegister(REG_IRQ_FLAGS, 0xFF);				// Reset all interrupt flags
}


// ----------------------------------------------------------------------------
// Interrupt_0 Handler.
// Both interrupts DIO0 and DIO1 are mapped on GPIO15. Se we have to look at 
// the interrupt flags to see which interrupt(s) are called.
//
// NOTE:: This method may work not as good as just using more GPIO pins on 
//  the ESP8266 mcu. But in practice it works good enough
// ----------------------------------------------------------------------------
void ICACHE_RAM_ATTR Interrupt_0()
{
	_event=1;
}


// ----------------------------------------------------------------------------
// Interrupt handler for DIO1 having High Value
// As DIO0 and DIO1 may be multiplexed on one GPIO interrupt handler
// (as we do) we have to be careful only to call the right Interrupt_x
// handler and clear the corresponding interrupts for that dio.
// NOTE: Make sure all Serial communication is only for debug level 3 and up.
// Handler for:
//		- CDDETD
//		- RXTIMEOUT
//		- (RXDONE error only)
// ----------------------------------------------------------------------------
void ICACHE_RAM_ATTR Interrupt_1()
{
	_event=1;
}

// ----------------------------------------------------------------------------
// Frequency Hopping Channel (FHSS) dio2
// ----------------------------------------------------------------------------
void ICACHE_RAM_ATTR Interrupt_2() 
{
	_event=1;
}