_stateMachine.ino 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. // 1-channel LoRa Gateway for ESP8266
  2. // Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266
  3. //
  4. // based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
  5. // and many others.
  6. //
  7. // All rights reserved. This program and the accompanying materials
  8. // are made available under the terms of the MIT License
  9. // which accompanies this distribution, and is available at
  10. // https://opensource.org/licenses/mit-license.php
  11. //
  12. // NO WARRANTY OF ANY KIND IS PROVIDED
  13. //
  14. // Author: Maarten Westenberg (mw12554@hotmail.com)
  15. //
  16. // This file contains the state machine code enabling to receive
  17. // and transmit packages/messages.
  18. // ============================================================================================
  19. //
  20. // --------------------------------------------------------------------------------------------
  21. // stateMachine handler of the state machine.
  22. // We use ONE state machine for all kind of interrupts. This assures that we take
  23. // the correct action upon receiving an interrupt.
  24. //
  25. // _event is the software interrupt: If set this function is executed from loop(),
  26. // the function should itself take care of setting or resetting the variable.
  27. //
  28. // STATE MACHINE
  29. // The program uses the following state machine (in _state), all states
  30. // are done in interrupt routine, only the follow-up of S-RXDONE is done
  31. // in the main loop() program. This is because otherwise the interrupt processing
  32. // would take too long to finish
  33. //
  34. // So _state has one of the following state values:
  35. //
  36. // S-INIT=0, The commands in this state are executed only once
  37. // - Goto S_SCAN
  38. //
  39. // S-SCAN, CadScanner() part
  40. // - Upon CDDECT (int1) goto S_RX,
  41. // - upon CDDONE (int0) goto S_CAD, walk through all SF until CDDETD
  42. // - Else stay in SCAN state
  43. //
  44. // S-CAD,
  45. // - Upon CDDECT (int1) goto S_RX,
  46. // - Upon CDDONE (int0) goto S_SCAN, start with SF7 recognition again
  47. //
  48. // S-RX, Received CDDECT so message detected, RX cycle started.
  49. // - Upon RXDONE (int0) package read. If read finished continue to read message, then goto S_SCAN
  50. // - upon RXTOUT (int1) error, goto S_SCAN
  51. //
  52. // S-TX Transmitting a message
  53. // - When transmission done, goto TXDONE
  54. //
  55. // S-TXDONE Transmission complete by loop() now again in interrupt
  56. // - Set the Mask
  57. // - reset the Flags
  58. // - Goto either SCAN or RX
  59. //
  60. // This interrupt routine has been kept as simple and short as possible.
  61. // If we receive an interrupt that does not belong to a _state then print error.
  62. // _event is a special variable which indicate that an interrupt event has happened
  63. // and we need to take action OR that we generate a soft interrupt for state machine.
  64. //
  65. // NOTE: We may clear the interrupt but leave the flag for the moment.
  66. // The eventHandler should take care of repairing flags between interrupts.
  67. // --------------------------------------------------------------------------------------------
  68. void stateMachine()
  69. {
  70. // Determine what interrupt flags are set
  71. //
  72. uint8_t flags = readRegister(REG_IRQ_FLAGS);
  73. uint8_t mask = readRegister(REG_IRQ_FLAGS_MASK);
  74. uint8_t intr = flags & ( ~ mask ); // Only react on non masked interrupts
  75. uint8_t rssi;
  76. _event=0; // Reset the interrupt detector
  77. # if _MONITOR>=1
  78. if (intr != flags) {
  79. String response = "";
  80. mStat(intr, response);
  81. mPrint("FLAG ::"+response);
  82. }
  83. # endif //_MONITOR
  84. // If Hopping is selected AND if there is NO event interrupt detected
  85. // and the state machine is called anyway
  86. // then we know its a soft interrupt and we do nothing and return to main loop.
  87. //
  88. if ((gwayConfig.hop) && (intr == 0x00))
  89. {
  90. // eventWait is the time since we have had a CDDETD event (preamble detected).
  91. // If we are not in scanning state, and there will be an interrupt coming,
  92. // In state S_RX it could be RXDONE in which case allow kernel time
  93. //
  94. if ((_state == S_SCAN) || (_state == S_CAD)) {
  95. _event=0;
  96. uint32_t eventWait = EVENT_WAIT;
  97. switch (_state) {
  98. case S_INIT: eventWait = 0; break;
  99. // Next two are most important
  100. case S_SCAN: eventWait = EVENT_WAIT * 1; break;
  101. case S_CAD: eventWait = EVENT_WAIT * 1; break;
  102. case S_RX: eventWait = EVENT_WAIT * 8; break;
  103. case S_TX: eventWait = EVENT_WAIT * 1; break;
  104. case S_TXDONE: eventWait = EVENT_WAIT * 4; break;
  105. default:
  106. eventWait=0;
  107. # if _MONITOR>=1
  108. String response = "StateMachine:: Default: ";
  109. mStat(intr, response);
  110. mPrint(response);
  111. # endif //_MONITOR
  112. }
  113. // doneWait is the time that we received CDDONE interrupt
  114. // So we init the wait time for RXDONE based on the current SF.
  115. // As for highter CF it takes longer to receive symbols
  116. // Assume symbols in SF8 take twice the time of SF7
  117. //
  118. uint32_t doneWait = DONE_WAIT; // Initial value
  119. switch (sf) {
  120. case SF7: break;
  121. case SF8: doneWait *= 2; break;
  122. case SF9: doneWait *= 4; break;
  123. case SF10: doneWait *= 8; break;
  124. case SF11: doneWait *= 16; break;
  125. case SF12: doneWait *= 32; break;
  126. default:
  127. doneWait *= 1;
  128. # if _MONITOR>=1
  129. if ((debug>=0) && (pdebug & P_PRE)) {
  130. mPrint("StateMachine:: PRE: DEF set");
  131. }
  132. # endif //_MONITOR
  133. break;
  134. }
  135. // If micros is starting over again after 51 minutes
  136. // it's value is smaller than an earlier value of eventTime/doneTime
  137. //
  138. if (eventTime > micros()) eventTime=micros();
  139. if (doneTime > micros()) doneTime=micros();
  140. if (((micros() - doneTime) > doneWait) &&
  141. ((_state == S_SCAN) || (_state == S_CAD)))
  142. {
  143. _state = S_SCAN;
  144. hop(); // increment gwayConfig.ch = (gwayConfig.ch + 1) % NUM_HOPS ;
  145. cadScanner(); // Reset to initial SF, leave frequency "freqs[gwayConfig.ch]"
  146. # if _MONITOR>=1
  147. if ((debug >= 1) && (pdebug & P_PRE)) {
  148. String response = "DONE :: ";
  149. mStat(intr, response);
  150. mPrint(response); // Can move down for timing reasons
  151. }
  152. # endif //_MONITOR
  153. eventTime=micros(); // reset the timer on timeout
  154. doneTime=micros(); // reset the timer on timeout
  155. return;
  156. }
  157. // If timeout occurs and still no _event, then hop
  158. // and start scanning again
  159. //
  160. if ((micros() - eventTime) > eventWait )
  161. {
  162. _state = S_SCAN;
  163. hop(); // gwayConfig.ch= (gwayConfig.ch+1)%NUM_HOPS ;
  164. cadScanner(); // Reset to initial SF, leave "freqs[gwayConfig.ch]"
  165. # if _MONITOR>=1
  166. if ((debug >= 2) && (pdebug & P_PRE)) {
  167. String response = "HOP :: ";
  168. mStat(intr, response);
  169. mPrint(response);
  170. }
  171. # endif //_MONITOR
  172. eventTime=micros(); // reset the eventtimer on timeout
  173. doneTime=micros(); // reset the timer on timeout
  174. return;
  175. }
  176. // If we are here, NO timeout has occurred
  177. // So we need to return to the main State Machine
  178. // as there was NO interrupt
  179. # if _MONITOR>=1
  180. if ((debug>=3) && (pdebug & P_PRE)) {
  181. String response = "PRE:: eventTime=";
  182. response += String(eventTime);
  183. response += ", micros=";
  184. response += String(micros());
  185. response += ": ";
  186. mStat(intr, response);
  187. mPrint(response);
  188. }
  189. # endif //_MONITOR
  190. } // if SCAN or CAD
  191. // else, S_RX of S_TX for example
  192. else {
  193. //yield(); // May take too much time for RX
  194. } // else S_RX or S_TX, TXDONE
  195. yield();
  196. }// intr==0 && gwayConfig.hop
  197. // ========================================================================================
  198. // This is the actual state machine of the gateway
  199. // and its next actions are depending on the state we are in.
  200. // For hop situations we do not get interrupts, so we have to
  201. // simulate and generate events ourselves.
  202. //
  203. switch (_state)
  204. {
  205. // ----------------------------------------------------------------------------------------
  206. // If the state is init, we are starting up.
  207. // The initLoraModem() function is already called in setup();
  208. //
  209. case S_INIT:
  210. # if _MONITOR>=1
  211. if ((debug>=1) && (pdebug & P_PRE)) {
  212. mPrint("S_INIT");
  213. }
  214. # endif //_MONITOR
  215. // new state, needed to startup the radio (to S_SCAN)
  216. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF ); // Clear ALL interrupts
  217. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00 ); // Clear ALL interrupts
  218. _event=0;
  219. break;
  220. // ----------------------------------------------------------------------------------------
  221. // In S_SCAN we measure a high RSSI this means that there (probably) is a message
  222. // coming in at that freq. But not necessarily on the current SF.
  223. // If so find the right SF with CDDETD.
  224. //
  225. case S_SCAN:
  226. //
  227. // Intr==IRQ_LORA_CDDETD_MASK
  228. // We detected a message on this frequency and SF when scanning
  229. // We clear both CDDETD and swich to reading state to read the message
  230. //
  231. if (intr & IRQ_LORA_CDDETD_MASK) {
  232. _state = S_RX; // Set state to receiving
  233. // Set RXDONE interrupt to dio0, RXTOUT to dio1
  234. writeRegister(REG_DIO_MAPPING_1, (
  235. MAP_DIO0_LORA_RXDONE |
  236. MAP_DIO1_LORA_RXTOUT |
  237. MAP_DIO2_LORA_NOP |
  238. MAP_DIO3_LORA_CRC));
  239. // Since new state is S_RX, accept no interrupts except RXDONE or RXTOUT
  240. // HEADER and CRCERR
  241. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(
  242. IRQ_LORA_RXDONE_MASK |
  243. IRQ_LORA_RXTOUT_MASK |
  244. IRQ_LORA_HEADER_MASK |
  245. IRQ_LORA_CRCERR_MASK));
  246. // Starting with version 5.0.1 the waittime is dependent on the SF
  247. // So for SF12 we wait longer (2^7 == 128 uSec) and for SF7 4 uSec.
  248. //delayMicroseconds( (0x01 << ((uint8_t)sf - 5 )) );
  249. //if (gwayConfig.cad) // XXX 180520 make sure we start reading asap in hop
  250. // delayMicroseconds( RSSI_WAIT ); // Wait some microseconds less
  251. rssi = readRegister(REG_RSSI); // Read the RSSI
  252. _rssi = rssi; // Read the RSSI in the state variable
  253. _event = 0; // Make 0, as soon as we have an interrupt
  254. detTime = micros(); // mark time that preamble detected
  255. # if _MONITOR>=1
  256. if ((debug>=1) && (pdebug & P_PRE)) {
  257. String response = "SCAN:: ";
  258. mStat(intr, response);
  259. mPrint(response);
  260. }
  261. # endif //_MONITOR
  262. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset all interrupt flags
  263. opmode(OPMODE_RX_SINGLE); // set reg 0x01 to 0x06 for receiving
  264. }//if
  265. // CDDONE
  266. // We received a CDDONE int telling us that we received a message on this
  267. // frequency and possibly on one of its SF. Only when the incoming message
  268. // matches the SF then also CDDETD is raised.
  269. // If so, we switch to CAD state where we will wait for CDDETD event.
  270. //
  271. else if (intr & IRQ_LORA_CDDONE_MASK) {
  272. opmode(OPMODE_CAD);
  273. rssi = readRegister(REG_RSSI); // Read the RSSI
  274. # if _MONITOR>=1
  275. if ((debug>=2) && (pdebug & P_SCAN)) {
  276. String response = "SCAN:: CDDONE: ";
  277. mStat(intr, response);
  278. mPrint(response);
  279. }
  280. # endif //_MONITOR
  281. // We choose the generic RSSI as a sorting mechanism for packages/messages
  282. // The pRSSI (package RSSI) is calculated upon successful reception of message
  283. // So we expect that this value makes little sense for the moment with CDDONE.
  284. // Set the rssi as low as the noise floor. Lower values are not recognized then.
  285. // Every cycle starts with gwayConfig.ch==0 and sf=SF7 (or the set init SF)
  286. //
  287. if (rssi > (RSSI_LIMIT - (gwayConfig.hop * 7))) // Is set to 35, or 29 for HOP
  288. {
  289. # if _MONITOR>=1
  290. if (( debug>=2 ) && ( pdebug & P_SCAN )) {
  291. String response = "SCAN:: -> CAD: ";
  292. mStat(intr, response);
  293. mPrint(response);
  294. }
  295. # endif //_MONITOR
  296. _state = S_CAD; // promote to next level
  297. _event=0;
  298. }
  299. // If the RSSI is not big enough we skip the CDDONE
  300. // and go back to scanning
  301. else {
  302. # if _MONITOR>=1
  303. if (( debug>=2 ) && ( pdebug & P_SCAN )) {
  304. String response = "SCAN:: rssi=";
  305. response += String(rssi);
  306. response += ": ";
  307. mStat(intr, response);
  308. mPrint(response);
  309. }
  310. # endif //_MONITOR
  311. _state = S_SCAN;
  312. }
  313. // Clear the CADDONE flag
  314. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
  315. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
  316. doneTime = micros(); // Need CDDONE or other intr to reset timeout
  317. }//SCAN CDDONE
  318. // So if we are here then we are in S_SCAN and the interrupt is not
  319. // CDDECT or CDDONE. it is probably soft interrupt _event==1
  320. // So if gwayConfig.hop we change the frequency and restart the
  321. // interrupt in order to check for CDONE on other frequencies
  322. // if gwayConfig.hop we start at the next frequency, hop () sets the sf to SF7.
  323. // If we are at the end of all frequencies, reset frequencies and sf
  324. // and go to S_SCAN state.
  325. //
  326. // Note:: We should make sure that all frequencies are scanned in a row
  327. // and when we switch to gwayConfig.ch==0 we should stop for a while
  328. // to allow system processing.
  329. // We should make sure that we enable webserver etc every once in a while.
  330. // We do this by changing _event to 1 in loop() only for gwayConfig.hop and
  331. // use _event=0 for non hop.
  332. //
  333. else if (intr == 0x00)
  334. {
  335. _event=0; // XXX 26/12/2017 !!! NEED
  336. }
  337. // Unkown Interrupt, so we have an error
  338. //
  339. else {
  340. # if _MONITOR>=1
  341. if (( debug>=0 ) && ( pdebug & P_SCAN )) {
  342. String response = "SCAN unknown:: ";
  343. mStat(intr, response);
  344. mPrint(response);
  345. }
  346. # endif //_MONITOR
  347. _state=S_SCAN;
  348. //_event=1; // XXX 06/03 loop until interrupt
  349. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
  350. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
  351. }
  352. break; // S_SCAN
  353. // ----------------------------------------------------------------------------------------
  354. // S_CAD: In CAD mode we scan every SF for high RSSI until we have a DETECT.
  355. // Reason is the we received a CADDONE interrupt so we know a message is received
  356. // on the frequency but may be on another SF.
  357. //
  358. // If message is of the right frequency and SF, IRQ_LORA_CDDETD_MSAK interrupt
  359. // is raised, indicating that we can start beging reading the message from SPI.
  360. //
  361. // DIO0 interrupt IRQ_LORA_CDDONE_MASK in state S_CAD==2 means that we might have
  362. // a lock on the Freq but not the right SF. So we increase the SF
  363. //
  364. case S_CAD:
  365. // Intr=IRQ_LORA_CDDETD_MASK
  366. // We have to set the sf based on a strong RSSI for this channel
  367. // Also we set the state to S_RX and start receiving the message
  368. //
  369. if (intr & IRQ_LORA_CDDETD_MASK) {
  370. // Set RXDONE interrupt to dio0, RXTOUT to dio1
  371. writeRegister(REG_DIO_MAPPING_1, (
  372. MAP_DIO0_LORA_RXDONE |
  373. MAP_DIO1_LORA_RXTOUT |
  374. MAP_DIO2_LORA_NOP |
  375. MAP_DIO3_LORA_CRC ));
  376. // Accept no interrupts except RXDONE or RXTOUT
  377. _event=0;
  378. // if CDECT, make state S_RX so we wait for RXDONE intr
  379. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(
  380. IRQ_LORA_RXDONE_MASK |
  381. IRQ_LORA_RXTOUT_MASK |
  382. IRQ_LORA_HEADER_MASK |
  383. IRQ_LORA_CRCERR_MASK ));
  384. // Reset all interrupts as soon as possible
  385. // But listen ONLY to RXDONE and RXTOUT interrupts
  386. //writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDETD_MASK | IRQ_LORA_RXDONE_MASK);
  387. // If we want to reset CRC, HEADER and RXTOUT flags as well
  388. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF ); // XXX 180326, reset all CAD Detect interrupt flags
  389. //_state = S_RX; // XXX 180521 Set state to start receiving
  390. opmode(OPMODE_RX_SINGLE); // set reg 0x01 to 0x06, initiate READ
  391. delayMicroseconds( RSSI_WAIT ); // Wait some microseconds less
  392. //delayMicroseconds( (0x01 << ((uint8_t)sf - 5 )) );
  393. rssi = readRegister(REG_RSSI); // Read the RSSI
  394. _rssi = rssi; // Read the RSSI in the state variable
  395. detTime = micros();
  396. # if _MONITOR>=1
  397. if (( debug>=1 ) && ( pdebug & P_CAD )) {
  398. String response = "CAD:: ";
  399. mStat(intr, response);
  400. mPrint(response);
  401. }
  402. # endif //_MONITOR
  403. _state = S_RX; // Set state to start receiving
  404. }// CDDETD
  405. // Intr == CADDONE
  406. // So we scan this SF and if not high enough ... next
  407. //
  408. else if (intr & IRQ_LORA_CDDONE_MASK) {
  409. // If this is not the max SF, increment the SF and try again
  410. // Depending on the frequency scheme this is for example SF8, SF10 or SF12
  411. // We expect on other SF get CDDETD
  412. //
  413. if (((uint8_t)sf) < freqs[gwayConfig.ch].upHi) {
  414. sf = (sf_t)((uint8_t)sf+1); // Increment sf
  415. setRate(sf, 0x04); // Set SF with CRC==on
  416. // reset interrupt flags for CAD Done
  417. _event=0; // XXX 180324, when increasing SF loop, ws 0x00
  418. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask
  419. //writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDONE_MASK | IRQ_LORA_CDDETD_MASK);
  420. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF ); // This will prevent the CDDETD from being read
  421. opmode(OPMODE_CAD); // Scanning mode
  422. delayMicroseconds(RSSI_WAIT);
  423. rssi = readRegister(REG_RSSI); // Read the RSSI
  424. # if _MONITOR>=1
  425. if (( debug>=3 ) && ( pdebug & P_CAD )) {
  426. mPrint("S_CAD:: CDONE, SF=" + String(sf) );
  427. }
  428. # endif //_MONITOR
  429. }
  430. // If we reach the highest SF for the frequency plan,
  431. // we should go back to SCAN state
  432. //
  433. else {
  434. // Reset Interrupts
  435. _event=1; // reset soft intr, to state machine again
  436. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask
  437. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF ); // or IRQ_LORA_CDDONE_MASK
  438. _state = S_SCAN; // As soon as we reach SF12 do something
  439. sf = (sf_t) freqs[gwayConfig.ch].upLo;
  440. cadScanner(); // Which will reset SF to lowest SF
  441. # if _MONITOR>=1
  442. if (( debug>=3 ) && ( pdebug & P_CAD )) {
  443. mPrint("CAD->SCAN:: " + String(intr) );
  444. }
  445. # endif //_MONITOR
  446. }
  447. doneTime = micros(); // We need CDDONE or other intr to reset timeout
  448. } //CAD CDDONE
  449. // if this interrupt is not CDECT or CDDONE then probably is 0x00
  450. // This means _event was set but there was no real interrupt (yet).
  451. // So we clear _event and wait for next (soft) interrupt.
  452. // We stay in the CAD state because CDDONE means something is
  453. // coming on this frequency so we wait on CDECT.
  454. //
  455. else if (intr == 0x00) {
  456. # if _MONITOR>=1
  457. if (( debug>=3 ) && ( pdebug & P_CAD )) {
  458. mPrint ("CAD:: intr is 0x00");
  459. }
  460. # endif //_MONITOR
  461. _event=1; // Stay in CAD _state until real interrupt
  462. }
  463. // else we do not recognize the interrupt. We print an error
  464. // and restart scanning. If hop we start at gwayConfig.ch==1
  465. //
  466. else {
  467. # if _MONITOR>=1
  468. if (( debug>=0) && ( pdebug & P_CAD )) {
  469. mPrint("Err CAD: Unknown::" + String(intr) );
  470. }
  471. # endif //_MONITOR
  472. _state = S_SCAN;
  473. sf = SF7;
  474. cadScanner(); // Scan and set SF7
  475. // Reset Interrupts
  476. _event=1; // If unknown interrupt, restarts
  477. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask
  478. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset all interrupts
  479. }
  480. break; //S_CAD
  481. // ----------------------------------------------------------------------------------------
  482. // If we receive an RXDONE interrupt on dio0 with state==S_RX
  483. // So we should handle the received message
  484. // Else if it is RXTOUT interrupt
  485. // Timeout, so we handle this interrupt, and get modem out of standby
  486. // Else
  487. // Go back to SCAN
  488. //
  489. case S_RX:
  490. if (intr & IRQ_LORA_RXDONE_MASK) {
  491. # if _CRCCHECK>=1
  492. // We have to check for CRC error which will be visible AFTER RXDONE is set.
  493. // CRC errors might indicate that the reception is not OK.
  494. // Could be CRC error or message too large.
  495. // CRC error checking requires DIO3
  496. //
  497. if (intr & IRQ_LORA_CRCERR_MASK) {
  498. # if _MONITOR>=1
  499. if (( debug>=0 ) && ( pdebug & P_RX )) {
  500. String response = "";
  501. mStat(intr, response);
  502. Serial.print(F("Rx CRC err: "));
  503. }
  504. # endif //_MONITOR
  505. if ((gwayConfig.cad) || (gwayConfig.hop)) {
  506. sf = SF7;
  507. _state = S_SCAN;
  508. cadScanner();
  509. }
  510. else {
  511. _state = S_RX;
  512. rxLoraModem();
  513. }
  514. // Reset interrupts
  515. _event=0; // CRC error
  516. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask
  517. writeRegister(REG_IRQ_FLAGS, (uint8_t)(
  518. IRQ_LORA_RXDONE_MASK |
  519. IRQ_LORA_RXTOUT_MASK |
  520. IRQ_LORA_HEADER_MASK |
  521. IRQ_LORA_CRCERR_MASK ));
  522. break; // Get out of loop
  523. }// RX-CRC mask
  524. # endif //_CRCCHECK
  525. // If we are here, no CRC error occurred, start timer
  526. # if _DUSB>=1 || _MONITOR>=1
  527. uint32_t ffTime = micros();
  528. # endif
  529. // There should not be an error in the message
  530. LoraUp.payLoad[0]= 0x00; // Empty the message
  531. // If receive S_RX error,
  532. // - print Error message
  533. // - Set _state to SCAN
  534. // - Set _event=1 so that we loop until we have an interrupt
  535. // - Reset the interrupts
  536. // - break
  537. // NOTE: receivePacket also increases .ok0 - .ok2 counter
  538. if((LoraUp.payLength = receivePkt(LoraUp.payLoad)) <= 0) {
  539. # if _MONITOR>=1
  540. if (( debug>=1 ) && ( pdebug & P_RX )) {
  541. String response = "sMachine:: Error S-RX: payLenth=";
  542. response += String(LoraUp.payLength);
  543. mPrint(response);
  544. }
  545. # endif //_MONITOR
  546. _event=1;
  547. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask
  548. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
  549. _state = S_SCAN;
  550. break;
  551. }
  552. # if _MONITOR>=1
  553. if ((pdebug & P_RX) && (debug >= 2)) {
  554. String response = "RXDONE:: dT=";
  555. response += String(ffTime - detTime);
  556. mStat(intr, response);
  557. mPrint(response);
  558. }
  559. # endif //_MONITOR
  560. // Do all register processing in this section
  561. uint8_t value = readRegister(REG_PKT_SNR_VALUE); // 0x19;
  562. if ( value & 0x80 ) { // The SNR sign bit is 1
  563. value = ( ( ~value + 1 ) & 0xFF ) >> 2; // Invert and divide by 4
  564. LoraUp.snr = -value;
  565. }
  566. else {
  567. // Divide by 4
  568. LoraUp.snr = ( value & 0xFF ) >> 2;
  569. }
  570. // Packet RSSI
  571. LoraUp.prssi = readRegister(REG_PKT_RSSI); // read register 0x1A, packet rssi
  572. // Correction of RSSI value based on chip used.
  573. if (sx1272) { // Is it a sx1272 radio?
  574. LoraUp.rssicorr = 139;
  575. } else { // Probably SX1276 or RFM95
  576. LoraUp.rssicorr = 157;
  577. }
  578. LoraUp.sf = readRegister(REG_MODEM_CONFIG2) >> 4;
  579. // If read was successful, read the package from the LoRa bus
  580. //
  581. if (receivePacket() <= 0) { // read is not successful
  582. # if _MONITOR>=1
  583. if (( debug>=0 ) && ( pdebug & P_RX )) {
  584. mPrint("sMach:: Error receivePacket");
  585. }
  586. # endif //_MONITOR
  587. }
  588. // Set the modem to receiving BEFORE going back to user space.
  589. //
  590. if ((gwayConfig.cad) || (gwayConfig.hop)) {
  591. _state = S_SCAN;
  592. sf = SF7;
  593. cadScanner();
  594. }
  595. else {
  596. _state = S_RX;
  597. rxLoraModem();
  598. }
  599. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
  600. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset the interrupt mask
  601. eventTime=micros(); // There was an event for receive
  602. _event=0;
  603. }// RXDONE
  604. // RXOUT:
  605. // We did receive message receive timeout
  606. // This is the most common event in hop mode, possibly due to the fact
  607. // that receiving has started too late in the middle of a message
  608. // (according to the documentation). So is there a way to start receiving
  609. // immediately without delay.
  610. //
  611. else if (intr & IRQ_LORA_RXTOUT_MASK) {
  612. // Make sure we reset all interrupts
  613. // and get back to scanning
  614. _event=0; // Is set by interrupt handlers
  615. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00 );
  616. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset all interrupts
  617. // If RXTOUT we put the modem in cad state and reset to SF7
  618. // If a timeout occurs here we reset the cadscanner
  619. //
  620. if ((gwayConfig.cad) || (gwayConfig.hop)) {
  621. // Set the state to CAD scanning
  622. # if _MONITOR>=1
  623. if (( debug>=2 ) && ( pdebug & P_RX )) {
  624. String response = "RXTOUT:: ";
  625. mStat(intr, response);
  626. mPrint(response);
  627. }
  628. # endif //_MONITOR
  629. sf = SF7;
  630. cadScanner(); // Start the scanner after RXTOUT
  631. _state = S_SCAN; // New state is scan
  632. }
  633. // If not in cad mode we are in single channel single sf mode.
  634. //
  635. else {
  636. _state = S_RX; // Receive when interrupted
  637. rxLoraModem();
  638. }
  639. eventTime=micros(); //There was an event for receive
  640. doneTime = micros(); // We need CDDONE or other intr to reset timeout
  641. }// RXTOUT
  642. else if (intr & IRQ_LORA_HEADER_MASK) {
  643. // This interrupt means we received an header successfully
  644. // which is normall an indication of RXDONE
  645. //writeRegister(REG_IRQ_FLAGS, IRQ_LORA_HEADER_MASK);
  646. # if _MONITOR>=1
  647. if (( debug>=3 ) && ( pdebug & P_RX )) {
  648. mPrint("RX HEADER:: " + String(intr));
  649. }
  650. # endif //_MONITOR
  651. //_event=1;
  652. }
  653. // If we did not receive a hard interrupt
  654. // Then probably do not do anything, because in the S_RX
  655. // state there always comes a RXTOUT or RXDONE interrupt
  656. //
  657. else if (intr == 0x00) {
  658. # if _MONITOR>=1
  659. if (( debug>=3) && ( pdebug & P_RX )) {
  660. mPrint("S_RX no INTR:: " + String(intr));
  661. }
  662. # endif //_MONITOR
  663. }
  664. // The interrupt received is not RXDONE, RXTOUT or HEADER
  665. // therefore we wait. Make sure to clear the interrupt
  666. // as HEADER interrupt comes just before RXDONE
  667. else {
  668. # if _MONITOR>=1
  669. if (( debug>=0 ) && ( pdebug & P_RX )) {
  670. mPrint("R S_RX:: no RXDONE, RXTOUT, HEADER:: " + String(intr));
  671. }
  672. # endif //_MONITOR
  673. //writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00 );
  674. //writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
  675. }// int not RXDONE or RXTOUT
  676. break; // S_RX
  677. // ----------------------------------------------------------------------------------------
  678. // Start the transmission of a message in state S-TX
  679. // This is not an interrupt state, we use this state to start transmission
  680. // the interrupt TX-DONE tells us that the transmission was successful.
  681. // It therefore is no use to set _event==1 as transmission might
  682. // not be finished in the next loop iteration
  683. //
  684. case S_TX:
  685. // We need a timeout for this case. In case there does not come an interrupt,
  686. // then there will nog be a TXDONE but probably another CDDONE/CDDETD before
  687. // we have a timeout in the main program (Keep Alive)
  688. if (intr == 0x00) {
  689. # if _MONITOR>=1
  690. if (( debug>=2 ) && ( pdebug & P_TX )) {
  691. mPrint("TX:: 0x00");
  692. }
  693. # endif //_MONITOR
  694. _event=1;
  695. _state=S_TXDONE;
  696. }
  697. // Set state to transmit
  698. _state = S_TXDONE;
  699. // Clear interrupt flags and masks
  700. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
  701. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags
  702. // Initiate the transmission of the buffer (in Interrupt space)
  703. // We react on ALL interrupts if we are in TX state.
  704. txLoraModem(
  705. LoraDown.payLoad,
  706. LoraDown.payLength,
  707. LoraDown.tmst,
  708. LoraDown.sfTx,
  709. LoraDown.powe,
  710. LoraDown.fff,
  711. LoraDown.crc,
  712. LoraDown.iiq
  713. );
  714. // After filling the buffer we only react on TXDONE interrupt
  715. # if _MONITOR>=1
  716. if (( debug>=1 ) && ( pdebug & P_TX )) {
  717. mPrint("TX done:: " + String(intr) );
  718. }
  719. # endif //_MONITOR
  720. // More or less start at the "case TXDONE:" below
  721. _state=S_TXDONE;
  722. _event=1; // Or remove the break below
  723. break; // S_TX
  724. // ----------------------------------------------------------------------------------------
  725. // After the transmission is completed by the hardware,
  726. // the interrupt TXDONE is raised telling us that the tranmission
  727. // was successful.
  728. // If we receive an interrupt on dio0 _state==S_TX it is a TxDone interrupt
  729. // Do nothing with the interrupt, it is just an indication.
  730. // send Packet switch back to scanner mode after transmission finished OK
  731. //
  732. case S_TXDONE:
  733. if (intr & IRQ_LORA_TXDONE_MASK) {
  734. # if _MONITOR>=1
  735. if (( debug>=0 ) && ( pdebug & P_TX )) {
  736. String response = "T TXDONE:: rcvd=" + String(micros());
  737. response += ", diff=" + String(micros()-LoraDown.tmst);
  738. mPrint(response);
  739. }
  740. # endif //_MONITOR
  741. # if _MONITOR>=2
  742. if (pdebug & P_TX) {
  743. String response = "T TXDONE:: rcvd=" + micros();
  744. response += ", diff=" + String(micros()-LoraDown.tmst);
  745. mPrint(response);
  746. }
  747. # endif //_MONITOR
  748. // After transmission reset to receiver
  749. if ((gwayConfig.cad) || (gwayConfig.hop)) { // XXX 26/02
  750. // Set the state to CAD scanning
  751. _state = S_SCAN;
  752. sf = SF7;
  753. cadScanner(); // Start the scanner after TX cycle
  754. }
  755. else {
  756. _state = S_RX;
  757. rxLoraModem();
  758. }
  759. _event=0;
  760. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
  761. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags
  762. # if _MONITOR>=1
  763. if (( debug>=1 ) && ( pdebug & P_TX )) {
  764. mPrint("T TXDONE:: done OK");
  765. }
  766. # endif //_MONITOR
  767. }
  768. // If a soft _event==0 interrupt and no transmission finished:
  769. else if ( intr != 0 ) {
  770. # if _MONITOR>=1
  771. if (( debug>=0 ) && ( pdebug & P_TX )) {
  772. String response = "TXDONE:: unknown int:";
  773. mStat(intr, response);
  774. mPrint(response);
  775. } //_MONITOR
  776. # endif
  777. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
  778. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags
  779. _event=0;
  780. _state=S_SCAN;
  781. }
  782. // intr == 0
  783. else {
  784. // Increase timer. If timer exceeds certain value (7 seconds!), reset
  785. // After sending a message with S_TX, we have to receive a TXDONE interrupt
  786. // within 7 seconds according to spec, of here is a problem.
  787. if ( sendTime > micros() ) sendTime = 0; // This could be omitted for usigned ints
  788. if (( _state == S_TXDONE ) && (( micros() - sendTime) > 7000000 )) {
  789. # if _MONITOR>=1
  790. if (( debug>=1 ) && ( pdebug & P_TX )) {
  791. mPrint("TXDONE:: reset TX");
  792. }
  793. # endif //_MONITOR
  794. startReceiver();
  795. }
  796. # if _MONITOR>=1
  797. if (( debug>=3 ) && ( pdebug & P_TX )) {
  798. mPrint("T TXDONE:: No Interrupt");
  799. }
  800. # endif //_MONITOR
  801. }
  802. break; // S_TXDONE
  803. // ----------------------------------------------------------------------------------------
  804. // If _STATE is in an undefined state
  805. // If such a thing happens, we should re-init the interface and
  806. // make sure that we pick up next interrupt
  807. default:
  808. # if _MONITOR>=1
  809. if (( debug>=0) && ( pdebug & P_PRE )) {
  810. mPrint("ERR state=" + String(_state));
  811. }
  812. # endif //_MONITOR
  813. if ((gwayConfig.cad) || (gwayConfig.hop)) {
  814. # if _MONITOR>=1
  815. if (debug>=0) {
  816. String response = "default:: Unknown _state ";
  817. mStat(intr, response);
  818. mPrint(response);
  819. }
  820. # endif //_MONITOR
  821. _state = S_SCAN;
  822. sf = SF7;
  823. cadScanner(); // Restart the state machine
  824. _event=0;
  825. }
  826. else // Single channel AND single SF
  827. {
  828. _state = S_RX;
  829. rxLoraModem();
  830. _event=0;
  831. }
  832. writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
  833. writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset all interrupts
  834. eventTime=micros(); // Reset event for unkonwn state
  835. break;// default
  836. }// switch(_state)
  837. return;
  838. }