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