LoRaMacClassB.c 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767
  1. /*
  2. / _____) _ | |
  3. ( (____ _____ ____ _| |_ _____ ____| |__
  4. \____ \| ___ | (_ _) ___ |/ ___) _ \
  5. _____) ) ____| | | || |_| ____( (___| | | |
  6. (______/|_____)_|_|_| \__)_____)\____)_| |_|
  7. (C)2013 Semtech
  8. ___ _____ _ ___ _ _____ ___ ___ ___ ___
  9. / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
  10. \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
  11. |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
  12. embedded.connectivity.solutions===============
  13. Description: LoRa MAC Class B layer implementation
  14. License: Revised BSD License, see LICENSE.TXT file include in the project
  15. Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
  16. */
  17. #include <math.h>
  18. #include "utilities.h"
  19. #include "secure-element.h"
  20. #include "LoRaMac.h"
  21. #include "LoRaMacClassB.h"
  22. #include "LoRaMacClassBConfig.h"
  23. #include "LoRaMacCrypto.h"
  24. #include "LoRaMacConfirmQueue.h"
  25. #ifdef LORAMAC_CLASSB_ENABLED
  26. /*
  27. * LoRaMac Class B Context structure for NVM parameters
  28. * related to ping slots
  29. */
  30. typedef struct sLoRaMacClassBPingSlotNvmCtx
  31. {
  32. struct sPingSlotCtrlNvm
  33. {
  34. /*!
  35. * Set when the server assigned a ping slot to the node
  36. */
  37. uint8_t Assigned : 1;
  38. /*!
  39. * Set when a custom frequency is used
  40. */
  41. uint8_t CustomFreq : 1;
  42. }Ctrl;
  43. /*!
  44. * Number of ping slots
  45. */
  46. uint8_t PingNb;
  47. /*!
  48. * Period of the ping slots
  49. */
  50. uint16_t PingPeriod;
  51. /*!
  52. * Reception frequency of the ping slot windows
  53. */
  54. uint32_t Frequency;
  55. /*!
  56. * Datarate of the ping slot
  57. */
  58. int8_t Datarate;
  59. } LoRaMacClassBPingSlotNvmCtx_t;
  60. /*
  61. * LoRaMac Class B Context structure for NVM parameters
  62. * related to beaconing
  63. */
  64. typedef struct sLoRaMacClassBBeaconNvmCtx
  65. {
  66. struct sBeaconCtrlNvm
  67. {
  68. /*!
  69. * Set if the node has a custom frequency for beaconing and ping slots
  70. */
  71. uint8_t CustomFreq : 1;
  72. }Ctrl;
  73. /*!
  74. * Beacon reception frequency
  75. */
  76. uint32_t Frequency;
  77. } LoRaMacClassBBeaconNvmCtx_t;
  78. /*
  79. * LoRaMac Class B Context structure
  80. */
  81. typedef struct sLoRaMacClassBNvmCtx
  82. {
  83. /*!
  84. * Class B ping slot context
  85. */
  86. LoRaMacClassBPingSlotNvmCtx_t PingSlotCtx;
  87. /*!
  88. * Class B beacon context
  89. */
  90. LoRaMacClassBBeaconNvmCtx_t BeaconCtx;
  91. } LoRaMacClassBNvmCtx_t;
  92. /*
  93. * LoRaMac Class B Context structure
  94. */
  95. typedef struct sLoRaMacClassBCtx
  96. {
  97. /*!
  98. * Class B ping slot context
  99. */
  100. PingSlotContext_t PingSlotCtx;
  101. /*!
  102. * Class B beacon context
  103. */
  104. BeaconContext_t BeaconCtx;
  105. /*!
  106. * State of the beaconing mechanism
  107. */
  108. BeaconState_t BeaconState;
  109. /*!
  110. * State of the ping slot mechanism
  111. */
  112. PingSlotState_t PingSlotState;
  113. /*!
  114. * State of the multicast slot mechanism
  115. */
  116. PingSlotState_t MulticastSlotState;
  117. /*!
  118. * Timer for CLASS B beacon acquisition and tracking.
  119. */
  120. TimerEvent_t BeaconTimer;
  121. /*!
  122. * Timer for CLASS B ping slot timer.
  123. */
  124. TimerEvent_t PingSlotTimer;
  125. /*!
  126. * Timer for CLASS B multicast ping slot timer.
  127. */
  128. TimerEvent_t MulticastSlotTimer;
  129. /*!
  130. * Container for the callbacks related to class b.
  131. */
  132. LoRaMacClassBCallback_t LoRaMacClassBCallbacks;
  133. /*!
  134. * Data structure which holds the parameters which needs to be set
  135. * in class b operation.
  136. */
  137. LoRaMacClassBParams_t LoRaMacClassBParams;
  138. /*
  139. * Callback function to notify the upper layer about context change
  140. */
  141. LoRaMacClassBNvmEvent LoRaMacClassBNvmEvent;
  142. /*!
  143. * Non-volatile module context.
  144. */
  145. LoRaMacClassBNvmCtx_t* NvmCtx;
  146. } LoRaMacClassBCtx_t;
  147. /*!
  148. * Defines the LoRaMac radio events status
  149. */
  150. typedef union uLoRaMacClassBEvents
  151. {
  152. uint32_t Value;
  153. struct sEvents
  154. {
  155. uint32_t Beacon : 1;
  156. uint32_t PingSlot : 1;
  157. uint32_t MulticastSlot : 1;
  158. }Events;
  159. }LoRaMacClassBEvents_t;
  160. LoRaMacClassBEvents_t LoRaMacClassBEvents = { .Value = 0 };
  161. /*
  162. * Non-volatile module context.
  163. */
  164. static LoRaMacClassBNvmCtx_t NvmCtx;
  165. /*
  166. * Module context.
  167. */
  168. static LoRaMacClassBCtx_t Ctx;
  169. /*!
  170. * Computes the Ping Offset
  171. *
  172. * \param [IN] beaconTime - Time of the recent received beacon
  173. * \param [IN] address - Frame address
  174. * \param [IN] pingPeriod - Ping period of the node
  175. * \param [OUT] pingOffset - Pseudo random ping offset
  176. */
  177. static void ComputePingOffset( uint64_t beaconTime, uint32_t address, uint16_t pingPeriod, uint16_t *pingOffset )
  178. {
  179. uint8_t buffer[16];
  180. uint8_t cipher[16];
  181. uint32_t result = 0;
  182. /* Refer to chapter 15.2 of the LoRaWAN specification v1.1. The beacon time
  183. * GPS time in seconds modulo 2^32
  184. */
  185. uint32_t time = ( beaconTime % ( ( ( uint64_t ) 1 ) << 32 ) );
  186. memset1( buffer, 0, 16 );
  187. memset1( cipher, 0, 16 );
  188. buffer[0] = ( time ) & 0xFF;
  189. buffer[1] = ( time >> 8 ) & 0xFF;
  190. buffer[2] = ( time >> 16 ) & 0xFF;
  191. buffer[3] = ( time >> 24 ) & 0xFF;
  192. buffer[4] = ( address ) & 0xFF;
  193. buffer[5] = ( address >> 8 ) & 0xFF;
  194. buffer[6] = ( address >> 16 ) & 0xFF;
  195. buffer[7] = ( address >> 24 ) & 0xFF;
  196. SecureElementAesEncrypt( buffer, 16, SLOT_RAND_ZERO_KEY, cipher );
  197. result = ( ( ( uint32_t ) cipher[0] ) + ( ( ( uint32_t ) cipher[1] ) * 256 ) );
  198. *pingOffset = ( uint16_t )( result % pingPeriod );
  199. }
  200. /*!
  201. * \brief Calculates the downlink frequency for a given channel.
  202. *
  203. * \param [IN] channel The channel according to the channel plan.
  204. *
  205. * \retval The downlink frequency
  206. */
  207. static uint32_t CalcDownlinkFrequency( uint8_t channel )
  208. {
  209. GetPhyParams_t getPhy;
  210. PhyParam_t phyParam;
  211. uint32_t frequency = 0;
  212. uint32_t stepwidth = 0;
  213. getPhy.Attribute = PHY_BEACON_CHANNEL_FREQ;
  214. phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
  215. frequency = phyParam.Value;
  216. getPhy.Attribute = PHY_BEACON_CHANNEL_STEPWIDTH;
  217. phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
  218. stepwidth = phyParam.Value;
  219. // Calculate the frequency
  220. return frequency + ( channel * stepwidth );
  221. }
  222. /*!
  223. * \brief Calculates the downlink channel for the beacon and for
  224. * ping slot downlinks.
  225. *
  226. * \param [IN] devAddr The address of the device
  227. *
  228. * \param [IN] beaconTime The beacon time of the beacon.
  229. *
  230. * \param [IN] beaconInterval The beacon interval
  231. *
  232. * \retval The downlink channel
  233. */
  234. static uint32_t CalcDownlinkChannelAndFrequency( uint32_t devAddr, TimerTime_t beaconTime, TimerTime_t beaconInterval )
  235. {
  236. GetPhyParams_t getPhy;
  237. PhyParam_t phyParam;
  238. uint32_t channel = 0;
  239. uint8_t nbChannels = 0;
  240. uint32_t frequency = 0;
  241. getPhy.Attribute = PHY_BEACON_NB_CHANNELS;
  242. phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
  243. nbChannels = (uint8_t) phyParam.Value;
  244. if( nbChannels > 1 )
  245. {
  246. // Calculate the channel for the next downlink
  247. channel = devAddr + ( beaconTime / ( beaconInterval / 1000 ) );
  248. channel = channel % nbChannels;
  249. }
  250. // Calculate the frequency for the next downlink
  251. frequency = CalcDownlinkFrequency( channel );
  252. // Calculate the frequency for the next downlink
  253. return frequency;
  254. }
  255. /*!
  256. * \brief Calculates the correct frequency and opens up the beacon reception window.
  257. *
  258. * \param [IN] rxTime The reception time which should be setup
  259. *
  260. * \param [IN] activateDefaultChannel Set to true, if the function shall setup the default channel
  261. */
  262. static void RxBeaconSetup( TimerTime_t rxTime, bool activateDefaultChannel )
  263. {
  264. RxBeaconSetup_t rxBeaconSetup;
  265. uint32_t frequency = 0;
  266. RxConfigParams_t beaconRxConfig;
  267. GetPhyParams_t getPhy;
  268. PhyParam_t phyParam;
  269. uint16_t windowTimeout = Ctx.BeaconCtx.SymbolTimeout;
  270. if( activateDefaultChannel == true )
  271. {
  272. // This is the default frequency in case we don't know when the next
  273. // beacon will be transmitted. We select channel 0 as default.
  274. frequency = CalcDownlinkFrequency( 0 );
  275. }
  276. else
  277. {
  278. // This is the frequency according to the channel plan
  279. frequency = CalcDownlinkChannelAndFrequency( 0, Ctx.BeaconCtx.BeaconTime.Seconds + ( CLASSB_BEACON_INTERVAL / 1000 ),
  280. CLASSB_BEACON_INTERVAL );
  281. }
  282. if( Ctx.NvmCtx->BeaconCtx.Ctrl.CustomFreq == 1 )
  283. {
  284. // Set the frequency from the BeaconFreqReq
  285. frequency = Ctx.NvmCtx->BeaconCtx.Frequency;
  286. }
  287. if( Ctx.BeaconCtx.Ctrl.BeaconChannelSet == 1 )
  288. {
  289. // Set the frequency which was provided by BeaconTimingAns MAC command
  290. Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 0;
  291. frequency = CalcDownlinkFrequency( Ctx.BeaconCtx.BeaconTimingChannel );
  292. }
  293. if( ( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 ) || ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) )
  294. {
  295. // Apply the symbol timeout only if we have acquired the beacon
  296. // Otherwise, take the window enlargement into account
  297. // Read beacon datarate
  298. getPhy.Attribute = PHY_BEACON_CHANNEL_DR;
  299. phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
  300. // Calculate downlink symbols
  301. RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion,
  302. ( int8_t )phyParam.Value, // datarate
  303. Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
  304. Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError,
  305. &beaconRxConfig );
  306. windowTimeout = beaconRxConfig.WindowTimeout;
  307. }
  308. rxBeaconSetup.SymbolTimeout = windowTimeout;
  309. rxBeaconSetup.RxTime = rxTime;
  310. rxBeaconSetup.Frequency = frequency;
  311. RegionRxBeaconSetup( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate );
  312. Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Frequency = frequency;
  313. Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Datarate = Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate;
  314. }
  315. /*!
  316. * \brief Calculates the next ping slot time.
  317. *
  318. * \param [IN] slotOffset The ping slot offset
  319. * \param [IN] pingPeriod The ping period
  320. * \param [OUT] timeOffset Time offset of the next slot, based on current time
  321. *
  322. * \retval [true: ping slot found, false: no ping slot found]
  323. */
  324. static bool CalcNextSlotTime( uint16_t slotOffset, uint16_t pingPeriod, uint16_t pingNb, TimerTime_t* timeOffset )
  325. {
  326. uint8_t currentPingSlot = 0;
  327. TimerTime_t slotTime = 0;
  328. TimerTime_t currentTime = TimerGetCurrentTime( );
  329. // Calculate the point in time of the last beacon even if we missed it
  330. slotTime = ( ( currentTime - SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) ) % CLASSB_BEACON_INTERVAL );
  331. slotTime = currentTime - slotTime;
  332. // Add the reserved time and the ping offset
  333. slotTime += CLASSB_BEACON_RESERVED;
  334. slotTime += slotOffset * CLASSB_PING_SLOT_WINDOW;
  335. if( slotTime < currentTime )
  336. {
  337. currentPingSlot = ( ( currentTime - slotTime ) /
  338. ( pingPeriod * CLASSB_PING_SLOT_WINDOW ) ) + 1;
  339. slotTime += ( ( TimerTime_t )( currentPingSlot * pingPeriod ) *
  340. CLASSB_PING_SLOT_WINDOW );
  341. }
  342. if( currentPingSlot < pingNb )
  343. {
  344. if( slotTime <= ( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - CLASSB_BEACON_GUARD - CLASSB_PING_SLOT_WINDOW ) )
  345. {
  346. // Calculate the relative ping slot time
  347. slotTime -= currentTime;
  348. slotTime -= Radio.GetWakeupTime( );
  349. slotTime = TimerTempCompensation( slotTime, Ctx.BeaconCtx.Temperature );
  350. *timeOffset = slotTime;
  351. return true;
  352. }
  353. }
  354. return false;
  355. }
  356. /*!
  357. * \brief Calculates CRC's of the beacon frame
  358. *
  359. * \param [IN] buffer Pointer to the data
  360. * \param [IN] length Length of the data
  361. *
  362. * \retval CRC
  363. */
  364. static uint16_t BeaconCrc( uint8_t *buffer, uint16_t length )
  365. {
  366. // The CRC calculation follows CCITT
  367. const uint16_t polynom = 0x1021;
  368. // CRC initial value
  369. uint16_t crc = 0x0000;
  370. if( buffer == NULL )
  371. {
  372. return 0;
  373. }
  374. for( uint16_t i = 0; i < length; ++i )
  375. {
  376. crc ^= ( uint16_t ) buffer[i] << 8;
  377. for( uint16_t j = 0; j < 8; ++j )
  378. {
  379. crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 );
  380. }
  381. }
  382. return crc;
  383. }
  384. static void GetTemperatureLevel( LoRaMacClassBCallback_t *callbacks, BeaconContext_t *beaconCtx )
  385. {
  386. // Measure temperature, if available
  387. if( ( callbacks != NULL ) && ( callbacks->GetTemperatureLevel != NULL ) )
  388. {
  389. beaconCtx->Temperature = callbacks->GetTemperatureLevel( );
  390. }
  391. }
  392. static void InitClassB( void )
  393. {
  394. GetPhyParams_t getPhy;
  395. PhyParam_t phyParam;
  396. // Init events
  397. LoRaMacClassBEvents.Value = 0;
  398. // Init variables to default
  399. memset1( ( uint8_t* ) &NvmCtx, 0, sizeof( LoRaMacClassBNvmCtx_t ) );
  400. memset1( ( uint8_t* ) &Ctx.PingSlotCtx, 0, sizeof( PingSlotContext_t ) );
  401. memset1( ( uint8_t* ) &Ctx.BeaconCtx, 0, sizeof( BeaconContext_t ) );
  402. // Setup default temperature
  403. Ctx.BeaconCtx.Temperature = 25.0;
  404. GetTemperatureLevel( &Ctx.LoRaMacClassBCallbacks, &Ctx.BeaconCtx );
  405. // Setup default ping slot datarate
  406. getPhy.Attribute = PHY_PING_SLOT_CHANNEL_DR;
  407. phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
  408. Ctx.NvmCtx->PingSlotCtx.Datarate = (int8_t)( phyParam.Value );
  409. // Setup default states
  410. Ctx.BeaconState = BEACON_STATE_ACQUISITION;
  411. Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
  412. Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
  413. }
  414. static void InitClassBDefaults( void )
  415. {
  416. // This function shall reset the Class B settings to default,
  417. // but should keep important configurations
  418. LoRaMacClassBBeaconNvmCtx_t beaconCtx = Ctx.NvmCtx->BeaconCtx;
  419. LoRaMacClassBPingSlotNvmCtx_t pingSlotCtx = Ctx.NvmCtx->PingSlotCtx;
  420. InitClassB( );
  421. // Parameters from BeaconFreqReq
  422. Ctx.NvmCtx->BeaconCtx.Frequency = beaconCtx.Frequency;
  423. Ctx.NvmCtx->BeaconCtx.Ctrl.CustomFreq = beaconCtx.Ctrl.CustomFreq;
  424. // Parameters from PingSlotChannelReq
  425. Ctx.NvmCtx->PingSlotCtx.Ctrl.CustomFreq = pingSlotCtx.Ctrl.CustomFreq;
  426. Ctx.NvmCtx->PingSlotCtx.Frequency = pingSlotCtx.Frequency;
  427. Ctx.NvmCtx->PingSlotCtx.Datarate = pingSlotCtx.Datarate;
  428. }
  429. static void EnlargeWindowTimeout( void )
  430. {
  431. // Update beacon movement
  432. Ctx.BeaconCtx.BeaconWindowMovement *= CLASSB_WINDOW_MOVE_EXPANSION_FACTOR;
  433. if( Ctx.BeaconCtx.BeaconWindowMovement > CLASSB_WINDOW_MOVE_EXPANSION_MAX )
  434. {
  435. Ctx.BeaconCtx.BeaconWindowMovement = CLASSB_WINDOW_MOVE_EXPANSION_MAX;
  436. }
  437. // Update symbol timeout
  438. Ctx.BeaconCtx.SymbolTimeout *= CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
  439. if( Ctx.BeaconCtx.SymbolTimeout > CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX )
  440. {
  441. Ctx.BeaconCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX;
  442. }
  443. Ctx.PingSlotCtx.SymbolTimeout *= CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR;
  444. if( Ctx.PingSlotCtx.SymbolTimeout > CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX )
  445. {
  446. Ctx.PingSlotCtx.SymbolTimeout = CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX;
  447. }
  448. }
  449. static void ResetWindowTimeout( void )
  450. {
  451. Ctx.BeaconCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_DEFAULT;
  452. Ctx.PingSlotCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_DEFAULT;
  453. Ctx.BeaconCtx.BeaconWindowMovement = CLASSB_WINDOW_MOVE_DEFAULT;
  454. }
  455. static TimerTime_t CalcDelayForNextBeacon( TimerTime_t currentTime, TimerTime_t lastBeaconRx )
  456. {
  457. TimerTime_t nextBeaconRxTime = 0;
  458. // Calculate the point in time of the next beacon
  459. nextBeaconRxTime = ( ( currentTime - lastBeaconRx ) % CLASSB_BEACON_INTERVAL );
  460. return ( CLASSB_BEACON_INTERVAL - nextBeaconRxTime );
  461. }
  462. static void IndicateBeaconStatus( LoRaMacEventInfoStatus_t status )
  463. {
  464. if( Ctx.BeaconCtx.Ctrl.ResumeBeaconing == 0 )
  465. {
  466. Ctx.LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON;
  467. Ctx.LoRaMacClassBParams.MlmeIndication->Status = status;
  468. Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1;
  469. Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1;
  470. }
  471. Ctx.BeaconCtx.Ctrl.ResumeBeaconing = 0;
  472. }
  473. static TimerTime_t ApplyGuardTime( TimerTime_t beaconEventTime )
  474. {
  475. TimerTime_t timeGuard = beaconEventTime;
  476. if( timeGuard > CLASSB_BEACON_GUARD )
  477. {
  478. timeGuard -= CLASSB_BEACON_GUARD;
  479. }
  480. return timeGuard;
  481. }
  482. static TimerTime_t UpdateBeaconState( LoRaMacEventInfoStatus_t status,
  483. TimerTime_t windowMovement, TimerTime_t currentTime )
  484. {
  485. TimerTime_t beaconEventTime = 0;
  486. // Calculate the next beacon RX time
  487. beaconEventTime = CalcDelayForNextBeacon( currentTime, SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) );
  488. Ctx.BeaconCtx.NextBeaconRx = SysTimeFromMs( currentTime + beaconEventTime );
  489. // Take temperature compensation into account
  490. beaconEventTime = TimerTempCompensation( beaconEventTime, Ctx.BeaconCtx.Temperature );
  491. // Move the window
  492. if( beaconEventTime > windowMovement )
  493. {
  494. beaconEventTime -= windowMovement;
  495. }
  496. Ctx.BeaconCtx.NextBeaconRxAdjusted = currentTime + beaconEventTime;
  497. // Start the RX slot state machine for ping and multicast slots
  498. LoRaMacClassBStartRxSlots( );
  499. // Setup an MLME_BEACON indication to inform the upper layer
  500. IndicateBeaconStatus( status );
  501. // Apply guard time
  502. return ApplyGuardTime( beaconEventTime );
  503. }
  504. static uint8_t CalcPingNb( uint16_t periodicity )
  505. {
  506. return 128 / ( 1 << periodicity );
  507. }
  508. static uint16_t CalcPingPeriod( uint8_t pingNb )
  509. {
  510. return CLASSB_BEACON_WINDOW_SLOTS / pingNb;
  511. }
  512. /*
  513. * Dummy callback in case if the user provides NULL function pointer
  514. */
  515. static void NvmContextChange( void )
  516. {
  517. if( Ctx.LoRaMacClassBNvmEvent != NULL )
  518. {
  519. Ctx.LoRaMacClassBNvmEvent( );
  520. }
  521. }
  522. #endif // LORAMAC_CLASSB_ENABLED
  523. void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks, LoRaMacClassBNvmEvent classBNvmCtxChanged )
  524. {
  525. #ifdef LORAMAC_CLASSB_ENABLED
  526. // Store callbacks
  527. Ctx.LoRaMacClassBCallbacks = *callbacks;
  528. // Store parameter pointers
  529. Ctx.LoRaMacClassBParams = *classBParams;
  530. // Assign non-volatile context
  531. Ctx.NvmCtx = &NvmCtx;
  532. // Assign callback
  533. Ctx.LoRaMacClassBNvmEvent = classBNvmCtxChanged;
  534. // Initialize timers
  535. TimerInit( &Ctx.BeaconTimer, LoRaMacClassBBeaconTimerEvent );
  536. TimerInit( &Ctx.PingSlotTimer, LoRaMacClassBPingSlotTimerEvent );
  537. TimerInit( &Ctx.MulticastSlotTimer, LoRaMacClassBMulticastSlotTimerEvent );
  538. InitClassB( );
  539. #endif // LORAMAC_CLASSB_ENABLED
  540. }
  541. bool LoRaMacClassBRestoreNvmCtx( void* classBNvmCtx )
  542. {
  543. #ifdef LORAMAC_CLASSB_ENABLED
  544. // Restore module context
  545. if( classBNvmCtx != NULL )
  546. {
  547. memcpy1( ( uint8_t* ) &NvmCtx, ( uint8_t* ) classBNvmCtx, sizeof( NvmCtx ) );
  548. return true;
  549. }
  550. else
  551. {
  552. return false;
  553. }
  554. #else
  555. return true;
  556. #endif // LORAMAC_CLASSB_ENABLED
  557. }
  558. void* LoRaMacClassBGetNvmCtx( size_t* classBNvmCtxSize )
  559. {
  560. #ifdef LORAMAC_CLASSB_ENABLED
  561. *classBNvmCtxSize = sizeof( NvmCtx );
  562. return &NvmCtx;
  563. #else
  564. *classBNvmCtxSize = 0;
  565. return NULL;
  566. #endif // LORAMAC_CLASSB_ENABLED
  567. }
  568. void LoRaMacClassBSetBeaconState( BeaconState_t beaconState )
  569. {
  570. #ifdef LORAMAC_CLASSB_ENABLED
  571. if( beaconState == BEACON_STATE_ACQUISITION )
  572. {
  573. // If the MAC has received a time reference for the beacon,
  574. // apply the state BEACON_STATE_ACQUISITION_BY_TIME.
  575. if( ( Ctx.BeaconCtx.Ctrl.BeaconDelaySet == 1 ) &&
  576. ( LoRaMacClassBIsAcquisitionPending( ) == false ) )
  577. {
  578. Ctx.BeaconState = BEACON_STATE_ACQUISITION_BY_TIME;
  579. }
  580. else
  581. {
  582. Ctx.BeaconState = beaconState;
  583. }
  584. }
  585. else
  586. {
  587. if( ( Ctx.BeaconState != BEACON_STATE_ACQUISITION ) &&
  588. ( Ctx.BeaconState != BEACON_STATE_ACQUISITION_BY_TIME ) )
  589. {
  590. Ctx.BeaconState = beaconState;
  591. }
  592. }
  593. #endif // LORAMAC_CLASSB_ENABLED
  594. }
  595. void LoRaMacClassBSetPingSlotState( PingSlotState_t pingSlotState )
  596. {
  597. #ifdef LORAMAC_CLASSB_ENABLED
  598. Ctx.PingSlotState = pingSlotState;
  599. #endif // LORAMAC_CLASSB_ENABLED
  600. }
  601. void LoRaMacClassBSetMulticastSlotState( PingSlotState_t multicastSlotState )
  602. {
  603. #ifdef LORAMAC_CLASSB_ENABLED
  604. Ctx.MulticastSlotState = multicastSlotState;
  605. #endif // LORAMAC_CLASSB_ENABLED
  606. }
  607. bool LoRaMacClassBIsAcquisitionInProgress( void )
  608. {
  609. #ifdef LORAMAC_CLASSB_ENABLED
  610. if( Ctx.BeaconState == BEACON_STATE_ACQUISITION_BY_TIME )
  611. {
  612. // In this case the acquisition is in progress, as the MAC has
  613. // a time reference for the next beacon RX.
  614. return true;
  615. }
  616. if( LoRaMacClassBIsAcquisitionPending( ) == true )
  617. {
  618. // In this case the acquisition is in progress, as the MAC
  619. // searches for a beacon.
  620. return true;
  621. }
  622. return false;
  623. #else
  624. return false;
  625. #endif // LORAMAC_CLASSB_ENABLED
  626. }
  627. void LoRaMacClassBBeaconTimerEvent( void* context )
  628. {
  629. #ifdef LORAMAC_CLASSB_ENABLED
  630. Ctx.BeaconCtx.TimeStamp = TimerGetCurrentTime( );
  631. TimerStop( &Ctx.BeaconTimer );
  632. LoRaMacClassBEvents.Events.Beacon = 1;
  633. if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL )
  634. {
  635. Ctx.LoRaMacClassBCallbacks.MacProcessNotify( );
  636. }
  637. #endif // LORAMAC_CLASSB_ENABLED
  638. }
  639. #ifdef LORAMAC_CLASSB_ENABLED
  640. static void LoRaMacClassBProcessBeacon( void )
  641. {
  642. bool activateTimer = false;
  643. TimerTime_t beaconEventTime = 1;
  644. TimerTime_t currentTime = Ctx.BeaconCtx.TimeStamp;
  645. // Beacon state machine
  646. switch( Ctx.BeaconState )
  647. {
  648. case BEACON_STATE_ACQUISITION_BY_TIME:
  649. {
  650. activateTimer = true;
  651. if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 )
  652. {
  653. Radio.Sleep();
  654. Ctx.BeaconState = BEACON_STATE_LOST;
  655. }
  656. else
  657. {
  658. // Default symbol timeouts
  659. ResetWindowTimeout( );
  660. if( Ctx.BeaconCtx.Ctrl.BeaconDelaySet == 1 )
  661. {
  662. if( Ctx.BeaconCtx.BeaconTimingDelay > 0 )
  663. {
  664. if( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) > currentTime )
  665. {
  666. beaconEventTime = TimerTempCompensation( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - currentTime, Ctx.BeaconCtx.Temperature );
  667. }
  668. else
  669. {
  670. // Reset status provides by BeaconTimingAns
  671. Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 0;
  672. Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 0;
  673. Ctx.BeaconState = BEACON_STATE_ACQUISITION;
  674. }
  675. Ctx.BeaconCtx.BeaconTimingDelay = 0;
  676. }
  677. else
  678. {
  679. activateTimer = false;
  680. // Reset status provides by BeaconTimingAns
  681. Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 0;
  682. // Set the node into acquisition mode
  683. Ctx.BeaconCtx.Ctrl.AcquisitionPending = 1;
  684. // Don't use the default channel. We know on which
  685. // channel the next beacon will be transmitted
  686. RxBeaconSetup( CLASSB_BEACON_RESERVED, false );
  687. }
  688. }
  689. else
  690. {
  691. Ctx.BeaconCtx.NextBeaconRx.Seconds = 0;
  692. Ctx.BeaconCtx.NextBeaconRx.SubSeconds = 0;
  693. Ctx.BeaconCtx.BeaconTimingDelay = 0;
  694. Ctx.BeaconState = BEACON_STATE_ACQUISITION;
  695. }
  696. }
  697. break;
  698. }
  699. case BEACON_STATE_ACQUISITION:
  700. {
  701. activateTimer = true;
  702. if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 )
  703. {
  704. Radio.Sleep();
  705. Ctx.BeaconState = BEACON_STATE_LOST;
  706. }
  707. else
  708. {
  709. // Default symbol timeouts
  710. ResetWindowTimeout( );
  711. Ctx.BeaconCtx.Ctrl.AcquisitionPending = 1;
  712. beaconEventTime = CLASSB_BEACON_INTERVAL;
  713. // Start the beacon acquisition. When the MAC has received a beacon in function
  714. // RxBeacon successfully, the next state is BEACON_STATE_LOCKED. If the MAC does not
  715. // find a beacon, the state machine will stay in state BEACON_STATE_ACQUISITION.
  716. // This state detects that a acquisition was pending previously and will change the next
  717. // state to BEACON_STATE_LOST.
  718. RxBeaconSetup( 0, true );
  719. }
  720. break;
  721. }
  722. case BEACON_STATE_TIMEOUT:
  723. {
  724. // We have to update the beacon time, since we missed a beacon
  725. Ctx.BeaconCtx.BeaconTime.Seconds += ( CLASSB_BEACON_INTERVAL / 1000 );
  726. Ctx.BeaconCtx.BeaconTime.SubSeconds = 0;
  727. // Enlarge window timeouts to increase the chance to receive the next beacon
  728. EnlargeWindowTimeout( );
  729. // Setup next state
  730. Ctx.BeaconState = BEACON_STATE_REACQUISITION;
  731. }
  732. // Intentional fall through
  733. case BEACON_STATE_REACQUISITION:
  734. {
  735. activateTimer = true;
  736. // The beacon is no longer acquired
  737. Ctx.BeaconCtx.Ctrl.BeaconAcquired = 0;
  738. // Verify if the maximum beacon less period has been elapsed
  739. if( ( currentTime - SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) ) > CLASSB_MAX_BEACON_LESS_PERIOD )
  740. {
  741. Ctx.BeaconState = BEACON_STATE_LOST;
  742. }
  743. else
  744. {
  745. // Handle beacon miss
  746. beaconEventTime = UpdateBeaconState( LORAMAC_EVENT_INFO_STATUS_BEACON_LOST,
  747. Ctx.BeaconCtx.BeaconWindowMovement, currentTime );
  748. // Setup next state
  749. Ctx.BeaconState = BEACON_STATE_IDLE;
  750. }
  751. break;
  752. }
  753. case BEACON_STATE_LOCKED:
  754. {
  755. activateTimer = true;
  756. // We have received a beacon. Acquisition is no longer pending.
  757. Ctx.BeaconCtx.Ctrl.AcquisitionPending = 0;
  758. // Handle beacon reception
  759. beaconEventTime = UpdateBeaconState( LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED,
  760. 0, currentTime );
  761. // Setup the MLME confirm for the MLME_BEACON_ACQUISITION
  762. if( Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 )
  763. {
  764. if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true )
  765. {
  766. LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_ACQUISITION );
  767. Ctx.LoRaMacClassBParams.MlmeConfirm->TxTimeOnAir = 0;
  768. }
  769. }
  770. // Setup next state
  771. Ctx.BeaconState = BEACON_STATE_IDLE;
  772. break;
  773. }
  774. case BEACON_STATE_IDLE:
  775. {
  776. activateTimer = true;
  777. GetTemperatureLevel( &Ctx.LoRaMacClassBCallbacks, &Ctx.BeaconCtx );
  778. beaconEventTime = Ctx.BeaconCtx.NextBeaconRxAdjusted - Radio.GetWakeupTime( );
  779. currentTime = TimerGetCurrentTime( );
  780. if( beaconEventTime > currentTime )
  781. {
  782. Ctx.BeaconState = BEACON_STATE_GUARD;
  783. beaconEventTime -= currentTime;
  784. beaconEventTime = TimerTempCompensation( beaconEventTime, Ctx.BeaconCtx.Temperature );
  785. }
  786. else
  787. {
  788. Ctx.BeaconState = BEACON_STATE_REACQUISITION;
  789. beaconEventTime = 1;
  790. }
  791. break;
  792. }
  793. case BEACON_STATE_GUARD:
  794. {
  795. Ctx.BeaconState = BEACON_STATE_RX;
  796. // Stop slot timers
  797. LoRaMacClassBStopRxSlots( );
  798. // Don't use the default channel. We know on which
  799. // channel the next beacon will be transmitted
  800. RxBeaconSetup( CLASSB_BEACON_RESERVED, false );
  801. break;
  802. }
  803. case BEACON_STATE_LOST:
  804. {
  805. // Handle events
  806. if( Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 )
  807. {
  808. if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true )
  809. {
  810. LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_BEACON_ACQUISITION );
  811. }
  812. }
  813. else
  814. {
  815. Ctx.LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON_LOST;
  816. Ctx.LoRaMacClassBParams.MlmeIndication->Status = LORAMAC_EVENT_INFO_STATUS_OK;
  817. Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1;
  818. }
  819. // Stop slot timers
  820. LoRaMacClassBStopRxSlots( );
  821. // Initialize default state for class b
  822. InitClassBDefaults( );
  823. Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1;
  824. break;
  825. }
  826. default:
  827. {
  828. Ctx.BeaconState = BEACON_STATE_ACQUISITION;
  829. break;
  830. }
  831. }
  832. if( activateTimer == true )
  833. {
  834. TimerSetValue( &Ctx.BeaconTimer, beaconEventTime );
  835. TimerStart( &Ctx.BeaconTimer );
  836. }
  837. }
  838. #endif // LORAMAC_CLASSB_ENABLED
  839. void LoRaMacClassBPingSlotTimerEvent( void* context )
  840. {
  841. #ifdef LORAMAC_CLASSB_ENABLED
  842. LoRaMacClassBEvents.Events.PingSlot = 1;
  843. if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL )
  844. {
  845. Ctx.LoRaMacClassBCallbacks.MacProcessNotify( );
  846. }
  847. #endif // LORAMAC_CLASSB_ENABLED
  848. }
  849. #ifdef LORAMAC_CLASSB_ENABLED
  850. static void LoRaMacClassBProcessPingSlot( void )
  851. {
  852. static RxConfigParams_t pingSlotRxConfig;
  853. TimerTime_t pingSlotTime = 0;
  854. switch( Ctx.PingSlotState )
  855. {
  856. case PINGSLOT_STATE_CALC_PING_OFFSET:
  857. {
  858. ComputePingOffset( Ctx.BeaconCtx.BeaconTime.Seconds,
  859. *Ctx.LoRaMacClassBParams.LoRaMacDevAddr,
  860. Ctx.NvmCtx->PingSlotCtx.PingPeriod,
  861. &( Ctx.PingSlotCtx.PingOffset ) );
  862. Ctx.PingSlotState = PINGSLOT_STATE_SET_TIMER;
  863. }
  864. // Intentional fall through
  865. case PINGSLOT_STATE_SET_TIMER:
  866. {
  867. if( CalcNextSlotTime( Ctx.PingSlotCtx.PingOffset, Ctx.NvmCtx->PingSlotCtx.PingPeriod, Ctx.NvmCtx->PingSlotCtx.PingNb, &pingSlotTime ) == true )
  868. {
  869. if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 )
  870. {
  871. // Compute the symbol timeout. Apply it only, if the beacon is acquired
  872. // Otherwise, take the enlargement of the symbols into account.
  873. RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion,
  874. Ctx.NvmCtx->PingSlotCtx.Datarate,
  875. Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
  876. Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError,
  877. &pingSlotRxConfig );
  878. Ctx.PingSlotCtx.SymbolTimeout = pingSlotRxConfig.WindowTimeout;
  879. if( ( int32_t )pingSlotTime > pingSlotRxConfig.WindowOffset )
  880. {// Apply the window offset
  881. pingSlotTime += pingSlotRxConfig.WindowOffset;
  882. }
  883. }
  884. // Start the timer if the ping slot time is in range
  885. Ctx.PingSlotState = PINGSLOT_STATE_IDLE;
  886. TimerSetValue( &Ctx.PingSlotTimer, pingSlotTime );
  887. TimerStart( &Ctx.PingSlotTimer );
  888. }
  889. break;
  890. }
  891. case PINGSLOT_STATE_IDLE:
  892. {
  893. uint32_t frequency = Ctx.NvmCtx->PingSlotCtx.Frequency;
  894. // Apply a custom frequency if the following bit is set
  895. if( Ctx.NvmCtx->PingSlotCtx.Ctrl.CustomFreq == 0 )
  896. {
  897. // Restore floor plan
  898. frequency = CalcDownlinkChannelAndFrequency( *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, Ctx.BeaconCtx.BeaconTime.Seconds, CLASSB_BEACON_INTERVAL );
  899. }
  900. // Open the ping slot window only, if there is no multicast ping slot
  901. // open. Multicast ping slots have always priority
  902. if( Ctx.MulticastSlotState != PINGSLOT_STATE_RX )
  903. {
  904. Ctx.PingSlotState = PINGSLOT_STATE_RX;
  905. pingSlotRxConfig.Datarate = Ctx.NvmCtx->PingSlotCtx.Datarate;
  906. pingSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
  907. pingSlotRxConfig.RepeaterSupport = Ctx.LoRaMacClassBParams.LoRaMacParams->RepeaterSupport;
  908. pingSlotRxConfig.Frequency = frequency;
  909. pingSlotRxConfig.RxContinuous = false;
  910. pingSlotRxConfig.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT;
  911. RegionRxConfig( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &pingSlotRxConfig, ( int8_t* )&Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate );
  912. if( pingSlotRxConfig.RxContinuous == false )
  913. {
  914. Radio.Rx( Ctx.LoRaMacClassBParams.LoRaMacParams->MaxRxWindow );
  915. }
  916. else
  917. {
  918. Radio.Rx( 0 ); // Continuous mode
  919. }
  920. }
  921. else
  922. {
  923. // Multicast slots have priority. Skip Rx
  924. Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
  925. TimerSetValue( &Ctx.PingSlotTimer, CLASSB_PING_SLOT_WINDOW );
  926. TimerStart( &Ctx.PingSlotTimer );
  927. }
  928. break;
  929. }
  930. default:
  931. {
  932. Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
  933. break;
  934. }
  935. }
  936. }
  937. #endif // LORAMAC_CLASSB_ENABLED
  938. void LoRaMacClassBMulticastSlotTimerEvent( void* context )
  939. {
  940. #ifdef LORAMAC_CLASSB_ENABLED
  941. LoRaMacClassBEvents.Events.MulticastSlot = 1;
  942. if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL )
  943. {
  944. Ctx.LoRaMacClassBCallbacks.MacProcessNotify( );
  945. }
  946. #endif // LORAMAC_CLASSB_ENABLED
  947. }
  948. #ifdef LORAMAC_CLASSB_ENABLED
  949. static void LoRaMacClassBProcessMulticastSlot( void )
  950. {
  951. static RxConfigParams_t multicastSlotRxConfig;
  952. TimerTime_t multicastSlotTime = 0;
  953. TimerTime_t slotTime = 0;
  954. MulticastCtx_t *cur = Ctx.LoRaMacClassBParams.MulticastChannels;
  955. if( cur == NULL )
  956. {
  957. return;
  958. }
  959. if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX )
  960. {
  961. // A multicast slot is already open
  962. return;
  963. }
  964. switch( Ctx.MulticastSlotState )
  965. {
  966. case PINGSLOT_STATE_CALC_PING_OFFSET:
  967. {
  968. // Compute all offsets for every multicast slots
  969. for( uint8_t i = 0; i < 4; i++ )
  970. {
  971. ComputePingOffset( Ctx.BeaconCtx.BeaconTime.Seconds,
  972. cur->ChannelParams.Address,
  973. cur->PingPeriod,
  974. &( cur->PingOffset ) );
  975. cur++;
  976. }
  977. Ctx.MulticastSlotState = PINGSLOT_STATE_SET_TIMER;
  978. }
  979. // Intentional fall through
  980. case PINGSLOT_STATE_SET_TIMER:
  981. {
  982. cur = Ctx.LoRaMacClassBParams.MulticastChannels;
  983. Ctx.PingSlotCtx.NextMulticastChannel = NULL;
  984. for( uint8_t i = 0; i < 4; i++ )
  985. {
  986. // Calculate the next slot time for every multicast slot
  987. if( CalcNextSlotTime( cur->PingOffset, cur->PingPeriod, cur->PingNb, &slotTime ) == true )
  988. {
  989. if( ( multicastSlotTime == 0 ) || ( multicastSlotTime > slotTime ) )
  990. {
  991. // Update the slot time and the next multicast channel
  992. multicastSlotTime = slotTime;
  993. Ctx.PingSlotCtx.NextMulticastChannel = cur;
  994. }
  995. }
  996. cur++;
  997. }
  998. // Schedule the next multicast slot
  999. if( Ctx.PingSlotCtx.NextMulticastChannel != NULL )
  1000. {
  1001. if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 )
  1002. {
  1003. RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion,
  1004. Ctx.NvmCtx->PingSlotCtx.Datarate,
  1005. Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols,
  1006. Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError,
  1007. &multicastSlotRxConfig );
  1008. Ctx.PingSlotCtx.SymbolTimeout = multicastSlotRxConfig.WindowTimeout;
  1009. }
  1010. if( ( int32_t )multicastSlotTime > multicastSlotRxConfig.WindowOffset )
  1011. {// Apply the window offset
  1012. multicastSlotTime += multicastSlotRxConfig.WindowOffset;
  1013. }
  1014. // Start the timer if the ping slot time is in range
  1015. Ctx.MulticastSlotState = PINGSLOT_STATE_IDLE;
  1016. TimerSetValue( &Ctx.MulticastSlotTimer, multicastSlotTime );
  1017. TimerStart( &Ctx.MulticastSlotTimer );
  1018. }
  1019. break;
  1020. }
  1021. case PINGSLOT_STATE_IDLE:
  1022. {
  1023. uint32_t frequency = 0;
  1024. // Verify if the multicast channel is valid
  1025. if( Ctx.PingSlotCtx.NextMulticastChannel == NULL )
  1026. {
  1027. Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
  1028. TimerSetValue( &Ctx.MulticastSlotTimer, 1 );
  1029. TimerStart( &Ctx.MulticastSlotTimer );
  1030. break;
  1031. }
  1032. // Apply frequency
  1033. frequency = Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.RxParams.ClassB.Frequency;
  1034. // Restore the floor plan frequency if there is no individual frequency assigned
  1035. if( frequency == 0 )
  1036. {
  1037. // Restore floor plan
  1038. frequency = CalcDownlinkChannelAndFrequency( Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.Address, Ctx.BeaconCtx.BeaconTime.Seconds, CLASSB_BEACON_INTERVAL );
  1039. }
  1040. Ctx.MulticastSlotState = PINGSLOT_STATE_RX;
  1041. multicastSlotRxConfig.Datarate = Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.RxParams.ClassB.Datarate;
  1042. multicastSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
  1043. multicastSlotRxConfig.RepeaterSupport = Ctx.LoRaMacClassBParams.LoRaMacParams->RepeaterSupport;
  1044. multicastSlotRxConfig.Frequency = frequency;
  1045. multicastSlotRxConfig.RxContinuous = false;
  1046. multicastSlotRxConfig.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT;
  1047. RegionRxConfig( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &multicastSlotRxConfig, ( int8_t* )&Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate );
  1048. if( Ctx.PingSlotState == PINGSLOT_STATE_RX )
  1049. {
  1050. // Close ping slot window, if necessary. Multicast slots have priority
  1051. Radio.Standby( );
  1052. Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
  1053. TimerSetValue( &Ctx.PingSlotTimer, CLASSB_PING_SLOT_WINDOW );
  1054. TimerStart( &Ctx.PingSlotTimer );
  1055. }
  1056. if( multicastSlotRxConfig.RxContinuous == false )
  1057. {
  1058. Radio.Rx( Ctx.LoRaMacClassBParams.LoRaMacParams->MaxRxWindow );
  1059. }
  1060. else
  1061. {
  1062. Radio.Rx( 0 ); // Continuous mode
  1063. }
  1064. break;
  1065. }
  1066. default:
  1067. {
  1068. Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
  1069. break;
  1070. }
  1071. }
  1072. }
  1073. #endif // LORAMAC_CLASSB_ENABLED
  1074. bool LoRaMacClassBRxBeacon( uint8_t *payload, uint16_t size )
  1075. {
  1076. #ifdef LORAMAC_CLASSB_ENABLED
  1077. GetPhyParams_t getPhy;
  1078. PhyParam_t phyParam;
  1079. bool beaconProcessed = false;
  1080. uint16_t crc0 = 0;
  1081. uint16_t crc1 = 0;
  1082. uint16_t beaconCrc0 = 0;
  1083. uint16_t beaconCrc1 = 0;
  1084. getPhy.Attribute = PHY_BEACON_FORMAT;
  1085. phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy );
  1086. // Verify if we are in the state where we expect a beacon
  1087. if( ( Ctx.BeaconState == BEACON_STATE_RX ) || ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) )
  1088. {
  1089. if( size == phyParam.BeaconFormat.BeaconSize )
  1090. {
  1091. // A beacon frame is defined as:
  1092. // Bytes: | x | 4 | 2 | 7 | y | 2 |
  1093. // |------|------|------|------------|------|------|
  1094. // Field: | RFU1 | Time | CRC1 | GwSpecific | RFU2 | CRC2 |
  1095. //
  1096. // Field RFU1 and RFU2 have variable sizes. It depends on the region specific implementation
  1097. // Read CRC1 field from the frame
  1098. beaconCrc0 = ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 4] ) & 0x00FF;
  1099. beaconCrc0 |= ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 4 + 1] << 8 ) & 0xFF00;
  1100. crc0 = BeaconCrc( payload, phyParam.BeaconFormat.Rfu1Size + 4 );
  1101. // Validate the first crc of the beacon frame
  1102. if( crc0 == beaconCrc0 )
  1103. {
  1104. // Read Time field from the frame
  1105. Ctx.BeaconCtx.BeaconTime.Seconds = ( ( uint32_t )payload[phyParam.BeaconFormat.Rfu1Size] ) & 0x000000FF;
  1106. Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 1] << 8 ) ) & 0x0000FF00;
  1107. Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 2] << 16 ) ) & 0x00FF0000;
  1108. Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 3] << 24 ) ) & 0xFF000000;
  1109. Ctx.BeaconCtx.BeaconTime.SubSeconds = 0;
  1110. Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Time = Ctx.BeaconCtx.BeaconTime;
  1111. beaconProcessed = true;
  1112. }
  1113. // Read CRC2 field from the frame
  1114. beaconCrc1 = ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2 + 7 + phyParam.BeaconFormat.Rfu2Size] ) & 0x00FF;
  1115. beaconCrc1 |= ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2 + 7 + phyParam.BeaconFormat.Rfu2Size + 1] << 8 ) & 0xFF00;
  1116. crc1 = BeaconCrc( &payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2], 7 + phyParam.BeaconFormat.Rfu2Size );
  1117. // Validate the second crc of the beacon frame
  1118. if( crc1 == beaconCrc1 )
  1119. {
  1120. // Read GwSpecific field from the frame
  1121. // The GwSpecific field contains 1 byte InfoDesc and 6 bytes Info
  1122. Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.InfoDesc = payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2];
  1123. memcpy1( Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.Info, &payload[phyParam.BeaconFormat.Rfu1Size + 4 + 2 + 1], 6 );
  1124. }
  1125. // Reset beacon variables, if one of the crc is valid
  1126. if( beaconProcessed == true )
  1127. {
  1128. TimerTime_t time = Radio.TimeOnAir( MODEM_LORA, size );
  1129. SysTime_t timeOnAir;
  1130. timeOnAir.Seconds = time / 1000;
  1131. timeOnAir.SubSeconds = time - timeOnAir.Seconds * 1000;
  1132. Ctx.BeaconCtx.LastBeaconRx = Ctx.BeaconCtx.BeaconTime;
  1133. Ctx.BeaconCtx.LastBeaconRx.Seconds += UNIX_GPS_EPOCH_OFFSET;
  1134. // Update system time.
  1135. SysTimeSet( SysTimeAdd( Ctx.BeaconCtx.LastBeaconRx, timeOnAir ) );
  1136. Ctx.BeaconCtx.Ctrl.BeaconAcquired = 1;
  1137. Ctx.BeaconCtx.Ctrl.BeaconMode = 1;
  1138. ResetWindowTimeout( );
  1139. Ctx.BeaconState = BEACON_STATE_LOCKED;
  1140. LoRaMacClassBBeaconTimerEvent( NULL );
  1141. }
  1142. }
  1143. if( Ctx.BeaconState == BEACON_STATE_RX )
  1144. {
  1145. Ctx.BeaconState = BEACON_STATE_TIMEOUT;
  1146. LoRaMacClassBBeaconTimerEvent( NULL );
  1147. }
  1148. // When the MAC listens for a beacon, it is not allowed to process any other
  1149. // downlink except the beacon frame itself. The reason for this is that no valid downlink window is open.
  1150. // If it receives a frame which is
  1151. // 1. not a beacon or
  1152. // 2. a beacon with a crc fail
  1153. // the MAC shall ignore the frame completely. Thus, the function must always return true, even if no
  1154. // valid beacon has been received.
  1155. beaconProcessed = true;
  1156. }
  1157. return beaconProcessed;
  1158. #else
  1159. return false;
  1160. #endif // LORAMAC_CLASSB_ENABLED
  1161. }
  1162. bool LoRaMacClassBIsBeaconExpected( void )
  1163. {
  1164. #ifdef LORAMAC_CLASSB_ENABLED
  1165. if( ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) ||
  1166. ( Ctx.BeaconState == BEACON_STATE_RX ) )
  1167. {
  1168. return true;
  1169. }
  1170. return false;
  1171. #else
  1172. return false;
  1173. #endif // LORAMAC_CLASSB_ENABLED
  1174. }
  1175. bool LoRaMacClassBIsPingExpected( void )
  1176. {
  1177. #ifdef LORAMAC_CLASSB_ENABLED
  1178. if( Ctx.PingSlotState == PINGSLOT_STATE_RX )
  1179. {
  1180. return true;
  1181. }
  1182. return false;
  1183. #else
  1184. return false;
  1185. #endif // LORAMAC_CLASSB_ENABLED
  1186. }
  1187. bool LoRaMacClassBIsMulticastExpected( void )
  1188. {
  1189. #ifdef LORAMAC_CLASSB_ENABLED
  1190. if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX )
  1191. {
  1192. return true;
  1193. }
  1194. return false;
  1195. #else
  1196. return false;
  1197. #endif // LORAMAC_CLASSB_ENABLED
  1198. }
  1199. bool LoRaMacClassBIsAcquisitionPending( void )
  1200. {
  1201. #ifdef LORAMAC_CLASSB_ENABLED
  1202. if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 )
  1203. {
  1204. return true;
  1205. }
  1206. return false;
  1207. #else
  1208. return false;
  1209. #endif // LORAMAC_CLASSB_ENABLED
  1210. }
  1211. bool LoRaMacClassBIsBeaconModeActive( void )
  1212. {
  1213. #ifdef LORAMAC_CLASSB_ENABLED
  1214. if( ( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) ||
  1215. ( Ctx.BeaconState == BEACON_STATE_ACQUISITION_BY_TIME ) )
  1216. {
  1217. return true;
  1218. }
  1219. return false;
  1220. #else
  1221. return false;
  1222. #endif // LORAMAC_CLASSB_ENABLED
  1223. }
  1224. void LoRaMacClassBSetPingSlotInfo( uint8_t periodicity )
  1225. {
  1226. #ifdef LORAMAC_CLASSB_ENABLED
  1227. Ctx.NvmCtx->PingSlotCtx.PingNb = CalcPingNb( periodicity );
  1228. Ctx.NvmCtx->PingSlotCtx.PingPeriod = CalcPingPeriod( Ctx.NvmCtx->PingSlotCtx.PingNb );
  1229. NvmContextChange( );
  1230. #endif // LORAMAC_CLASSB_ENABLED
  1231. }
  1232. void LoRaMacClassBHaltBeaconing( void )
  1233. {
  1234. #ifdef LORAMAC_CLASSB_ENABLED
  1235. if( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 )
  1236. {
  1237. if( ( Ctx.BeaconState == BEACON_STATE_TIMEOUT ) ||
  1238. ( Ctx.BeaconState == BEACON_STATE_LOST ) )
  1239. {
  1240. // Update the state machine before halt
  1241. LoRaMacClassBBeaconTimerEvent( NULL );
  1242. }
  1243. CRITICAL_SECTION_BEGIN( );
  1244. LoRaMacClassBEvents.Events.Beacon = 0;
  1245. CRITICAL_SECTION_END( );
  1246. // Halt ping slot state machine
  1247. TimerStop( &Ctx.BeaconTimer );
  1248. // Halt beacon state machine
  1249. Ctx.BeaconState = BEACON_STATE_HALT;
  1250. // Halt ping and multicast slot state machines
  1251. LoRaMacClassBStopRxSlots( );
  1252. }
  1253. #endif // LORAMAC_CLASSB_ENABLED
  1254. }
  1255. void LoRaMacClassBResumeBeaconing( void )
  1256. {
  1257. #ifdef LORAMAC_CLASSB_ENABLED
  1258. if( Ctx.BeaconState == BEACON_STATE_HALT )
  1259. {
  1260. Ctx.BeaconCtx.Ctrl.ResumeBeaconing = 1;
  1261. // Set default state
  1262. Ctx.BeaconState = BEACON_STATE_LOCKED;
  1263. if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 0 )
  1264. {
  1265. // Set the default state for beacon less operation
  1266. Ctx.BeaconState = BEACON_STATE_REACQUISITION;
  1267. }
  1268. LoRaMacClassBBeaconTimerEvent( NULL );
  1269. }
  1270. #endif // LORAMAC_CLASSB_ENABLED
  1271. }
  1272. LoRaMacStatus_t LoRaMacClassBSwitchClass( DeviceClass_t nextClass )
  1273. {
  1274. #ifdef LORAMAC_CLASSB_ENABLED
  1275. if( nextClass == CLASS_B )
  1276. {// Switch to from class a to class b
  1277. if( ( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) && ( Ctx.NvmCtx->PingSlotCtx.Ctrl.Assigned == 1 ) )
  1278. {
  1279. return LORAMAC_STATUS_OK;
  1280. }
  1281. }
  1282. if( nextClass == CLASS_A )
  1283. {// Switch from class b to class a
  1284. LoRaMacClassBHaltBeaconing( );
  1285. // Initialize default state for class b
  1286. InitClassBDefaults( );
  1287. return LORAMAC_STATUS_OK;
  1288. }
  1289. return LORAMAC_STATUS_SERVICE_UNKNOWN;
  1290. #else
  1291. return LORAMAC_STATUS_SERVICE_UNKNOWN;
  1292. #endif // LORAMAC_CLASSB_ENABLED
  1293. }
  1294. LoRaMacStatus_t LoRaMacClassBMibGetRequestConfirm( MibRequestConfirm_t *mibGet )
  1295. {
  1296. #ifdef LORAMAC_CLASSB_ENABLED
  1297. LoRaMacStatus_t status;
  1298. switch( mibGet->Type )
  1299. {
  1300. case MIB_PING_SLOT_DATARATE:
  1301. {
  1302. mibGet->Param.PingSlotDatarate = Ctx.NvmCtx->PingSlotCtx.Datarate;
  1303. break;
  1304. }
  1305. default:
  1306. {
  1307. status = LORAMAC_STATUS_SERVICE_UNKNOWN;
  1308. break;
  1309. }
  1310. }
  1311. return status;
  1312. #else
  1313. return LORAMAC_STATUS_SERVICE_UNKNOWN;
  1314. #endif // LORAMAC_CLASSB_ENABLED
  1315. }
  1316. LoRaMacStatus_t LoRaMacMibClassBSetRequestConfirm( MibRequestConfirm_t *mibSet )
  1317. {
  1318. #ifdef LORAMAC_CLASSB_ENABLED
  1319. LoRaMacStatus_t status;
  1320. switch( mibSet->Type )
  1321. {
  1322. case MIB_PING_SLOT_DATARATE:
  1323. {
  1324. Ctx.NvmCtx->PingSlotCtx.Datarate = mibSet->Param.PingSlotDatarate;
  1325. NvmContextChange( );
  1326. break;
  1327. }
  1328. default:
  1329. {
  1330. status = LORAMAC_STATUS_SERVICE_UNKNOWN;
  1331. break;
  1332. }
  1333. }
  1334. return status;
  1335. #else
  1336. return LORAMAC_STATUS_SERVICE_UNKNOWN;
  1337. #endif // LORAMAC_CLASSB_ENABLED
  1338. }
  1339. void LoRaMacClassBPingSlotInfoAns( void )
  1340. {
  1341. #ifdef LORAMAC_CLASSB_ENABLED
  1342. if( LoRaMacConfirmQueueIsCmdActive( MLME_PING_SLOT_INFO ) == true )
  1343. {
  1344. LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_PING_SLOT_INFO );
  1345. Ctx.NvmCtx->PingSlotCtx.Ctrl.Assigned = 1;
  1346. NvmContextChange( );
  1347. }
  1348. #endif // LORAMAC_CLASSB_ENABLED
  1349. }
  1350. uint8_t LoRaMacClassBPingSlotChannelReq( uint8_t datarate, uint32_t frequency )
  1351. {
  1352. #ifdef LORAMAC_CLASSB_ENABLED
  1353. uint8_t status = 0x03;
  1354. VerifyParams_t verify;
  1355. bool isCustomFreq = false;
  1356. if( frequency != 0 )
  1357. {
  1358. isCustomFreq = true;
  1359. verify.Frequency = frequency;
  1360. if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_FREQUENCY ) == false )
  1361. {
  1362. status &= 0xFE; // Channel frequency KO
  1363. }
  1364. }
  1365. verify.DatarateParams.Datarate = datarate;
  1366. verify.DatarateParams.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime;
  1367. if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_RX_DR ) == false )
  1368. {
  1369. status &= 0xFD; // Datarate range KO
  1370. }
  1371. if( status == 0x03 )
  1372. {
  1373. if( isCustomFreq == true )
  1374. {
  1375. Ctx.NvmCtx->PingSlotCtx.Ctrl.CustomFreq = 1;
  1376. Ctx.NvmCtx->PingSlotCtx.Frequency = frequency;
  1377. }
  1378. else
  1379. {
  1380. Ctx.NvmCtx->PingSlotCtx.Ctrl.CustomFreq = 0;
  1381. Ctx.NvmCtx->PingSlotCtx.Frequency = 0;
  1382. }
  1383. Ctx.NvmCtx->PingSlotCtx.Datarate = datarate;
  1384. NvmContextChange( );
  1385. }
  1386. return status;
  1387. #else
  1388. return 0;
  1389. #endif // LORAMAC_CLASSB_ENABLED
  1390. }
  1391. void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel, TimerTime_t lastRxDone )
  1392. {
  1393. #ifdef LORAMAC_CLASSB_ENABLED
  1394. Ctx.BeaconCtx.BeaconTimingDelay = ( CLASSB_BEACON_DELAY_BEACON_TIMING_ANS * beaconTimingDelay );
  1395. Ctx.BeaconCtx.BeaconTimingChannel = beaconTimingChannel;
  1396. if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_TIMING ) == true )
  1397. {
  1398. if( Ctx.BeaconCtx.BeaconTimingDelay > CLASSB_BEACON_INTERVAL )
  1399. {
  1400. // We missed the beacon already
  1401. Ctx.BeaconCtx.BeaconTimingDelay = 0;
  1402. Ctx.BeaconCtx.BeaconTimingChannel = 0;
  1403. LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_BEACON_TIMING );
  1404. }
  1405. else
  1406. {
  1407. Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 1;
  1408. Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 1;
  1409. Ctx.BeaconCtx.NextBeaconRx = SysTimeFromMs( lastRxDone + Ctx.BeaconCtx.BeaconTimingDelay );
  1410. LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_TIMING );
  1411. }
  1412. Ctx.LoRaMacClassBParams.MlmeConfirm->BeaconTimingDelay = Ctx.BeaconCtx.BeaconTimingDelay;
  1413. Ctx.LoRaMacClassBParams.MlmeConfirm->BeaconTimingChannel = Ctx.BeaconCtx.BeaconTimingChannel;
  1414. }
  1415. #endif // LORAMAC_CLASSB_ENABLED
  1416. }
  1417. void LoRaMacClassBDeviceTimeAns( void )
  1418. {
  1419. #ifdef LORAMAC_CLASSB_ENABLED
  1420. SysTime_t nextBeacon = SysTimeGet( );
  1421. uint32_t currentTimeMs = SysTimeToMs( nextBeacon );
  1422. nextBeacon.Seconds = nextBeacon.Seconds + ( 128 - ( nextBeacon.Seconds % 128 ) );
  1423. nextBeacon.SubSeconds = 0;
  1424. Ctx.BeaconCtx.NextBeaconRx = nextBeacon;
  1425. Ctx.BeaconCtx.LastBeaconRx = SysTimeSub( Ctx.BeaconCtx.NextBeaconRx, ( SysTime_t ){ .Seconds = CLASSB_BEACON_INTERVAL / 1000, .SubSeconds = 0 } );
  1426. if( LoRaMacConfirmQueueIsCmdActive( MLME_DEVICE_TIME ) == true )
  1427. {
  1428. if( currentTimeMs > SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) )
  1429. {
  1430. // We missed the beacon already
  1431. Ctx.BeaconCtx.LastBeaconRx.Seconds = 0;
  1432. Ctx.BeaconCtx.LastBeaconRx.SubSeconds = 0;
  1433. Ctx.BeaconCtx.NextBeaconRx.Seconds = 0;
  1434. Ctx.BeaconCtx.NextBeaconRx.SubSeconds = 0;
  1435. LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_DEVICE_TIME );
  1436. }
  1437. else
  1438. {
  1439. Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 1;
  1440. Ctx.BeaconCtx.BeaconTimingDelay = SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - currentTimeMs;
  1441. Ctx.BeaconCtx.BeaconTime.Seconds = nextBeacon.Seconds - UNIX_GPS_EPOCH_OFFSET - 128;
  1442. Ctx.BeaconCtx.BeaconTime.SubSeconds = 0;
  1443. LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_DEVICE_TIME );
  1444. }
  1445. }
  1446. #endif // LORAMAC_CLASSB_ENABLED
  1447. }
  1448. bool LoRaMacClassBBeaconFreqReq( uint32_t frequency )
  1449. {
  1450. #ifdef LORAMAC_CLASSB_ENABLED
  1451. VerifyParams_t verify;
  1452. if( frequency != 0 )
  1453. {
  1454. verify.Frequency = frequency;
  1455. if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_FREQUENCY ) == true )
  1456. {
  1457. Ctx.NvmCtx->BeaconCtx.Ctrl.CustomFreq = 1;
  1458. Ctx.NvmCtx->BeaconCtx.Frequency = frequency;
  1459. NvmContextChange( );
  1460. return true;
  1461. }
  1462. }
  1463. else
  1464. {
  1465. Ctx.NvmCtx->BeaconCtx.Ctrl.CustomFreq = 0;
  1466. NvmContextChange( );
  1467. return true;
  1468. }
  1469. return false;
  1470. #else
  1471. return false;
  1472. #endif // LORAMAC_CLASSB_ENABLED
  1473. }
  1474. TimerTime_t LoRaMacClassBIsUplinkCollision( TimerTime_t txTimeOnAir )
  1475. {
  1476. #ifdef LORAMAC_CLASSB_ENABLED
  1477. TimerTime_t currentTime = TimerGetCurrentTime( );
  1478. TimerTime_t beaconReserved = 0;
  1479. TimerTime_t nextBeacon = SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx );
  1480. beaconReserved = nextBeacon -
  1481. CLASSB_BEACON_GUARD -
  1482. Ctx.LoRaMacClassBParams.LoRaMacParams->ReceiveDelay1 -
  1483. Ctx.LoRaMacClassBParams.LoRaMacParams->ReceiveDelay2 -
  1484. txTimeOnAir;
  1485. // Check if the next beacon will be received during the next uplink.
  1486. if( ( currentTime >= beaconReserved ) && ( currentTime < ( nextBeacon + CLASSB_BEACON_RESERVED ) ) )
  1487. {// Next beacon will be sent during the next uplink.
  1488. return CLASSB_BEACON_RESERVED;
  1489. }
  1490. return 0;
  1491. #else
  1492. return 0;
  1493. #endif // LORAMAC_CLASSB_ENABLED
  1494. }
  1495. void LoRaMacClassBStopRxSlots( void )
  1496. {
  1497. #ifdef LORAMAC_CLASSB_ENABLED
  1498. TimerStop( &Ctx.PingSlotTimer );
  1499. TimerStop( &Ctx.MulticastSlotTimer );
  1500. CRITICAL_SECTION_BEGIN( );
  1501. LoRaMacClassBEvents.Events.PingSlot = 0;
  1502. LoRaMacClassBEvents.Events.MulticastSlot = 0;
  1503. CRITICAL_SECTION_END( );
  1504. #endif // LORAMAC_CLASSB_ENABLED
  1505. }
  1506. void LoRaMacClassBStartRxSlots( void )
  1507. {
  1508. #ifdef LORAMAC_CLASSB_ENABLED
  1509. if( Ctx.NvmCtx->PingSlotCtx.Ctrl.Assigned == 1 )
  1510. {
  1511. Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
  1512. TimerSetValue( &Ctx.PingSlotTimer, 1 );
  1513. TimerStart( &Ctx.PingSlotTimer );
  1514. Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET;
  1515. TimerSetValue( &Ctx.MulticastSlotTimer, 1 );
  1516. TimerStart( &Ctx.MulticastSlotTimer );
  1517. }
  1518. #endif // LORAMAC_CLASSB_ENABLED
  1519. }
  1520. void LoRaMacClassBSetMulticastPeriodicity( MulticastCtx_t* multicastChannel )
  1521. {
  1522. #ifdef LORAMAC_CLASSB_ENABLED
  1523. if( multicastChannel != NULL )
  1524. {
  1525. multicastChannel->PingNb = CalcPingNb( multicastChannel->ChannelParams.RxParams.ClassB.Periodicity );
  1526. multicastChannel->PingPeriod = CalcPingPeriod( multicastChannel->PingNb );
  1527. }
  1528. #endif // LORAMAC_CLASSB_ENABLED
  1529. }
  1530. void LoRaMacClassBProcess( void )
  1531. {
  1532. #ifdef LORAMAC_CLASSB_ENABLED
  1533. LoRaMacClassBEvents_t events;
  1534. CRITICAL_SECTION_BEGIN( );
  1535. events = LoRaMacClassBEvents;
  1536. LoRaMacClassBEvents.Value = 0;
  1537. CRITICAL_SECTION_END( );
  1538. if( events.Value != 0 )
  1539. {
  1540. if( events.Events.Beacon == 1 )
  1541. {
  1542. LoRaMacClassBProcessBeacon( );
  1543. }
  1544. if( events.Events.PingSlot == 1 )
  1545. {
  1546. LoRaMacClassBProcessPingSlot( );
  1547. }
  1548. if( events.Events.MulticastSlot == 1 )
  1549. {
  1550. LoRaMacClassBProcessMulticastSlot( );
  1551. }
  1552. }
  1553. #endif // LORAMAC_CLASSB_ENABLED
  1554. }