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