// 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.
// The functions implemented work in user-space so not with interrupt.
// ========================================================================================
//
//
// ----------------------------------------------------------------------------------------
// Variable definitions
//
//
// ----------------------------------------------------------------------------------------



//
// ========================================================================================
// SPI AND INTERRUPTS
// The RFM96/SX1276 communicates 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 the 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 interrupts and to further processing
// in the main loop() program.
//
// ========================================================================================


// ----------------------------------------------------------------------------------------
// Mutex definitions
//
// ----------------------------------------------------------------------------------------
#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);
	
    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
//  for example
//		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
//
//	sf is SF7 to SF12
//	CRC is 0x00 (off) or 
// ----------------------------------------------------------------------------------------

void setRate(uint8_t sf, uint8_t crc) 
{
	uint8_t mc1=0, mc2=0, mc3=0;

	if ((sf<SF7) || (sf>SF12)) {
#		if _MONITOR>=2
		if (( debug>=1 ) && ( pdebug & P_RADIO )) {
			mPrint("setRate:: SF=" + String(sf));
		}
#		endif //_MONITOR
		sf=8;
	}

	// 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 using 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
	
	// Note: Power settings for CFG_sx1272 are different
	
	return;
}



// ----------------------------------------------------------------------------------------
// 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 gwayConfig.ch index like so: freqs[gwayConfig.ch] 
// ----------------------------------------------------------------------------------------
void hop() 
{

	// 1. Set radio to standby
	opmode(OPMODE_STANDBY);
		
	// 3. Set frequency based on value in freq		
	gwayConfig.ch = (gwayConfig.ch + 1) % NUM_HOPS ;			// Increment the freq round robin
	setFreq(freqs[gwayConfig.ch].upFreq);
	
	// 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
	
	// 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 _MONITOR>=1
	if (( debug>=2 ) && ( pdebug & P_RADIO )){
			String response = "hop:: hopTime:: " + String(micros() - hopTime);
			mStat(0, response);
			mPrint(response);
	}
#	endif //_MONITOR
	// Remember the last time we hop
	hopTime = micros();									// At what time did we hop
}
	

// ------------------------------------- UP -----------------------------------------------
//
// This LoRa function reads a message from the LoRa transceiver
// on Success: returns message length read when message correctly received.
// on Failure: it returns a negative value on error (CRC error for example).
// This is the "lowlevel" receive function called by stateMachine()
// dealing with the radio specific LoRa functions.
//
// |         | CR = 4/8  | CR= Coding Rate      |             |
// |Preamble |Header| CRC| Payload              | Payload CRC |
// |---------|------|----|----------------------|-------------|
//
// The function deals with Explicit Header Mode. Implicit header mode is only
// valid for SF6 and only for Uplink so it does not work for LoraWAN.
//
// Parameters:
//		Payload: uint8_t[] message. when message is read it is returned in payload.
// Returns:
//		Length of payload received
//
// 9 bytes header (Explicit Header mode only)
//	Code Rate is 4/8 for Header, might be different for Payload content
//
//	1: Payload Length (in bytes)
//	2: Forward Error Correction Rate
//	3: Optional 16 Bit (2 Byte) PHDR_CRC for the PHDR (Physical Header)
//
// followed by data N bytes Payload (Max 255)
//
// 4 bytes MIC end 
//
// ----------------------------------------------------------------------------------------
uint8_t receivePkt(uint8_t *payload)
{

    statc.msg_ttl++;													// Receive statistics counter

    uint8_t irqflags = readRegister(REG_IRQ_FLAGS);						// 0x12; read back flags											
	uint8_t crcUsed = readRegister(REG_HOP_CHANNEL);					// Is CRC used? (Register 0x1C)
	if (crcUsed & 0x40) {
#		if _DUSB>=1
		if (( debug>=2) && (pdebug & P_RX )) {
			mPrint("R rxPkt:: CRC used");
		}
#		endif //_DUSB
	}
	
    //  Check for payload IRQ_LORA_CRCERR_MASK=0x20 set
    if (irqflags & IRQ_LORA_CRCERR_MASK)								// Is CRC error?
    {
#		if _MONITOR>=1
        if (( debug>=0) && ( pdebug & P_RADIO )) {
			String response=("rxPkt:: Err CRC, t=");
			stringTime(now(), response);
			mPrint(response);
		}
#		endif //_MONITOR
		return 0;
    }
	
	// Is header OK?
	// Please note that if we reset the HEADER interrupt in RX,
	// that we would here conclude that there is no HEADER
	else if ((irqflags & IRQ_LORA_HEADER_MASK) == false)				// Header not ok?
    {
#		if _MONITOR>=1
			if (( debug>=0) && ( pdebug & P_RADIO )) {
				mPrint("rxPkt:: Err HEADER");
			}
#		endif //_MONITOR
		// 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 {
        statc.msg_ok++;													// Receive OK statistics counter
		switch(statr[0].ch) {
			case 0: statc.msg_ok_0++; break;
			case 1: statc.msg_ok_1++; break;
			case 2: statc.msg_ok_2++; break;
		}

		if (readRegister(REG_FIFO_RX_CURRENT_ADDR) != readRegister(REG_FIFO_RX_BASE_AD)) {
#if			_MONITOR>=1
			if (( debug>=1 ) && ( pdebug & P_RADIO )) {
				mPrint("RX BASE <" + String(readRegister(REG_FIFO_RX_BASE_AD)) + "> != RX CURRENT <" + String(readRegister(REG_FIFO_RX_CURRENT_ADDR)) + ">"	);
			}
#			endif //_MONITOR
		}
		
        //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 _MONITOR>=1
			if ((debug>=1) && (currentAddr > 64)) {						// More than 64 read?
				mPrint("rxPkt:: ERROR Rx addr>64"+String(currentAddr));
			}
#		endif //_MONITOR
        writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) currentAddr);		// 0x0D 

		if (receivedCount > PAYLOAD_LENGTH) {
#			if _MONITOR>=1
				if (( debug>=0 ) & ( pdebug & P_RADIO )) {
					mPrint("rxPkt:: ERROR Payliad receivedCount="+String(receivedCount));
				}
#			endif //_MONITOR
			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
		
		// As long as _MONITOR is enabled, and P_RX debug messages are selected,
		// the received packet is displayed on the output.
#		if _MONITOR>=1
		if ((debug>=1) && (pdebug & P_RX)){
		
			String response = "UP receivePkt:: rxPkt: t=";
			stringTime(now(), response);
			response += ", f=" + String(gwayConfig.ch) + ", sf=" + String(sf);
			
			response += ", a=";
			uint8_t DevAddr [4];
					DevAddr[0] = payload[4];
					DevAddr[1] = payload[3];
					DevAddr[2] = payload[2];
					DevAddr[3] = payload[1];
			printHex((IPAddress)DevAddr, ':', response);
			
			response += ", flags=" + String(irqflags,HEX);
			response += ", addr=" + String(currentAddr);
			response += ", len=" + String(receivedCount);

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

#			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(", ERRRO No Index");
					return(receivedCount);
				}	

				// ------------------------------
				
				Serial.print(F(", data="));

				for (int i=0; i<receivedCount; i++) { 			// Copy array
					data[i] = payload[i]; 
				}

				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
			
			mPrint(response);							// Print response for Serial or mPrint
		}
#		endif //MONITOR
		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 UP
	
	
	
// ------------------------------ DOWN ----------------------------------------------------
//
// This DOWN function sends a payload to the LoRa node over the air
// Radio must go back in standby mode as soon as the transmission is finished
// 
// NOTE:: writeRegister functions should not be used outside interrupts
// ----------------------------------------------------------------------------------------
bool sendPkt(uint8_t *payLoad, uint8_t payLength)
{
#	if _MONITOR>=2
	if (payLength>=128) {
		if (debug>=1) {
			mPrint("sendPkt Dwn:: len="+String(payLength));
		}
		return false;
	}
#	endif //_MONITOR
	
	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;											// terminate buffer
	writeBuffer(REG_FIFO, (uint8_t *) payLoad, payLength);
	
	return true;
} // sendPkt()


// ------------------------------------------ DOWN ----------------------------------------
// loraWait()
// This function implements the wait protocol needed for downstream transmissions.
// Note: Timing of downstream and JoinAccept messages is VERY critical.
//
// 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. (!!!)
// so it contains the local Gateway time as a reference when to start Downlink.
// Note: We assume LoraDown->sfTx contains the SF we will use for downstream message.
//		gwayConfig.txDelay is the delay as specified in the GUI
// ----------------------------------------------------------------------------------------

void loraWait(struct LoraDown *LoraDown)
{
	int32_t delayTmst = (int32_t)(LoraDown->tmst - micros()) + gwayConfig.txDelay;
												// delayTmst based on txDelay and spreading factor
	
	if ((delayTmst > 8000000) || (delayTmst < 0)) {					// Delay is  > 8 secs
#		if _MONITOR>=1
			String response= "Dwn loraWait:: ERROR: ";
			printDwn(LoraDown,response);
			mPrint(response);
#		endif // _MONITOR
		gwayConfig.waitErr++;
		return;
	}

	// For larger delay times we use delay() since that is for > 15ms
	// This is the most efficient way.
	// MMM Check for huge wait times
	while (delayTmst > 15000) {
		delay(15);										// ms delay including yield, slightly shorter
//		delayMicroseconds(15000);						// ms delay including yield, slightly shorter
		delayTmst -= 15000;
	}
	
	// The remaining wait time is less than 15000 uSecs
	// but more than 1000 mSecs (see above)
	// therefore we use delayMicroseconds() to wait
	delayMicroseconds(delayTmst);

	gwayConfig.waitOk++;
	return;
}


// -------------------------------------- DOWN --------------------------------------------
// 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
//
// 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_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(LoraDown->sfTx, LoraDown->crc);
	
	// Frequency hopping
	//writeRegister(REG_HOP_PERIOD, (uint8_t) 0x00);		// set 0x24 to 0x00 only for receivers
	
	// 4. Init Frequency, config channel
	setFreq(LoraDown->freq);

	// 6. Set power level, REG_PAC
	setPow(LoraDown->powe);
	
	// 7. prevent node to node communication
	writeRegister(REG_INVERTIQ, (uint8_t) (LoraDown->iiq));	// 0x33, (0x27 or 0x40)
	
	// 8. set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP (or less for 1ch gateway)
    writeRegister(REG_DIO_MAPPING_1, (uint8_t)(
		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(LoraDown->payLoad, LoraDown->payLength);
	
	//Set the base addres of the transmit buffer in FIFO
	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_TX_BASE_AD));	// set 0x0D to 0x0F (contains 0x80);	
	
	//For TX we have to set the PAYLOAD_LENGTH
	writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) LoraDown->payLength);		// set 0x22, max 0x40==64Byte long
	
	//For TX we have to set the MAX_PAYLOAD_LENGTH
	writeRegister(REG_MAX_PAYLOAD_LENGTH, (uint8_t) MAX_PAYLOAD_LENGTH);	// set 0x22, max 0x40==64Byte long
	
	// Reset the IRQ register
	writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);			// Clear the mask
//	writeRegister(REG_IRQ_FLAGS, (uint8_t) IRQ_LORA_TXDONE_MASK);// set 0x12 to 0x08, clear TXDONE
	writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);// set 0x12 to 0xFF, clear TXDONE and others
	
	// 16. Initiate actual transmission of FiFo
	opmode(OPMODE_TX);											// set 0x01 to 0x03 (actual value becomes 0x83)

#	if _MONITOR>=1
	if (( debug>=1 ) && ( pdebug & P_TX )) {
		String response = "Dwn txLoraModem:: end: ";
		printDwn(LoraDown, response);
		mPrint(response);
	}
#	endif //_MONITOR

}// txLoraModem


// ----------------------------------------------------------------------------------------
// 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 RFM95 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. 
// 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[gwayConfig.ch].upFreq);						// set to the right frequency

	// 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 for 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
	
	// 5. 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 (gwayConfig.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));

	// 7+8.  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 (gwayConfig.cad) {
		// cad Scanner setup, set _state to S_RX
		// Set Single Receive Mode, and go in STANDBY mode after receipt
		_state= S_RX;											// 8.
		opmode(OPMODE_RX_SINGLE);								// 7. 0x80 | 0x06 (listen one message)
	}
	else {
		// Set Continous Receive Mode, useful if we stay on one SF
		_state= S_RX;											// 8.
#		if _DUSB>=1
		if (gwayConfig.hop) {
			Serial.println(F("rxLoraModem:: ERROR continuous receive in hop mode"));
		}
#		endif
		opmode(OPMODE_RX);										// 7. 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 modes)
	//opmode(OPMODE_LORA);
	
	// 2. Put the radio in sleep mode
	opmode(OPMODE_STANDBY);										// Was old value
	
	// 3. Set frequency based on value in gwayConfig.ch			// might be needed when receiving down 
	setFreq(freqs[gwayConfig.ch].upFreq);						// set to the right frequency

	// 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)
//	1.	Set Radio to sleep
//	2.	Set opmode to LoRa
//	3.	Set Frequency
//	4.	Set rate and Spreading Factor
//	5.	Set chip version
//	6.	Set SYNC word
//	7.	Set ranp-up time
//	8.	Set interrupt masks
//	9.	Clear INT flags
// ----------------------------------------------------------------------------------------
void initLoraModem()
{
	_state = S_INIT;
#if defined(ESP32_ARCH)
	digitalWrite(pins.rst, LOW);
	delayMicroseconds(10000);
    digitalWrite(pins.rst, HIGH);
	delayMicroseconds(10000);
	digitalWrite(pins.ss, HIGH);
#else
	// Reset the transceiver chip with a pulse of 10 mSec
	digitalWrite(pins.rst, HIGH);
	delayMicroseconds(10000);
    digitalWrite(pins.rst, LOW);
	delayMicroseconds(10000);
#endif
	// 1. Set radio to sleep
	opmode(OPMODE_SLEEP);										// set register 0x01 to 0x00

	// 2 Set LoRa Mode
	opmode(OPMODE_LORA);										// set register 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);
	
	// Low Noise Amplifier used in receiver
    writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN);  			// 0x0C, 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
#		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
	
	// 6. 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

	// 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
	//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);
}// 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 (gwayConfig.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;
}


