1 /*!
2  * \file      main.c
3  *
4  * \brief     FUOTA interop tests - test 01
5  *
6  * \copyright Revised BSD License, see section \ref LICENSE.
7  *
8  * \code
9  *                ______                              _
10  *               / _____)             _              | |
11  *              ( (____  _____ ____ _| |_ _____  ____| |__
12  *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
13  *               _____) ) ____| | | || |_| ____( (___| | | |
14  *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
15  *              (C)2013-2018 Semtech
16  *
17  * \endcode
18  *
19  * \author    Miguel Luis ( Semtech )
20  */
21 
22 /*! \file fuota-test-01/NucleoL152/main.c */
23 
24 #include <stdio.h>
25 #include "../firmwareVersion.h"
26 #include "../../common/githubVersion.h"
27 #include "utilities.h"
28 #include "board.h"
29 #include "gpio.h"
30 #include "uart.h"
31 #include "RegionCommon.h"
32 
33 #include "cli.h"
34 #include "Commissioning.h"
35 #include "LmHandler.h"
36 #include "LmhpCompliance.h"
37 #include "LmhpClockSync.h"
38 #include "LmhpRemoteMcastSetup.h"
39 #include "LmhpFragmentation.h"
40 #include "LmHandlerMsgDisplay.h"
41 
42 #ifndef ACTIVE_REGION
43 
44 #warning "No active region defined, LORAMAC_REGION_EU868 will be used as default."
45 
46 #define ACTIVE_REGION LORAMAC_REGION_EU868
47 
48 #endif
49 
50 /*!
51  * LoRaWAN default end-device class
52  */
53 #define LORAWAN_DEFAULT_CLASS                       CLASS_A
54 
55 /*!
56  * Defines the application data transmission duty cycle. 40s, value in [ms].
57  */
58 #define APP_TX_DUTYCYCLE                            40000
59 
60 /*!
61  * Defines a random delay for application data transmission duty cycle. 5s,
62  * value in [ms].
63  */
64 #define APP_TX_DUTYCYCLE_RND                        5000
65 
66 /*!
67  * LoRaWAN Adaptive Data Rate
68  *
69  * \remark Please note that when ADR is enabled the end-device should be static
70  */
71 #define LORAWAN_ADR_STATE                           LORAMAC_HANDLER_ADR_ON
72 
73 /*!
74  * Default datarate
75  *
76  * \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled
77  */
78 #define LORAWAN_DEFAULT_DATARATE                    DR_3
79 
80 /*!
81  * LoRaWAN confirmed messages
82  */
83 #define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE         LORAMAC_HANDLER_UNCONFIRMED_MSG
84 
85 /*!
86  * User application data buffer size
87  */
88 #define LORAWAN_APP_DATA_BUFFER_MAX_SIZE            242
89 
90 /*!
91  * LoRaWAN ETSI duty cycle control enable/disable
92  *
93  * \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes
94  */
95 #define LORAWAN_DUTYCYCLE_ON                        true
96 
97 /*!
98  *
99  */
100 typedef enum
101 {
102     LORAMAC_HANDLER_TX_ON_TIMER,
103     LORAMAC_HANDLER_TX_ON_EVENT,
104 }LmHandlerTxEvents_t;
105 
106 /*!
107  * User application data
108  */
109 static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE];
110 
111 /*!
112  * Timer to handle the application data transmission duty cycle
113  */
114 static TimerEvent_t TxTimer;
115 
116 /*!
117  * Timer to handle the state of LED1
118  */
119 static TimerEvent_t Led1Timer;
120 
121 /*!
122  * Timer to handle the state of LED2
123  */
124 static TimerEvent_t Led2Timer;
125 
126 /*!
127  * Timer to handle the state of LED beacon indicator
128  */
129 static TimerEvent_t LedBeaconTimer;
130 
131 static void OnMacProcessNotify( void );
132 static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size );
133 static void OnNetworkParametersChange( CommissioningParams_t* params );
134 static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn );
135 static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn );
136 static void OnJoinRequest( LmHandlerJoinParams_t* params );
137 static void OnTxData( LmHandlerTxParams_t* params );
138 static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params );
139 static void OnClassChange( DeviceClass_t deviceClass );
140 static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params );
141 #if( LMH_SYS_TIME_UPDATE_NEW_API == 1 )
142 static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection );
143 #else
144 static void OnSysTimeUpdate( void );
145 #endif
146 #if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 )
147 static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size );
148 static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size );
149 #endif
150 static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost );
151 #if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 )
152 static void OnFragDone( int32_t status, uint32_t size );
153 #else
154 static void OnFragDone( int32_t status, uint8_t *file, uint32_t size );
155 #endif
156 static void StartTxProcess( LmHandlerTxEvents_t txEvent );
157 static void UplinkProcess( void );
158 
159 static void OnTxPeriodicityChanged( uint32_t periodicity );
160 static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed );
161 static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity );
162 
163 /*!
164  * Function executed on TxTimer event
165  */
166 static void OnTxTimerEvent( void* context );
167 
168 /*!
169  * Function executed on Led 1 Timeout event
170  */
171 static void OnLed1TimerEvent( void* context );
172 
173 /*!
174  * Function executed on Led 2 Timeout event
175  */
176 static void OnLed2TimerEvent( void* context );
177 
178 /*!
179  * \brief Function executed on Beacon timer Timeout event
180  */
181 static void OnLedBeaconTimerEvent( void* context );
182 
183 static LmHandlerCallbacks_t LmHandlerCallbacks =
184 {
185     .GetBatteryLevel = BoardGetBatteryLevel,
186     .GetTemperature = NULL,
187     .GetRandomSeed = BoardGetRandomSeed,
188     .OnMacProcess = OnMacProcessNotify,
189     .OnNvmDataChange = OnNvmDataChange,
190     .OnNetworkParametersChange = OnNetworkParametersChange,
191     .OnMacMcpsRequest = OnMacMcpsRequest,
192     .OnMacMlmeRequest = OnMacMlmeRequest,
193     .OnJoinRequest = OnJoinRequest,
194     .OnTxData = OnTxData,
195     .OnRxData = OnRxData,
196     .OnClassChange= OnClassChange,
197     .OnBeaconStatusChange = OnBeaconStatusChange,
198     .OnSysTimeUpdate = OnSysTimeUpdate,
199 };
200 
201 static LmHandlerParams_t LmHandlerParams =
202 {
203     .Region = ACTIVE_REGION,
204     .AdrEnable = LORAWAN_ADR_STATE,
205     .IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE,
206     .TxDatarate = LORAWAN_DEFAULT_DATARATE,
207     .PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK,
208     .DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON,
209     .DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE,
210     .DataBuffer = AppDataBuffer,
211     .PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY,
212 };
213 
214 static LmhpComplianceParams_t LmhpComplianceParams =
215 {
216     .FwVersion.Value = FIRMWARE_VERSION,
217     .OnTxPeriodicityChanged = OnTxPeriodicityChanged,
218     .OnTxFrameCtrlChanged = OnTxFrameCtrlChanged,
219     .OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged,
220 };
221 
222 /*!
223  * Defines the maximum size for the buffer receiving the fragmentation result.
224  *
225  * \remark By default FragDecoder.h defines:
226  *         \ref FRAG_MAX_NB   21
227  *         \ref FRAG_MAX_SIZE 50
228  *
229  *         FileSize = FRAG_MAX_NB * FRAG_MAX_SIZE
230  *
231  *         If bigger file size is to be received or is fragmented differently
232  *         one must update those parameters.
233  */
234 #define UNFRAGMENTED_DATA_SIZE                     ( 21 * 50 )
235 
236 /*
237  * Un-fragmented data storage.
238  */
239 static uint8_t UnfragmentedData[UNFRAGMENTED_DATA_SIZE];
240 
241 static LmhpFragmentationParams_t FragmentationParams =
242 {
243 #if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 )
244     .DecoderCallbacks =
245     {
246         .FragDecoderWrite = FragDecoderWrite,
247         .FragDecoderRead = FragDecoderRead,
248     },
249 #else
250     .Buffer = UnfragmentedData,
251     .BufferSize = UNFRAGMENTED_DATA_SIZE,
252 #endif
253     .OnProgress = OnFragProgress,
254     .OnDone = OnFragDone
255 };
256 
257 /*!
258  * Indicates if LoRaMacProcess call is pending.
259  *
260  * \warning If variable is equal to 0 then the MCU can be set in low power mode
261  */
262 static volatile uint8_t IsMacProcessPending = 0;
263 
264 static volatile uint8_t IsTxFramePending = 0;
265 
266 static volatile uint32_t TxPeriodicity = 0;
267 
268 /*
269  * Indicates if the system time has been synchronized
270  */
271 static volatile bool IsClockSynched = false;
272 
273 /*
274  * MC Session Started
275  */
276 static volatile bool IsMcSessionStarted = false;
277 
278 /*
279  * Indicates if the file transfer is done
280  */
281 static volatile bool IsFileTransferDone = false;
282 
283 /*
284  *  Received file computed CRC32
285  */
286 static volatile uint32_t FileRxCrc = 0;
287 
288 /*!
289  * LED GPIO pins objects
290  */
291 extern Gpio_t Led1; // Tx
292 extern Gpio_t Led2; // Rx
293 
294 /*!
295  * UART object used for command line interface handling
296  */
297 extern Uart_t Uart2;
298 
299 /*!
300  * Main application entry point.
301  */
main(void)302 int main( void )
303 {
304     BoardInitMcu( );
305     BoardInitPeriph( );
306 
307     TimerInit( &Led1Timer, OnLed1TimerEvent );
308     TimerSetValue( &Led1Timer, 25 );
309 
310     TimerInit( &Led2Timer, OnLed2TimerEvent );
311     TimerSetValue( &Led2Timer, 100 );
312 
313     TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent );
314     TimerSetValue( &LedBeaconTimer, 5000 );
315 
316     // Initialize transmission periodicity variable
317     TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
318 
319     const Version_t appVersion = { .Value = FIRMWARE_VERSION };
320     const Version_t gitHubVersion = { .Value = GITHUB_VERSION };
321     DisplayAppInfo( "fuota-test-01",
322                     &appVersion,
323                     &gitHubVersion );
324 
325     if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS )
326     {
327         printf( "LoRaMac wasn't properly initialized\n" );
328         // Fatal error, endless loop.
329         while ( 1 )
330         {
331         }
332     }
333 
334     // Set system maximum tolerated rx error in milliseconds
335     LmHandlerSetSystemMaxRxError( 20 );
336 
337     // The LoRa-Alliance Compliance protocol package should always be
338     // initialized and activated.
339     LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams );
340     LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL );
341     LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL );
342     LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &FragmentationParams );
343 
344     IsClockSynched = false;
345     IsFileTransferDone = false;
346 
347     LmHandlerJoin( );
348 
349     StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER );
350 
351     while( 1 )
352     {
353         // Process characters sent over the command line interface
354         CliProcess( &Uart2 );
355 
356         // Processes the LoRaMac events
357         LmHandlerProcess( );
358 
359         // Process application uplinks management
360         UplinkProcess( );
361 
362         CRITICAL_SECTION_BEGIN( );
363         if( IsMacProcessPending == 1 )
364         {
365             // Clear flag and prevent MCU to go into low power modes.
366             IsMacProcessPending = 0;
367         }
368         else
369         {
370             // The MCU wakes up through events
371             BoardLowPowerHandler( );
372         }
373         CRITICAL_SECTION_END( );
374     }
375 }
376 
OnMacProcessNotify(void)377 static void OnMacProcessNotify( void )
378 {
379     IsMacProcessPending = 1;
380 }
381 
OnNvmDataChange(LmHandlerNvmContextStates_t state,uint16_t size)382 static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size )
383 {
384     DisplayNvmDataChange( state, size );
385 }
386 
OnNetworkParametersChange(CommissioningParams_t * params)387 static void OnNetworkParametersChange( CommissioningParams_t* params )
388 {
389     DisplayNetworkParametersUpdate( params );
390 }
391 
OnMacMcpsRequest(LoRaMacStatus_t status,McpsReq_t * mcpsReq,TimerTime_t nextTxIn)392 static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn )
393 {
394     DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn );
395 }
396 
OnMacMlmeRequest(LoRaMacStatus_t status,MlmeReq_t * mlmeReq,TimerTime_t nextTxIn)397 static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn )
398 {
399     DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn );
400 }
401 
OnJoinRequest(LmHandlerJoinParams_t * params)402 static void OnJoinRequest( LmHandlerJoinParams_t* params )
403 {
404     DisplayJoinRequestUpdate( params );
405     if( params->Status == LORAMAC_HANDLER_ERROR )
406     {
407         LmHandlerJoin( );
408     }
409     else
410     {
411         LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS );
412     }
413 }
414 
OnTxData(LmHandlerTxParams_t * params)415 static void OnTxData( LmHandlerTxParams_t* params )
416 {
417     DisplayTxUpdate( params );
418 }
419 
OnRxData(LmHandlerAppData_t * appData,LmHandlerRxParams_t * params)420 static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params )
421 {
422     DisplayRxUpdate( appData, params );
423 }
424 
OnClassChange(DeviceClass_t deviceClass)425 static void OnClassChange( DeviceClass_t deviceClass )
426 {
427     DisplayClassUpdate( deviceClass );
428 
429     switch( deviceClass )
430     {
431         default:
432         case CLASS_A:
433         {
434             IsMcSessionStarted = false;
435             break;
436         }
437         case CLASS_B:
438         {
439             // Inform the server as soon as possible that the end-device has switched to ClassB
440             LmHandlerAppData_t appData =
441             {
442                 .Buffer = NULL,
443                 .BufferSize = 0,
444                 .Port = 0,
445             };
446             LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG );
447             IsMcSessionStarted = true;
448             break;
449         }
450         case CLASS_C:
451         {
452             IsMcSessionStarted = true;
453             // Switch LED 2 ON
454             GpioWrite( &Led2, 1 );
455             break;
456         }
457     }
458 }
459 
OnBeaconStatusChange(LoRaMacHandlerBeaconParams_t * params)460 static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params )
461 {
462     switch( params->State )
463     {
464         case LORAMAC_HANDLER_BEACON_RX:
465         {
466             TimerStart( &LedBeaconTimer );
467             break;
468         }
469         case LORAMAC_HANDLER_BEACON_LOST:
470         case LORAMAC_HANDLER_BEACON_NRX:
471         {
472             TimerStop( &LedBeaconTimer );
473             break;
474         }
475         default:
476         {
477             break;
478         }
479     }
480 
481     DisplayBeaconUpdate( params );
482 }
483 
484 #if( LMH_SYS_TIME_UPDATE_NEW_API == 1 )
OnSysTimeUpdate(bool isSynchronized,int32_t timeCorrection)485 static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection )
486 {
487     IsClockSynched = isSynchronized;
488 }
489 #else
OnSysTimeUpdate(void)490 static void OnSysTimeUpdate( void )
491 {
492     IsClockSynched = true;
493 }
494 #endif
495 
496 #if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 )
FragDecoderWrite(uint32_t addr,uint8_t * data,uint32_t size)497 static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size )
498 {
499     if( size >= UNFRAGMENTED_DATA_SIZE )
500     {
501         return -1; // Fail
502     }
503     for(uint32_t i = 0; i < size; i++ )
504     {
505         UnfragmentedData[addr + i] = data[i];
506     }
507     return 0; // Success
508 }
509 
FragDecoderRead(uint32_t addr,uint8_t * data,uint32_t size)510 static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size )
511 {
512     if( size >= UNFRAGMENTED_DATA_SIZE )
513     {
514         return -1; // Fail
515     }
516     for(uint32_t i = 0; i < size; i++ )
517     {
518         data[i] = UnfragmentedData[addr + i];
519     }
520     return 0; // Success
521 }
522 #endif
523 
OnFragProgress(uint16_t fragCounter,uint16_t fragNb,uint8_t fragSize,uint16_t fragNbLost)524 static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost )
525 {
526     // Switch LED 2 OFF for each received downlink
527     GpioWrite( &Led2, 0 );
528     TimerStart( &Led2Timer );
529 
530     printf( "\n###### =========== FRAG_DECODER ============ ######\n" );
531     printf( "######               PROGRESS                ######\n");
532     printf( "###### ===================================== ######\n");
533     printf( "RECEIVED    : %5d / %5d Fragments\n", fragCounter, fragNb );
534     printf( "              %5d / %5d Bytes\n", fragCounter * fragSize, fragNb * fragSize );
535     printf( "LOST        :       %7d Fragments\n\n", fragNbLost );
536 }
537 
538 #if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 )
OnFragDone(int32_t status,uint32_t size)539 static void OnFragDone( int32_t status, uint32_t size )
540 {
541     FileRxCrc = Crc32( UnfragmentedData, size );
542     IsFileTransferDone = true;
543     // Switch LED 2 OFF
544     GpioWrite( &Led2, 0 );
545 
546     printf( "\n###### =========== FRAG_DECODER ============ ######\n" );
547     printf( "######               FINISHED                ######\n");
548     printf( "###### ===================================== ######\n");
549     printf( "STATUS      : %ld\n", status );
550     printf( "CRC         : %08lX\n\n", FileRxCrc );
551 }
552 #else
OnFragDone(int32_t status,uint8_t * file,uint32_t size)553 static void OnFragDone( int32_t status, uint8_t *file, uint32_t size )
554 {
555     FileRxCrc = Crc32( file, size );
556     IsFileTransferDone = true;
557     // Switch LED 2 OFF
558     GpioWrite( &Led2, 0 );
559 
560     printf( "\n###### =========== FRAG_DECODER ============ ######\n" );
561     printf( "######               FINISHED                ######\n");
562     printf( "###### ===================================== ######\n");
563     printf( "STATUS      : %ld\n", status );
564     printf( "CRC         : %08lX\n\n", FileRxCrc );
565 }
566 #endif
567 
StartTxProcess(LmHandlerTxEvents_t txEvent)568 static void StartTxProcess( LmHandlerTxEvents_t txEvent )
569 {
570     switch( txEvent )
571     {
572     default:
573         // Intentional fall through
574     case LORAMAC_HANDLER_TX_ON_TIMER:
575         {
576             // Schedule 1st packet transmission
577             TimerInit( &TxTimer, OnTxTimerEvent );
578             TimerSetValue( &TxTimer, TxPeriodicity );
579             OnTxTimerEvent( NULL );
580         }
581         break;
582     case LORAMAC_HANDLER_TX_ON_EVENT:
583         {
584         }
585         break;
586     }
587 }
588 
UplinkProcess(void)589 static void UplinkProcess( void )
590 {
591     LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR;
592 
593     if( LmHandlerIsBusy( ) == true )
594     {
595         return;
596     }
597 
598     uint8_t isPending = 0;
599     CRITICAL_SECTION_BEGIN( );
600     isPending = IsTxFramePending;
601     IsTxFramePending = 0;
602     CRITICAL_SECTION_END( );
603     if( isPending == 1 )
604     {
605         if( IsMcSessionStarted == false )
606         {
607             if( IsFileTransferDone == false )
608             {
609                 if( IsClockSynched == false )
610                 {
611                     status = LmhpClockSyncAppTimeReq( );
612                 }
613                 else
614                 {
615                     AppDataBuffer[0] = randr( 0, 255 );
616                     // Send random packet
617                     LmHandlerAppData_t appData =
618                     {
619                         .Buffer = AppDataBuffer,
620                         .BufferSize = 1,
621                         .Port = 1,
622                     };
623                     status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed );
624                 }
625             }
626             else
627             {
628                 AppDataBuffer[0] = 0x05; // FragDataBlockAuthReq
629                 AppDataBuffer[1] = FileRxCrc & 0x000000FF;
630                 AppDataBuffer[2] = ( FileRxCrc >> 8 ) & 0x000000FF;
631                 AppDataBuffer[3] = ( FileRxCrc >> 16 ) & 0x000000FF;
632                 AppDataBuffer[4] = ( FileRxCrc >> 24 ) & 0x000000FF;
633 
634                 // Send FragAuthReq
635                 LmHandlerAppData_t appData =
636                 {
637                     .Buffer = AppDataBuffer,
638                     .BufferSize = 5,
639                     .Port = 201,
640                 };
641                 status = LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed );
642             }
643             if( status == LORAMAC_HANDLER_SUCCESS )
644             {
645                 // Switch LED 1 ON
646                 GpioWrite( &Led1, 1 );
647                 TimerStart( &Led1Timer );
648             }
649         }
650     }
651 }
652 
OnTxPeriodicityChanged(uint32_t periodicity)653 static void OnTxPeriodicityChanged( uint32_t periodicity )
654 {
655     TxPeriodicity = periodicity;
656 
657     if( TxPeriodicity == 0 )
658     { // Revert to application default periodicity
659         TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
660     }
661 
662     // Update timer periodicity
663     TimerStop( &TxTimer );
664     TimerSetValue( &TxTimer, TxPeriodicity );
665     TimerStart( &TxTimer );
666 }
667 
OnTxFrameCtrlChanged(LmHandlerMsgTypes_t isTxConfirmed)668 static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed )
669 {
670     LmHandlerParams.IsTxConfirmed = isTxConfirmed;
671 }
672 
OnPingSlotPeriodicityChanged(uint8_t pingSlotPeriodicity)673 static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity )
674 {
675     LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity;
676 }
677 
678 /*!
679  * Function executed on TxTimer event
680  */
OnTxTimerEvent(void * context)681 static void OnTxTimerEvent( void* context )
682 {
683     TimerStop( &TxTimer );
684 
685     IsTxFramePending = 1;
686 
687     // Schedule next transmission
688     TimerSetValue( &TxTimer, TxPeriodicity );
689     TimerStart( &TxTimer );
690 }
691 
692 /*!
693  * Function executed on Led 1 Timeout event
694  */
OnLed1TimerEvent(void * context)695 static void OnLed1TimerEvent( void* context )
696 {
697     TimerStop( &Led1Timer );
698     // Switch LED 1 OFF
699     GpioWrite( &Led1, 0 );
700 }
701 
702 /*!
703  * Function executed on Led 2 Timeout event
704  */
OnLed2TimerEvent(void * context)705 static void OnLed2TimerEvent( void* context )
706 {
707     TimerStop( &Led2Timer );
708     // Switch LED 2 ON
709     GpioWrite( &Led2, 1 );
710 }
711 
712 /*!
713  * \brief Function executed on Beacon timer Timeout event
714  */
OnLedBeaconTimerEvent(void * context)715 static void OnLedBeaconTimerEvent( void* context )
716 {
717     GpioWrite( &Led2, 1 );
718     TimerStart( &Led2Timer );
719 
720     TimerStart( &LedBeaconTimer );
721 }
722