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/SAMR34/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 static void OnMacProcessNotify( void );
122 static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size );
123 static void OnNetworkParametersChange( CommissioningParams_t* params );
124 static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn );
125 static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn );
126 static void OnJoinRequest( LmHandlerJoinParams_t* params );
127 static void OnTxData( LmHandlerTxParams_t* params );
128 static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params );
129 static void OnClassChange( DeviceClass_t deviceClass );
130 static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params );
131 #if( LMH_SYS_TIME_UPDATE_NEW_API == 1 )
132 static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection );
133 #else
134 static void OnSysTimeUpdate( void );
135 #endif
136 #if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 )
137 static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size );
138 static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size );
139 #endif
140 static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost );
141 #if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 )
142 static void OnFragDone( int32_t status, uint32_t size );
143 #else
144 static void OnFragDone( int32_t status, uint8_t *file, uint32_t size );
145 #endif
146 static void StartTxProcess( LmHandlerTxEvents_t txEvent );
147 static void UplinkProcess( void );
148 
149 static void OnTxPeriodicityChanged( uint32_t periodicity );
150 static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed );
151 static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity );
152 
153 /*!
154  * Function executed on TxTimer event
155  */
156 static void OnTxTimerEvent( void* context );
157 
158 /*!
159  * Function executed on Led 1 Timeout event
160  */
161 static void OnLed1TimerEvent( void* context );
162 
163 static LmHandlerCallbacks_t LmHandlerCallbacks =
164 {
165     .GetBatteryLevel = BoardGetBatteryLevel,
166     .GetTemperature = NULL,
167     .GetRandomSeed = BoardGetRandomSeed,
168     .OnMacProcess = OnMacProcessNotify,
169     .OnNvmDataChange = OnNvmDataChange,
170     .OnNetworkParametersChange = OnNetworkParametersChange,
171     .OnMacMcpsRequest = OnMacMcpsRequest,
172     .OnMacMlmeRequest = OnMacMlmeRequest,
173     .OnJoinRequest = OnJoinRequest,
174     .OnTxData = OnTxData,
175     .OnRxData = OnRxData,
176     .OnClassChange= OnClassChange,
177     .OnBeaconStatusChange = OnBeaconStatusChange,
178     .OnSysTimeUpdate = OnSysTimeUpdate,
179 };
180 
181 static LmHandlerParams_t LmHandlerParams =
182 {
183     .Region = ACTIVE_REGION,
184     .AdrEnable = LORAWAN_ADR_STATE,
185     .IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE,
186     .TxDatarate = LORAWAN_DEFAULT_DATARATE,
187     .PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK,
188     .DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON,
189     .DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE,
190     .DataBuffer = AppDataBuffer,
191     .PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY,
192 };
193 
194 static LmhpComplianceParams_t LmhpComplianceParams =
195 {
196     .FwVersion.Value = FIRMWARE_VERSION,
197     .OnTxPeriodicityChanged = OnTxPeriodicityChanged,
198     .OnTxFrameCtrlChanged = OnTxFrameCtrlChanged,
199     .OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged,
200 };
201 
202 /*!
203  * Defines the maximum size for the buffer receiving the fragmentation result.
204  *
205  * \remark By default FragDecoder.h defines:
206  *         \ref FRAG_MAX_NB   21
207  *         \ref FRAG_MAX_SIZE 50
208  *
209  *         FileSize = FRAG_MAX_NB * FRAG_MAX_SIZE
210  *
211  *         If bigger file size is to be received or is fragmented differently
212  *         one must update those parameters.
213  */
214 #define UNFRAGMENTED_DATA_SIZE                     ( 21 * 50 )
215 
216 /*
217  * Un-fragmented data storage.
218  */
219 static uint8_t UnfragmentedData[UNFRAGMENTED_DATA_SIZE];
220 
221 static LmhpFragmentationParams_t FragmentationParams =
222 {
223 #if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 )
224     .DecoderCallbacks =
225     {
226         .FragDecoderWrite = FragDecoderWrite,
227         .FragDecoderRead = FragDecoderRead,
228     },
229 #else
230     .Buffer = UnfragmentedData,
231     .BufferSize = UNFRAGMENTED_DATA_SIZE,
232 #endif
233     .OnProgress = OnFragProgress,
234     .OnDone = OnFragDone
235 };
236 
237 /*!
238  * Indicates if LoRaMacProcess call is pending.
239  *
240  * \warning If variable is equal to 0 then the MCU can be set in low power mode
241  */
242 static volatile uint8_t IsMacProcessPending = 0;
243 
244 static volatile uint8_t IsTxFramePending = 0;
245 
246 static volatile uint32_t TxPeriodicity = 0;
247 
248 /*
249  * Indicates if the system time has been synchronized
250  */
251 static volatile bool IsClockSynched = false;
252 
253 /*
254  * MC Session Started
255  */
256 static volatile bool IsMcSessionStarted = false;
257 
258 /*
259  * Indicates if the file transfer is done
260  */
261 static volatile bool IsFileTransferDone = false;
262 
263 /*
264  *  Received file computed CRC32
265  */
266 static volatile uint32_t FileRxCrc = 0;
267 
268 /*!
269  * LED GPIO pins objects
270  */
271 extern Gpio_t Led1; // Rx
272 
273 /*!
274  * UART object used for command line interface handling
275  */
276 extern Uart_t Uart1;
277 
278 /*!
279  * Main application entry point.
280  */
main(void)281 int main( void )
282 {
283     BoardInitMcu( );
284     BoardInitPeriph( );
285 
286     TimerInit( &Led1Timer, OnLed1TimerEvent );
287     TimerSetValue( &Led1Timer, 100 );
288 
289     // Initialize transmission periodicity variable
290     TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
291 
292     const Version_t appVersion = { .Value = FIRMWARE_VERSION };
293     const Version_t gitHubVersion = { .Value = GITHUB_VERSION };
294     DisplayAppInfo( "fuota-test-01",
295                     &appVersion,
296                     &gitHubVersion );
297 
298     if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS )
299     {
300         printf( "LoRaMac wasn't properly initialized\n" );
301         // Fatal error, endless loop.
302         while ( 1 )
303         {
304         }
305     }
306 
307     // Set system maximum tolerated rx error in milliseconds
308     LmHandlerSetSystemMaxRxError( 50 );
309 
310     // The LoRa-Alliance Compliance protocol package should always be
311     // initialized and activated.
312     LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams );
313     LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL );
314     LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL );
315     LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &FragmentationParams );
316 
317     IsClockSynched = false;
318     IsFileTransferDone = false;
319 
320     LmHandlerJoin( );
321 
322     StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER );
323 
324     while( 1 )
325     {
326         // Tick the RTC to execute callback in context of the main loop (in stead of the IRQ)
327         TimerProcess( );
328 
329         // Process characters sent over the command line interface
330         CliProcess( &Uart1 );
331 
332         // Processes the LoRaMac events
333         LmHandlerProcess( );
334 
335         // Process application uplinks management
336         UplinkProcess( );
337 
338         CRITICAL_SECTION_BEGIN( );
339         if( IsMacProcessPending == 1 )
340         {
341             // Clear flag and prevent MCU to go into low power modes.
342             IsMacProcessPending = 0;
343         }
344         else
345         {
346             // The MCU wakes up through events
347             BoardLowPowerHandler( );
348         }
349         CRITICAL_SECTION_END( );
350     }
351 }
352 
OnMacProcessNotify(void)353 static void OnMacProcessNotify( void )
354 {
355     IsMacProcessPending = 1;
356 }
357 
OnNvmDataChange(LmHandlerNvmContextStates_t state,uint16_t size)358 static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size )
359 {
360     DisplayNvmDataChange( state, size );
361 }
362 
OnNetworkParametersChange(CommissioningParams_t * params)363 static void OnNetworkParametersChange( CommissioningParams_t* params )
364 {
365     DisplayNetworkParametersUpdate( params );
366 }
367 
OnMacMcpsRequest(LoRaMacStatus_t status,McpsReq_t * mcpsReq,TimerTime_t nextTxIn)368 static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn )
369 {
370     DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn );
371 }
372 
OnMacMlmeRequest(LoRaMacStatus_t status,MlmeReq_t * mlmeReq,TimerTime_t nextTxIn)373 static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn )
374 {
375     DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn );
376 }
377 
OnJoinRequest(LmHandlerJoinParams_t * params)378 static void OnJoinRequest( LmHandlerJoinParams_t* params )
379 {
380     DisplayJoinRequestUpdate( params );
381     if( params->Status == LORAMAC_HANDLER_ERROR )
382     {
383         LmHandlerJoin( );
384     }
385     else
386     {
387         LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS );
388     }
389 }
390 
OnTxData(LmHandlerTxParams_t * params)391 static void OnTxData( LmHandlerTxParams_t* params )
392 {
393     DisplayTxUpdate( params );
394 }
395 
OnRxData(LmHandlerAppData_t * appData,LmHandlerRxParams_t * params)396 static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params )
397 {
398     DisplayRxUpdate( appData, params );
399 }
400 
OnClassChange(DeviceClass_t deviceClass)401 static void OnClassChange( DeviceClass_t deviceClass )
402 {
403     DisplayClassUpdate( deviceClass );
404 
405     switch( deviceClass )
406     {
407         default:
408         case CLASS_A:
409         {
410             IsMcSessionStarted = false;
411             break;
412         }
413         case CLASS_B:
414         {
415             // Inform the server as soon as possible that the end-device has switched to ClassB
416             LmHandlerAppData_t appData =
417             {
418                 .Buffer = NULL,
419                 .BufferSize = 0,
420                 .Port = 0,
421             };
422             LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG );
423             IsMcSessionStarted = true;
424             break;
425         }
426         case CLASS_C:
427         {
428             IsMcSessionStarted = true;
429             // Switch LED 1 ON
430             GpioWrite( &Led1, 1 );
431             break;
432         }
433     }
434 }
435 
OnBeaconStatusChange(LoRaMacHandlerBeaconParams_t * params)436 static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params )
437 {
438     switch( params->State )
439     {
440         case LORAMAC_HANDLER_BEACON_RX:
441         {
442             break;
443         }
444         case LORAMAC_HANDLER_BEACON_LOST:
445         case LORAMAC_HANDLER_BEACON_NRX:
446         {
447             break;
448         }
449         default:
450         {
451             break;
452         }
453     }
454 
455     DisplayBeaconUpdate( params );
456 }
457 
458 #if( LMH_SYS_TIME_UPDATE_NEW_API == 1 )
OnSysTimeUpdate(bool isSynchronized,int32_t timeCorrection)459 static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection )
460 {
461     IsClockSynched = isSynchronized;
462 }
463 #else
OnSysTimeUpdate(void)464 static void OnSysTimeUpdate( void )
465 {
466     IsClockSynched = true;
467 }
468 #endif
469 
470 #if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 )
FragDecoderWrite(uint32_t addr,uint8_t * data,uint32_t size)471 static int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size )
472 {
473     if( size >= UNFRAGMENTED_DATA_SIZE )
474     {
475         return -1; // Fail
476     }
477     for(uint32_t i = 0; i < size; i++ )
478     {
479         UnfragmentedData[addr + i] = data[i];
480     }
481     return 0; // Success
482 }
483 
FragDecoderRead(uint32_t addr,uint8_t * data,uint32_t size)484 static int8_t FragDecoderRead( uint32_t addr, uint8_t *data, uint32_t size )
485 {
486     if( size >= UNFRAGMENTED_DATA_SIZE )
487     {
488         return -1; // Fail
489     }
490     for(uint32_t i = 0; i < size; i++ )
491     {
492         data[i] = UnfragmentedData[addr + i];
493     }
494     return 0; // Success
495 }
496 #endif
497 
OnFragProgress(uint16_t fragCounter,uint16_t fragNb,uint8_t fragSize,uint16_t fragNbLost)498 static void OnFragProgress( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost )
499 {
500     // Switch LED 1 OFF for each received downlink
501     GpioWrite( &Led1, 0 );
502     TimerStart( &Led1Timer );
503 
504     printf( "\n###### =========== FRAG_DECODER ============ ######\n" );
505     printf( "######               PROGRESS                ######\n");
506     printf( "###### ===================================== ######\n");
507     printf( "RECEIVED    : %5d / %5d Fragments\n", fragCounter, fragNb );
508     printf( "              %5d / %5d Bytes\n", fragCounter * fragSize, fragNb * fragSize );
509     printf( "LOST        :       %7d Fragments\n\n", fragNbLost );
510 }
511 
512 #if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 )
OnFragDone(int32_t status,uint32_t size)513 static void OnFragDone( int32_t status, uint32_t size )
514 {
515     FileRxCrc = Crc32( UnfragmentedData, size );
516     IsFileTransferDone = true;
517     // Switch LED 1 OFF
518     GpioWrite( &Led1, 0 );
519 
520     printf( "\n###### =========== FRAG_DECODER ============ ######\n" );
521     printf( "######               FINISHED                ######\n");
522     printf( "###### ===================================== ######\n");
523     printf( "STATUS      : %ld\n", status );
524     printf( "CRC         : %08lX\n\n", FileRxCrc );
525 }
526 #else
OnFragDone(int32_t status,uint8_t * file,uint32_t size)527 static void OnFragDone( int32_t status, uint8_t *file, uint32_t size )
528 {
529     FileRxCrc = Crc32( file, size );
530     IsFileTransferDone = true;
531     // Switch LED 1 OFF
532     GpioWrite( &Led1, 0 );
533 
534     printf( "\n###### =========== FRAG_DECODER ============ ######\n" );
535     printf( "######               FINISHED                ######\n");
536     printf( "###### ===================================== ######\n");
537     printf( "STATUS      : %ld\n", status );
538     printf( "CRC         : %08lX\n\n", FileRxCrc );
539 }
540 #endif
541 
StartTxProcess(LmHandlerTxEvents_t txEvent)542 static void StartTxProcess( LmHandlerTxEvents_t txEvent )
543 {
544     switch( txEvent )
545     {
546     default:
547         // Intentional fall through
548     case LORAMAC_HANDLER_TX_ON_TIMER:
549         {
550             // Schedule 1st packet transmission
551             TimerInit( &TxTimer, OnTxTimerEvent );
552             TimerSetValue( &TxTimer, TxPeriodicity );
553             OnTxTimerEvent( NULL );
554         }
555         break;
556     case LORAMAC_HANDLER_TX_ON_EVENT:
557         {
558         }
559         break;
560     }
561 }
562 
UplinkProcess(void)563 static void UplinkProcess( void )
564 {
565     if( LmHandlerIsBusy( ) == true )
566     {
567         return;
568     }
569 
570     uint8_t isPending = 0;
571     CRITICAL_SECTION_BEGIN( );
572     isPending = IsTxFramePending;
573     IsTxFramePending = 0;
574     CRITICAL_SECTION_END( );
575     if( isPending == 1 )
576     {
577         if( IsMcSessionStarted == false )
578         {
579             if( IsFileTransferDone == false )
580             {
581                 if( IsClockSynched == false )
582                 {
583                     LmhpClockSyncAppTimeReq( );
584                 }
585                 else
586                 {
587                     AppDataBuffer[0] = randr( 0, 255 );
588                     // Send random packet
589                     LmHandlerAppData_t appData =
590                     {
591                         .Buffer = AppDataBuffer,
592                         .BufferSize = 1,
593                         .Port = 1,
594                     };
595                     LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed );
596                 }
597             }
598             else
599             {
600                 AppDataBuffer[0] = 0x05; // FragDataBlockAuthReq
601                 AppDataBuffer[1] = FileRxCrc & 0x000000FF;
602                 AppDataBuffer[2] = ( FileRxCrc >> 8 ) & 0x000000FF;
603                 AppDataBuffer[3] = ( FileRxCrc >> 16 ) & 0x000000FF;
604                 AppDataBuffer[4] = ( FileRxCrc >> 24 ) & 0x000000FF;
605 
606                 // Send FragAuthReq
607                 LmHandlerAppData_t appData =
608                 {
609                     .Buffer = AppDataBuffer,
610                     .BufferSize = 5,
611                     .Port = 201,
612                 };
613                 LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed );
614             }
615         }
616     }
617 }
618 
OnTxPeriodicityChanged(uint32_t periodicity)619 static void OnTxPeriodicityChanged( uint32_t periodicity )
620 {
621     TxPeriodicity = periodicity;
622 
623     if( TxPeriodicity == 0 )
624     { // Revert to application default periodicity
625         TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
626     }
627 
628     // Update timer periodicity
629     TimerStop( &TxTimer );
630     TimerSetValue( &TxTimer, TxPeriodicity );
631     TimerStart( &TxTimer );
632 }
633 
OnTxFrameCtrlChanged(LmHandlerMsgTypes_t isTxConfirmed)634 static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed )
635 {
636     LmHandlerParams.IsTxConfirmed = isTxConfirmed;
637 }
638 
OnPingSlotPeriodicityChanged(uint8_t pingSlotPeriodicity)639 static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity )
640 {
641     LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity;
642 }
643 
644 /*!
645  * Function executed on TxTimer event
646  */
OnTxTimerEvent(void * context)647 static void OnTxTimerEvent( void* context )
648 {
649     TimerStop( &TxTimer );
650 
651     IsTxFramePending = 1;
652 
653     // Schedule next transmission
654     TimerSetValue( &TxTimer, TxPeriodicity );
655     TimerStart( &TxTimer );
656 }
657 
658 /*!
659  * Function executed on Led 1 Timeout event
660  */
OnLed1TimerEvent(void * context)661 static void OnLed1TimerEvent( void* context )
662 {
663     TimerStop( &Led1Timer );
664     // Switch LED 1 ON
665     GpioWrite( &Led1, 1 );
666 }
667