Delete _stateMachine.ino
This commit is contained in:
parent
c32376c2ea
commit
27a95b15cf
|
@ -1,950 +0,0 @@
|
|||
// 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 state machine code enabling to receive
|
||||
// and transmit packages/messages.
|
||||
// ============================================================================================
|
||||
//
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// stateMachine handler of the state machine.
|
||||
// We use ONE state machine for all kind of interrupts. This assures that we take
|
||||
// the correct action upon receiving an interrupt.
|
||||
//
|
||||
// _event is the software interrupt: If set this function is executed from loop(),
|
||||
// the function should itself take care of setting or resetting the variable.
|
||||
//
|
||||
// STATE MACHINE
|
||||
// The program uses the following state machine (in _state), all states
|
||||
// are done in interrupt routine, only the follow-up of S-RXDONE is done
|
||||
// in the main loop() program. This is because otherwise the interrupt processing
|
||||
// would take too long to finish
|
||||
//
|
||||
// So _state has one of the following state values:
|
||||
//
|
||||
// S-INIT=0, The commands in this state are executed only once
|
||||
// - Goto S_SCAN
|
||||
//
|
||||
// S-SCAN, CadScanner() part
|
||||
// - Upon CDDECT (int1) goto S_RX,
|
||||
// - upon CDDONE (int0) goto S_CAD, walk through all SF until CDDETD
|
||||
// - Else stay in SCAN state
|
||||
//
|
||||
// S-CAD,
|
||||
// - Upon CDDECT (int1) goto S_RX,
|
||||
// - Upon CDDONE (int0) goto S_SCAN, start with SF7 recognition again
|
||||
//
|
||||
// S-RX, Received CDDECT so message detected, RX cycle started.
|
||||
// - Upon RXDONE (int0) package read. If read finished continue to read message, then goto S_SCAN
|
||||
// - upon RXTOUT (int1) error, goto S_SCAN
|
||||
//
|
||||
// S-TX Transmitting a message
|
||||
// - When transmission done, goto TXDONE
|
||||
//
|
||||
// S-TXDONE Transmission complete by loop() now again in interrupt
|
||||
// - Set the Mask
|
||||
// - reset the Flags
|
||||
// - Goto either SCAN or RX
|
||||
//
|
||||
// This interrupt routine has been kept as simple and short as possible.
|
||||
// If we receive an interrupt that does not belong to a _state then print error.
|
||||
// _event is a special variable which indicate that an interrupt event has happened
|
||||
// and we need to take action OR that we generate a soft interrupt for state machine.
|
||||
//
|
||||
// NOTE: We may clear the interrupt but leave the flag for the moment.
|
||||
// The eventHandler should take care of repairing flags between interrupts.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
void stateMachine()
|
||||
{
|
||||
// Determine what interrupt flags are set
|
||||
//
|
||||
uint8_t flags = readRegister(REG_IRQ_FLAGS);
|
||||
uint8_t mask = readRegister(REG_IRQ_FLAGS_MASK);
|
||||
uint8_t intr = flags & ( ~ mask ); // Only react on non masked interrupts
|
||||
uint8_t rssi;
|
||||
_event=0; // Reset the interrupt detector
|
||||
|
||||
# if _MONITOR>=1
|
||||
if (intr != flags) {
|
||||
String response = "stateMachine:: Error: flags="+String(flags,HEX)+", ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
|
||||
// If Hopping is selected AND if there is NO event interrupt detected
|
||||
// and the state machine is called anyway
|
||||
// then we know its a soft interrupt and we do nothing and return to main loop.
|
||||
//
|
||||
if ((gwayConfig.hop) && (intr == 0x00))
|
||||
{
|
||||
// eventWait is the time since we have had a CDDETD event (preamble detected).
|
||||
// If we are not in scanning state, and there will be an interrupt coming,
|
||||
// In state S_RX it could be RXDONE in which case allow kernel time
|
||||
//
|
||||
if ((_state == S_SCAN) || (_state == S_CAD)) {
|
||||
|
||||
_event=0;
|
||||
|
||||
uint32_t eventWait = EVENT_WAIT;
|
||||
switch (_state) {
|
||||
case S_INIT: eventWait = 0; break;
|
||||
// Next two are most important
|
||||
case S_SCAN: eventWait = EVENT_WAIT * 1; break;
|
||||
case S_CAD: eventWait = EVENT_WAIT * 1; break;
|
||||
|
||||
case S_RX: eventWait = EVENT_WAIT * 8; break;
|
||||
case S_TX: eventWait = EVENT_WAIT * 1; break;
|
||||
case S_TXDONE: eventWait = EVENT_WAIT * 4; break;
|
||||
default:
|
||||
eventWait=0;
|
||||
# if _MONITOR>=1
|
||||
String response = "StateMachine:: Default: ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
# endif //_MONITOR
|
||||
}
|
||||
|
||||
// doneWait is the time that we received CDDONE interrupt
|
||||
// So we init the wait time for RXDONE based on the current SF.
|
||||
// As for highter CF it takes longer to receive symbols
|
||||
// Assume symbols in SF8 take twice the time of SF7
|
||||
//
|
||||
uint32_t doneWait = DONE_WAIT; // Initial value
|
||||
switch (sf) {
|
||||
case SF7: break;
|
||||
case SF8: doneWait *= 2; break;
|
||||
case SF9: doneWait *= 4; break;
|
||||
case SF10: doneWait *= 8; break;
|
||||
case SF11: doneWait *= 16; break;
|
||||
case SF12: doneWait *= 32; break;
|
||||
default:
|
||||
doneWait *= 1;
|
||||
# if _MONITOR>=1
|
||||
if ((debug>=0) && (pdebug & P_PRE)) {
|
||||
mPrint("StateMachine:: PRE: DEF set");
|
||||
}
|
||||
# endif //_MONITOR
|
||||
break;
|
||||
}
|
||||
|
||||
// If micros is starting over again after 51 minutes
|
||||
// it's value is smaller than an earlier value of eventTime/doneTime
|
||||
//
|
||||
if (eventTime > micros()) eventTime=micros();
|
||||
if (doneTime > micros()) doneTime=micros();
|
||||
|
||||
if (((micros() - doneTime) > doneWait) &&
|
||||
((_state == S_SCAN) || (_state == S_CAD)))
|
||||
{
|
||||
_state = S_SCAN;
|
||||
hop(); // increment gwayConfig.ch = (gwayConfig.ch + 1) % NUM_HOPS ;
|
||||
cadScanner(); // Reset to initial SF, leave frequency "freqs[gwayConfig.ch]"
|
||||
# if _MONITOR>=1
|
||||
if ((debug >= 1) && (pdebug & P_PRE)) {
|
||||
String response = "DONE :: ";
|
||||
mStat(intr, response);
|
||||
mPrint(response); // Can move down for timing reasons
|
||||
}
|
||||
# endif //_MONITOR
|
||||
eventTime=micros(); // reset the timer on timeout
|
||||
doneTime=micros(); // reset the timer on timeout
|
||||
return;
|
||||
}
|
||||
// If timeout occurs and still no _event, then hop
|
||||
// and start scanning again
|
||||
//
|
||||
if ((micros() - eventTime) > eventWait )
|
||||
{
|
||||
_state = S_SCAN;
|
||||
hop(); // gwayConfig.ch= (gwayConfig.ch+1)%NUM_HOPS ;
|
||||
cadScanner(); // Reset to initial SF, leave "freqs[gwayConfig.ch]"
|
||||
# if _MONITOR>=1
|
||||
if ((debug >= 2) && (pdebug & P_PRE)) {
|
||||
String response = "HOP :: ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
eventTime=micros(); // reset the eventtimer on timeout
|
||||
doneTime=micros(); // reset the timer on timeout
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// If we are here, NO timeout has occurred
|
||||
// So we need to return to the main State Machine
|
||||
// as there was NO interrupt
|
||||
# if _MONITOR>=1
|
||||
if ((debug>=3) && (pdebug & P_PRE)) {
|
||||
String response = "PRE:: eventTime=";
|
||||
response += String(eventTime);
|
||||
response += ", micros=";
|
||||
response += String(micros());
|
||||
response += ": ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
} // if SCAN or CAD
|
||||
|
||||
// else, S_RX of S_TX for example
|
||||
else {
|
||||
//
|
||||
} // else S_RX or S_TX, TXDONE
|
||||
|
||||
yield(); // if hopping is enabled
|
||||
|
||||
}// intr==0 && gwayConfig.hop
|
||||
|
||||
|
||||
|
||||
// ========================================================================================
|
||||
// This is the actual state machine of the gateway
|
||||
// and its next actions are depending on the state we are in.
|
||||
// For hop situations we do not get interrupts, so we have to
|
||||
// simulate and generate events ourselves.
|
||||
//
|
||||
switch (_state)
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// If the state is init, we are starting up.
|
||||
// The initLoraModem() function is already called in setup();
|
||||
//
|
||||
case S_INIT:
|
||||
# if _MONITOR>=1
|
||||
if ((debug>=1) && (pdebug & P_PRE)) {
|
||||
mPrint("S_INIT");
|
||||
}
|
||||
# endif //_MONITOR
|
||||
// new state, needed to startup the radio (to S_SCAN)
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF ); // Clear ALL interrupts
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00 ); // Clear ALL interrupts
|
||||
_event=0;
|
||||
break;
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// In S_SCAN we measure a high RSSI this means that there (probably) is a message
|
||||
// coming in at that freq. But not necessarily on the current SF.
|
||||
// If so find the right SF with CDDETD.
|
||||
//
|
||||
case S_SCAN:
|
||||
//
|
||||
// Intr==IRQ_LORA_CDDETD_MASK
|
||||
// We detected a message on this frequency and SF when scanning
|
||||
// We clear both CDDETD and swich to reading state to read the message
|
||||
//
|
||||
if (intr & IRQ_LORA_CDDETD_MASK) {
|
||||
|
||||
_state = S_RX; // Set state to receiving
|
||||
|
||||
// Set RXDONE interrupt to dio0, RXTOUT to dio1
|
||||
writeRegister(REG_DIO_MAPPING_1, (
|
||||
MAP_DIO0_LORA_RXDONE |
|
||||
MAP_DIO1_LORA_RXTOUT |
|
||||
MAP_DIO2_LORA_NOP |
|
||||
MAP_DIO3_LORA_CRC));
|
||||
|
||||
// Since new state is S_RX, accept no interrupts except RXDONE or RXTOUT
|
||||
// HEADER and CRCERR
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(
|
||||
IRQ_LORA_RXDONE_MASK |
|
||||
IRQ_LORA_RXTOUT_MASK |
|
||||
IRQ_LORA_HEADER_MASK |
|
||||
IRQ_LORA_CRCERR_MASK));
|
||||
|
||||
// Starting with version 5.0.1 the waittime is dependent on the SF
|
||||
// So for SF12 we wait longer (2^7 == 128 uSec) and for SF7 4 uSec.
|
||||
//delayMicroseconds( (0x01 << ((uint8_t)sf - 5 )) );
|
||||
//if (gwayConfig.cad) // XXX 180520 make sure we start reading asap in hop
|
||||
// delayMicroseconds( RSSI_WAIT ); // Wait some microseconds less
|
||||
|
||||
rssi = readRegister(REG_RSSI); // Read the RSSI
|
||||
_rssi = rssi; // Read the RSSI in the state variable
|
||||
|
||||
_event=0; // Make 0, as soon as we have an interrupt
|
||||
detTime=micros(); // mark time that preamble detected
|
||||
|
||||
# if _MONITOR>=1
|
||||
if ((debug>=1) && (pdebug & P_PRE)) {
|
||||
String response = "SCAN:: ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset all interrupt flags
|
||||
opmode(OPMODE_RX_SINGLE); // set reg 0x01 to 0x06 for receiving
|
||||
|
||||
}//if
|
||||
|
||||
// CDDONE
|
||||
// We received a CDDONE int telling us that we received a message on this
|
||||
// frequency and possibly on one of its SF. Only when the incoming message
|
||||
// matches the SF then also CDDETD is raised.
|
||||
// If so, we switch to CAD state where we will wait for CDDETD event.
|
||||
//
|
||||
else if (intr & IRQ_LORA_CDDONE_MASK) {
|
||||
|
||||
opmode(OPMODE_CAD);
|
||||
rssi = readRegister(REG_RSSI); // Read the RSSI
|
||||
|
||||
# if _MONITOR>=1
|
||||
if ((debug>=2) && (pdebug & P_SCAN)) {
|
||||
String response = "SCAN:: CDDONE: ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
// We choose the generic RSSI as a sorting mechanism for packages/messages
|
||||
// The pRSSI (package RSSI) is calculated upon successful reception of message
|
||||
// So we expect that this value makes little sense for the moment with CDDONE.
|
||||
// Set the rssi as low as the noise floor. Lower values are not recognized then.
|
||||
// Every cycle starts with gwayConfig.ch==0 and sf=SF7 (or the set init SF)
|
||||
//
|
||||
if (rssi > (RSSI_LIMIT - (gwayConfig.hop * 7))) // Is set to 35, or 29 for HOP
|
||||
{
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=2 ) && ( pdebug & P_SCAN )) {
|
||||
String response = "SCAN:: -> CAD: ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
_state = S_CAD; // promote to next level
|
||||
_event=0;
|
||||
}
|
||||
|
||||
// If the RSSI is not big enough we skip the CDDONE
|
||||
// and go back to scanning
|
||||
else {
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=2 ) && ( pdebug & P_SCAN )) {
|
||||
String response = "SCAN:: rssi=";
|
||||
response += String(rssi);
|
||||
response += ": ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
_state = S_SCAN;
|
||||
}
|
||||
|
||||
// Clear the CADDONE flag
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
|
||||
doneTime = micros(); // Need CDDONE or other intr to reset timeout
|
||||
|
||||
}//SCAN CDDONE
|
||||
|
||||
// So if we are here then we are in S_SCAN and the interrupt is not
|
||||
// CDDECT or CDDONE. it is probably soft interrupt _event==1
|
||||
// So if gwayConfig.hop we change the frequency and restart the
|
||||
// interrupt in order to check for CDONE on other frequencies
|
||||
// if gwayConfig.hop we start at the next frequency, hop () sets the sf to SF7.
|
||||
// If we are at the end of all frequencies, reset frequencies and sf
|
||||
// and go to S_SCAN state.
|
||||
//
|
||||
// Note:: We should make sure that all frequencies are scanned in a row
|
||||
// and when we switch to gwayConfig.ch==0 we should stop for a while
|
||||
// to allow system processing.
|
||||
// We should make sure that we enable webserver etc every once in a while.
|
||||
// We do this by changing _event to 1 in loop() only for gwayConfig.hop and
|
||||
// use _event=0 for non hop.
|
||||
//
|
||||
else if (intr == 0x00)
|
||||
{
|
||||
_event=0; // XXX 26/12/2017 !!! NEED
|
||||
}
|
||||
|
||||
// Unkown Interrupt, so we have an error
|
||||
//
|
||||
else {
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=0 ) && ( pdebug & P_SCAN )) {
|
||||
String response = "SCAN unknown:: ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
_state=S_SCAN;
|
||||
//_event=1; // XXX 19/06/03 loop until interrupt
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
|
||||
}
|
||||
|
||||
break; // S_SCAN
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// S_CAD: In CAD mode we scan every SF for high RSSI until we have a DETECT.
|
||||
// Reason is the we received a CADDONE interrupt so we know a message is received
|
||||
// on the frequency but may be on another SF.
|
||||
//
|
||||
// If message is of the right frequency and SF, IRQ_LORA_CDDETD_MSAK interrupt
|
||||
// is raised, indicating that we can start beging reading the message from SPI.
|
||||
//
|
||||
// DIO0 interrupt IRQ_LORA_CDDONE_MASK in state S_CAD==2 means that we might have
|
||||
// a lock on the Freq but not the right SF. So we increase the SF
|
||||
//
|
||||
case S_CAD:
|
||||
|
||||
// Intr=IRQ_LORA_CDDETD_MASK
|
||||
// We have to set the sf based on a strong RSSI for this channel
|
||||
// Also we set the state to S_RX and start receiving the message
|
||||
//
|
||||
if (intr & IRQ_LORA_CDDETD_MASK) {
|
||||
|
||||
// Set RXDONE interrupt to dio0, RXTOUT to dio1
|
||||
writeRegister(REG_DIO_MAPPING_1, (
|
||||
MAP_DIO0_LORA_RXDONE |
|
||||
MAP_DIO1_LORA_RXTOUT |
|
||||
MAP_DIO2_LORA_NOP |
|
||||
MAP_DIO3_LORA_CRC ));
|
||||
|
||||
// Accept no interrupts except RXDONE or RXTOUT
|
||||
_event=0;
|
||||
|
||||
// if CDECT, make state S_RX so we wait for RXDONE intr
|
||||
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(
|
||||
IRQ_LORA_RXDONE_MASK |
|
||||
IRQ_LORA_RXTOUT_MASK |
|
||||
IRQ_LORA_HEADER_MASK |
|
||||
IRQ_LORA_CRCERR_MASK ));
|
||||
|
||||
// Reset all interrupts as soon as possible
|
||||
// But listen ONLY to RXDONE and RXTOUT interrupts
|
||||
//writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDETD_MASK | IRQ_LORA_RXDONE_MASK);
|
||||
|
||||
// If we want to reset CRC, HEADER and RXTOUT flags as well
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF ); // XXX 180326, reset all CAD Detect interrupt flags
|
||||
|
||||
//_state = S_RX; // XXX 180521 Set state to start receiving
|
||||
opmode(OPMODE_RX_SINGLE); // set reg 0x01 to 0x06, initiate READ
|
||||
|
||||
delayMicroseconds( RSSI_WAIT ); // Wait some microseconds less
|
||||
//delayMicroseconds( (0x01 << ((uint8_t)sf - 5 )) );
|
||||
rssi = readRegister(REG_RSSI); // Read the RSSI
|
||||
_rssi = rssi; // Read the RSSI in the state variable
|
||||
|
||||
detTime = micros();
|
||||
# if _MONITOR>=1
|
||||
if ((debug>=1) && (pdebug & P_CAD)) {
|
||||
String response = "CAD:: ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
_state = S_RX; // Set state to start receiving
|
||||
|
||||
}// CDDETD
|
||||
|
||||
// Intr == CADDONE
|
||||
// So we scan this SF and if not high enough ... next
|
||||
//
|
||||
else if (intr & IRQ_LORA_CDDONE_MASK) {
|
||||
// If this is not the max SF, increment the SF and try again
|
||||
// Depending on the frequency scheme this is for example SF8, SF10 or SF12
|
||||
// We expect on other SF get CDDETD
|
||||
//
|
||||
if (((uint8_t)sf) < freqs[gwayConfig.ch].upHi) {
|
||||
|
||||
sf = (sf_t)((uint8_t)sf+1); // Increment sf
|
||||
setRate(sf, 0x04); // Set SF with CRC==on
|
||||
|
||||
// reset interrupt flags for CAD Done
|
||||
_event=0; // XXX 180324, when increasing SF loop, ws 0x00
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask
|
||||
//writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDONE_MASK | IRQ_LORA_CDDETD_MASK);
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF ); // This will prevent the CDDETD from being read
|
||||
|
||||
opmode(OPMODE_CAD); // Scanning mode
|
||||
|
||||
delayMicroseconds(RSSI_WAIT);
|
||||
rssi = readRegister(REG_RSSI); // Read the RSSI
|
||||
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=3 ) && ( pdebug & P_CAD )) {
|
||||
mPrint("S_CAD:: CDONE, SF=" + String(sf) );
|
||||
}
|
||||
# endif //_MONITOR
|
||||
}
|
||||
|
||||
// If we reach the highest SF for the frequency plan,
|
||||
// we should go back to SCAN state
|
||||
//
|
||||
else {
|
||||
|
||||
// Reset Interrupts
|
||||
_event=1; // reset soft intr, to state machine again
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF ); // or IRQ_LORA_CDDONE_MASK
|
||||
|
||||
_state = S_SCAN; // As soon as we reach SF12 do something
|
||||
sf = (sf_t) freqs[gwayConfig.ch].upLo;
|
||||
cadScanner(); // Which will reset SF to lowest SF
|
||||
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=3 ) && ( pdebug & P_CAD )) {
|
||||
mPrint("CAD->SCAN:: " + String(intr) );
|
||||
}
|
||||
# endif //_MONITOR
|
||||
}
|
||||
doneTime = micros(); // We need CDDONE or other intr to reset timeout
|
||||
|
||||
} //CAD CDDONE
|
||||
|
||||
// if this interrupt is not CDECT or CDDONE then probably is 0x00
|
||||
// This means _event was set but there was no real interrupt (yet).
|
||||
// So we clear _event and wait for next (soft) interrupt.
|
||||
// We stay in the CAD state because CDDONE means something is
|
||||
// coming on this frequency so we wait on CDECT.
|
||||
//
|
||||
else if (intr == 0x00) {
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=3 ) && ( pdebug & P_CAD )) {
|
||||
mPrint ("CAD:: intr is 0x00");
|
||||
}
|
||||
# endif //_MONITOR
|
||||
//_event=1; // Stay in CAD _state until real interrupt
|
||||
}
|
||||
|
||||
// else we do not recognize the interrupt. We print an error
|
||||
// and restart scanning. If hop we start at gwayConfig.ch==1
|
||||
//
|
||||
else {
|
||||
# if _MONITOR>=1
|
||||
if ( debug>=0) {
|
||||
mPrint("ERROR CAD: Unknown::" + String(intr) );
|
||||
}
|
||||
# endif //_MONITOR
|
||||
_state = S_SCAN;
|
||||
sf = SF7;
|
||||
cadScanner(); // Scan and set SF7
|
||||
|
||||
// Reset Interrupts
|
||||
_event=1; // If unknown interrupt, restarts
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset all interrupts
|
||||
|
||||
}
|
||||
break; //S_CAD
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// If we receive an RXDONE interrupt on dio0 with state==S_RX
|
||||
// So we should handle the received message
|
||||
// Else if it is RXTOUT interrupt
|
||||
// Timeout, so we handle this interrupt, and get modem out of standby
|
||||
// Else
|
||||
// Go back to SCAN
|
||||
//
|
||||
case S_RX:
|
||||
|
||||
if (intr & IRQ_LORA_RXDONE_MASK) {
|
||||
|
||||
# if _CRCCHECK>=1
|
||||
// We have to check for CRC error which will be visible AFTER RXDONE is set.
|
||||
// CRC errors might indicate that the reception is not OK.
|
||||
// Could be CRC error or message too large.
|
||||
// CRC error checking requires DIO3
|
||||
//
|
||||
if (intr & IRQ_LORA_CRCERR_MASK) {
|
||||
# if _MONITOR>=1
|
||||
if ((debug>=0) && (pdebug & P_RX)) {
|
||||
String response = "UP CRC ERROR:: ";
|
||||
mStat(intr, response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
|
||||
if ((gwayConfig.cad) || (gwayConfig.hop)) {
|
||||
sf = SF7;
|
||||
_state = S_SCAN;
|
||||
cadScanner();
|
||||
}
|
||||
else {
|
||||
_state = S_RX;
|
||||
rxLoraModem();
|
||||
}
|
||||
|
||||
// Reset interrupts
|
||||
_event=0; // CRC error
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t)(
|
||||
IRQ_LORA_RXDONE_MASK |
|
||||
IRQ_LORA_RXTOUT_MASK |
|
||||
IRQ_LORA_HEADER_MASK |
|
||||
IRQ_LORA_CRCERR_MASK ));
|
||||
|
||||
break; // Get out of loop
|
||||
}// RX-CRC mask
|
||||
# endif //_CRCCHECK
|
||||
|
||||
|
||||
// If we are here, no CRC error occurred, start timer
|
||||
# if _DUSB>=1 || _MONITOR>=1
|
||||
uint32_t rxDoneTime = micros();
|
||||
# endif
|
||||
|
||||
// There should not be an error in the message
|
||||
LoraUp.payLoad[0]= 0x00; // Empty the message
|
||||
|
||||
// If receive S_RX error,
|
||||
// - print Error message
|
||||
// - Set _state to SCAN
|
||||
// - Set _event=1 so that we loop until we have an interrupt
|
||||
// - Reset the interrupts
|
||||
// - break
|
||||
// NOTE: receivePacket also increases .ok0 - .ok2 counter
|
||||
|
||||
if((LoraUp.payLength = receivePkt(LoraUp.payLoad)) <= 0) {
|
||||
# if _MONITOR>=1
|
||||
if ((debug>=0) && (pdebug & P_RX)) {
|
||||
String response = "sMachine:: ERROR S-RX: payLength=";
|
||||
response += String(LoraUp.payLength);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
|
||||
_event=1;
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
|
||||
|
||||
_state = S_SCAN;
|
||||
break;
|
||||
}
|
||||
|
||||
# if _MONITOR>=1
|
||||
if ((debug >=2) && (pdebug & P_RX)) {
|
||||
String response = "RXDONE:: dT=";
|
||||
response += String(rxDoneTime - detTime);
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
|
||||
// Do all register processing in this section
|
||||
uint8_t value = readRegister(REG_PKT_SNR_VALUE); // 0x19;
|
||||
if ( value & 0x80 ) { // The SNR sign bit is 1
|
||||
|
||||
value = ( ( ~value + 1 ) & 0xFF ) >> 2; // Invert and divide by 4
|
||||
LoraUp.snr = -value;
|
||||
}
|
||||
else {
|
||||
// Divide by 4
|
||||
LoraUp.snr = ( value & 0xFF ) >> 2;
|
||||
}
|
||||
|
||||
// Packet RSSI
|
||||
LoraUp.prssi = readRegister(REG_PKT_RSSI); // read register 0x1A, packet rssi
|
||||
|
||||
// Correction of RSSI value based on chip used.
|
||||
if (sx1272) { // Is it a sx1272 radio?
|
||||
LoraUp.rssicorr = 139;
|
||||
} else { // Probably SX1276 or RFM95
|
||||
LoraUp.rssicorr = 157;
|
||||
}
|
||||
|
||||
LoraUp.sf = readRegister(REG_MODEM_CONFIG2) >> 4;
|
||||
|
||||
// If read was successful, read the package from the LoRa bus
|
||||
//
|
||||
if (receivePacket() <= 0) { // read is not successful
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=0 ) && ( pdebug & P_RX )) {
|
||||
mPrint("sMach:: ERROR receivePacket");
|
||||
}
|
||||
# endif //_MONITOR
|
||||
}
|
||||
|
||||
// Set the modem to receiving BEFORE going back to user space.
|
||||
//
|
||||
if ((gwayConfig.cad) || (gwayConfig.hop)) {
|
||||
_state = S_SCAN;
|
||||
sf = SF7;
|
||||
cadScanner();
|
||||
}
|
||||
else {
|
||||
_state = S_RX;
|
||||
rxLoraModem();
|
||||
}
|
||||
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset the interrupt mask
|
||||
eventTime=micros(); // There was an event for receive
|
||||
_event=0;
|
||||
}// RXDONE
|
||||
|
||||
// RXOUT:
|
||||
// We did receive message receive timeout
|
||||
// This is the most common event in hop mode, possibly due to the fact
|
||||
// that receiving has started too late in the middle of a message
|
||||
// (according to the documentation). So is there a way to start receiving
|
||||
// immediately without delay.
|
||||
//
|
||||
else if (intr & IRQ_LORA_RXTOUT_MASK) {
|
||||
|
||||
// Make sure we reset all interrupts
|
||||
// and get back to scanning
|
||||
_event=0; // Is set by interrupt handlers
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00 );
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset all interrupts
|
||||
|
||||
// If RXTOUT we put the modem in cad state and reset to SF7
|
||||
// If a timeout occurs here we reset the cadscanner
|
||||
//
|
||||
if ((gwayConfig.cad) || (gwayConfig.hop)) {
|
||||
// Set the state to CAD scanning
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=2 ) && ( pdebug & P_RX )) {
|
||||
String response = "RXTOUT:: ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
sf = SF7;
|
||||
cadScanner(); // Start the scanner after RXTOUT
|
||||
_state = S_SCAN; // New state is scan
|
||||
|
||||
}
|
||||
|
||||
// If not in cad mode we are in single channel single sf mode.
|
||||
//
|
||||
else {
|
||||
_state = S_RX; // Receive when interrupted
|
||||
rxLoraModem();
|
||||
}
|
||||
|
||||
eventTime=micros(); //There was an event for receive
|
||||
doneTime = micros(); // We need CDDONE or other intr to reset timeout
|
||||
|
||||
}// RXTOUT
|
||||
|
||||
else if (intr & IRQ_LORA_HEADER_MASK) {
|
||||
// This interrupt means we received an header successfully
|
||||
// which is normall an indication of RXDONE
|
||||
//writeRegister(REG_IRQ_FLAGS, IRQ_LORA_HEADER_MASK);
|
||||
# if _MONITOR>=1
|
||||
if ((debug>=3 ) && (pdebug & P_RX)) {
|
||||
mPrint("RX HEADER:: " + String(intr));
|
||||
}
|
||||
# endif //_MONITOR
|
||||
//_event=1;
|
||||
}
|
||||
|
||||
// If we did not receive a hard interrupt
|
||||
// Then probably do not do anything, because in the S_RX
|
||||
// state there always comes a RXTOUT or RXDONE interrupt
|
||||
//
|
||||
else if (intr == 0x00) {
|
||||
# if _MONITOR>=1
|
||||
if ((debug>=3) && (pdebug & P_RX)) {
|
||||
mPrint("S_RX no INTR:: " + String(intr));
|
||||
}
|
||||
# endif //_MONITOR
|
||||
}
|
||||
|
||||
// The interrupt received is not RXDONE, RXTOUT or HEADER
|
||||
// therefore we wait. Make sure to clear the interrupt
|
||||
// as HEADER interrupt comes just before RXDONE
|
||||
else {
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=0 ) && ( pdebug & P_RX )) {
|
||||
mPrint("R S_RX:: no RXDONE, RXTOUT, HEADER:: " + String(intr));
|
||||
}
|
||||
# endif //_MONITOR
|
||||
//writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00 );
|
||||
//writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
|
||||
}// int not RXDONE or RXTOUT
|
||||
|
||||
break; // S_RX
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Start the transmission of a message in state S-TX (DOWN)
|
||||
// This is not an interrupt state, we use this state to start transmission
|
||||
// the interrupt TX-DONE tells us that the transmission was successful.
|
||||
// It therefore is no use to set _event==1 as transmission might
|
||||
// not be finished in the next loop iteration
|
||||
//
|
||||
case S_TX:
|
||||
|
||||
// We need a timeout for this case. In case there does not come an interrupt,
|
||||
// then there will not be a TXDONE but probably another CDDONE/CDDETD before
|
||||
// we have a timeout in the main program (Keep Alive)
|
||||
if (intr == 0x00) {
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=2 ) && ( pdebug & P_TX )) {
|
||||
mPrint("TX:: 0x00");
|
||||
}
|
||||
# endif //_MONITOR
|
||||
_event= 1;
|
||||
}
|
||||
|
||||
loraWait(&LoraDown);
|
||||
|
||||
// Set state to transmit
|
||||
// Clear interrupt flags and masks
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags
|
||||
|
||||
// Initiate the transmission of the buffer (in Interrupt space)
|
||||
// We react on ALL interrupts if we are in TX state.
|
||||
txLoraModem(&LoraDown);
|
||||
|
||||
// After filling the buffer we only react on TXDONE interrupt
|
||||
// So, more or less start at the "case TXDONE:" below
|
||||
_state=S_TXDONE;
|
||||
_event=1; // Or remove the break below
|
||||
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=1 ) && ( pdebug & P_TX )) {
|
||||
String response="TX fini:: ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
|
||||
break; // S_TX
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// After the transmission is completed by the hardware,
|
||||
// the interrupt TXDONE is raised telling us that the tranmission
|
||||
// was successful.
|
||||
// If we receive an interrupt on dio0 _state==S_TX it is a TxDone interrupt
|
||||
// Do nothing with the interrupt, it is just an indication.
|
||||
// send Packet switch back to scanner mode after transmission finished OK
|
||||
//
|
||||
case S_TXDONE:
|
||||
if (intr & IRQ_LORA_TXDONE_MASK) {
|
||||
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=1 ) && ( pdebug & P_TX )) {
|
||||
String response = "Dwns TXDONE:: OK: rcvd=";
|
||||
printInt(micros(),response);
|
||||
if (micros() < LoraDown.tmst) {
|
||||
response += ", diff=-" ;
|
||||
printInt(LoraDown.tmst - micros(), response );
|
||||
}
|
||||
else {
|
||||
response += ", diff=";
|
||||
printInt(micros()-LoraDown.tmst, response);
|
||||
}
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
|
||||
// After transmission reset to receiver
|
||||
if ((gwayConfig.cad) || (gwayConfig.hop)) { // XXX 26/02
|
||||
// Set the state to CAD scanning
|
||||
_state = S_SCAN;
|
||||
sf = SF7;
|
||||
cadScanner(); // Start the scanner after TX cycle
|
||||
}
|
||||
else {
|
||||
_state = S_RX;
|
||||
rxLoraModem();
|
||||
}
|
||||
|
||||
_event=0;
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags
|
||||
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=2 ) && ( pdebug & P_TX )) {
|
||||
mPrint("TXDONE:: done OK");
|
||||
}
|
||||
# endif //_MONITOR
|
||||
}
|
||||
|
||||
// If a soft _event==0 interrupt and no transmission finished:
|
||||
else if ( intr != 0 ) {
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=0 ) && ( pdebug & P_TX )) {
|
||||
String response = "TXDONE:: Error unknown intr=";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
} //_MONITOR
|
||||
# endif
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags
|
||||
_event=0;
|
||||
_state=S_SCAN;
|
||||
}
|
||||
|
||||
// intr == 0
|
||||
else {
|
||||
|
||||
// Increase timer. If timer exceeds certain value (7 seconds!), reset
|
||||
// After sending a message with S_TX, we have to receive a TXDONE interrupt
|
||||
// within 7 seconds according to spec, of here is a problem.
|
||||
if ( sendTime > micros() ) sendTime = 0; // This could be omitted for usigned ints
|
||||
if (( _state == S_TXDONE ) && (( micros() - sendTime) > 7000000 )) {
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=1 ) && ( pdebug & P_TX )) {
|
||||
mPrint("TXDONE:: reset TX");
|
||||
}
|
||||
# endif //_MONITOR
|
||||
startReceiver();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
break; // S_TXDONE
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// If _STATE is in an undefined state
|
||||
// If such a thing happens, we should re-init the interface and
|
||||
// make sure that we pick up next interrupt
|
||||
default:
|
||||
# if _MONITOR>=1
|
||||
if (( debug>=0) && ( pdebug & P_PRE )) {
|
||||
mPrint("ERR state=" + String(_state));
|
||||
}
|
||||
# endif //_MONITOR
|
||||
|
||||
if ((gwayConfig.cad) || (gwayConfig.hop)) {
|
||||
# if _MONITOR>=1
|
||||
if (debug>=0) {
|
||||
String response = "default:: Unknown _state ";
|
||||
mStat(intr, response);
|
||||
mPrint(response);
|
||||
}
|
||||
# endif //_MONITOR
|
||||
_state = S_SCAN;
|
||||
sf = SF7;
|
||||
cadScanner(); // Restart the state machine
|
||||
_event=0;
|
||||
}
|
||||
else // Single channel AND single SF
|
||||
{
|
||||
_state = S_RX;
|
||||
rxLoraModem();
|
||||
_event=0;
|
||||
}
|
||||
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
|
||||
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset all interrupts
|
||||
eventTime=micros(); // Reset event for unkonwn state
|
||||
|
||||
break;// default
|
||||
}// switch(_state)
|
||||
|
||||
return;
|
||||
}
|
Loading…
Reference in New Issue