1 /*!
2 * \file main.c
3 *
4 * \brief Performs a periodic uplink
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 periodic-uplink/B-L072Z-LRWAN1/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 "CayenneLpp.h"
38 #include "LmHandlerMsgDisplay.h"
39
40 #ifndef ACTIVE_REGION
41
42 #warning "No active region defined, LORAMAC_REGION_EU868 will be used as default."
43
44 #define ACTIVE_REGION LORAMAC_REGION_EU868
45
46 #endif
47
48 /*!
49 * LoRaWAN default end-device class
50 */
51 #ifndef LORAWAN_DEFAULT_CLASS
52 #define LORAWAN_DEFAULT_CLASS CLASS_A
53 #endif
54
55 /*!
56 * Defines the application data transmission duty cycle. 5s, value in [ms].
57 */
58 #define APP_TX_DUTYCYCLE 5000
59
60 /*!
61 * Defines a random delay for application data transmission duty cycle. 1s,
62 * value in [ms].
63 */
64 #define APP_TX_DUTYCYCLE_RND 1000
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_0
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 * LoRaWAN application port
99 * @remark The allowed port range is from 1 up to 223. Other values are reserved.
100 */
101 #define LORAWAN_APP_PORT 2
102
103 /*!
104 *
105 */
106 typedef enum
107 {
108 LORAMAC_HANDLER_TX_ON_TIMER,
109 LORAMAC_HANDLER_TX_ON_EVENT,
110 }LmHandlerTxEvents_t;
111
112 /*!
113 * User application data
114 */
115 static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE];
116
117 /*!
118 * User application data structure
119 */
120 static LmHandlerAppData_t AppData =
121 {
122 .Buffer = AppDataBuffer,
123 .BufferSize = 0,
124 .Port = 0,
125 };
126
127 /*!
128 * Specifies the state of the application LED
129 */
130 static bool AppLedStateOn = false;
131
132 /*!
133 * Timer to handle the application data transmission duty cycle
134 */
135 static TimerEvent_t TxTimer;
136
137 /*!
138 * Timer to handle the state of LED1
139 */
140 static TimerEvent_t Led1Timer;
141
142 /*!
143 * Timer to handle the state of LED2
144 */
145 static TimerEvent_t Led2Timer;
146
147 /*!
148 * Timer to handle the state of LED3
149 */
150 static TimerEvent_t Led3Timer;
151
152 /*!
153 * Timer to handle the state of LED beacon indicator
154 */
155 static TimerEvent_t LedBeaconTimer;
156
157 static void OnMacProcessNotify( void );
158 static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size );
159 static void OnNetworkParametersChange( CommissioningParams_t* params );
160 static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn );
161 static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn );
162 static void OnJoinRequest( LmHandlerJoinParams_t* params );
163 static void OnTxData( LmHandlerTxParams_t* params );
164 static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params );
165 static void OnClassChange( DeviceClass_t deviceClass );
166 static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params );
167 #if( LMH_SYS_TIME_UPDATE_NEW_API == 1 )
168 static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection );
169 #else
170 static void OnSysTimeUpdate( void );
171 #endif
172 static void PrepareTxFrame( void );
173 static void StartTxProcess( LmHandlerTxEvents_t txEvent );
174 static void UplinkProcess( void );
175
176 static void OnTxPeriodicityChanged( uint32_t periodicity );
177 static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed );
178 static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity );
179
180 /*!
181 * Function executed on TxTimer event
182 */
183 static void OnTxTimerEvent( void* context );
184
185 /*!
186 * Function executed on Led 1 Timeout event
187 */
188 static void OnLed1TimerEvent( void* context );
189
190 /*!
191 * Function executed on Led 2 Timeout event
192 */
193 static void OnLed2TimerEvent( void* context );
194
195 /*!
196 * \brief Function executed on Led 3 Timeout event
197 */
198 static void OnLed3TimerEvent( void* context );
199
200 /*!
201 * \brief Function executed on Beacon timer Timeout event
202 */
203 static void OnLedBeaconTimerEvent( void* context );
204
205 static LmHandlerCallbacks_t LmHandlerCallbacks =
206 {
207 .GetBatteryLevel = BoardGetBatteryLevel,
208 .GetTemperature = NULL,
209 .GetRandomSeed = BoardGetRandomSeed,
210 .OnMacProcess = OnMacProcessNotify,
211 .OnNvmDataChange = OnNvmDataChange,
212 .OnNetworkParametersChange = OnNetworkParametersChange,
213 .OnMacMcpsRequest = OnMacMcpsRequest,
214 .OnMacMlmeRequest = OnMacMlmeRequest,
215 .OnJoinRequest = OnJoinRequest,
216 .OnTxData = OnTxData,
217 .OnRxData = OnRxData,
218 .OnClassChange= OnClassChange,
219 .OnBeaconStatusChange = OnBeaconStatusChange,
220 .OnSysTimeUpdate = OnSysTimeUpdate,
221 };
222
223 static LmHandlerParams_t LmHandlerParams =
224 {
225 .Region = ACTIVE_REGION,
226 .AdrEnable = LORAWAN_ADR_STATE,
227 .IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE,
228 .TxDatarate = LORAWAN_DEFAULT_DATARATE,
229 .PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK,
230 .DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON,
231 .DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE,
232 .DataBuffer = AppDataBuffer,
233 .PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY,
234 };
235
236 static LmhpComplianceParams_t LmhpComplianceParams =
237 {
238 .FwVersion.Value = FIRMWARE_VERSION,
239 .OnTxPeriodicityChanged = OnTxPeriodicityChanged,
240 .OnTxFrameCtrlChanged = OnTxFrameCtrlChanged,
241 .OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged,
242 };
243
244 /*!
245 * Indicates if LoRaMacProcess call is pending.
246 *
247 * \warning If variable is equal to 0 then the MCU can be set in low power mode
248 */
249 static volatile uint8_t IsMacProcessPending = 0;
250
251 static volatile uint8_t IsTxFramePending = 0;
252
253 static volatile uint32_t TxPeriodicity = 0;
254
255 /*!
256 * LED GPIO pins objects
257 */
258 extern Gpio_t Led1; // Tx
259 extern Gpio_t Led2; // Blinks every 5 seconds when beacon is acquired
260 extern Gpio_t Led3; // Rx
261 extern Gpio_t Led4; // App
262
263 /*!
264 * UART object used for command line interface handling
265 */
266 extern Uart_t Uart2;
267
268 /*!
269 * Main application entry point.
270 */
main(void)271 int main( void )
272 {
273 BoardInitMcu( );
274 BoardInitPeriph( );
275
276 TimerInit( &Led1Timer, OnLed1TimerEvent );
277 TimerSetValue( &Led1Timer, 25 );
278
279 TimerInit( &Led2Timer, OnLed2TimerEvent );
280 TimerSetValue( &Led2Timer, 25 );
281
282 TimerInit( &Led3Timer, OnLed3TimerEvent );
283 TimerSetValue( &Led3Timer, 25 );
284
285 TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent );
286 TimerSetValue( &LedBeaconTimer, 5000 );
287
288 // Initialize transmission periodicity variable
289 TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
290
291 const Version_t appVersion = { .Value = FIRMWARE_VERSION };
292 const Version_t gitHubVersion = { .Value = GITHUB_VERSION };
293 DisplayAppInfo( "periodic-uplink-lpp",
294 &appVersion,
295 &gitHubVersion );
296
297 if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS )
298 {
299 printf( "LoRaMac wasn't properly initialized\n" );
300 // Fatal error, endless loop.
301 while ( 1 )
302 {
303 }
304 }
305
306 // Set system maximum tolerated rx error in milliseconds
307 LmHandlerSetSystemMaxRxError( 20 );
308
309 // The LoRa-Alliance Compliance protocol package should always be
310 // initialized and activated.
311 LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams );
312
313 LmHandlerJoin( );
314
315 StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER );
316
317 while( 1 )
318 {
319 // Process characters sent over the command line interface
320 CliProcess( &Uart2 );
321
322 // Processes the LoRaMac events
323 LmHandlerProcess( );
324
325 // Process application uplinks management
326 UplinkProcess( );
327
328 CRITICAL_SECTION_BEGIN( );
329 if( IsMacProcessPending == 1 )
330 {
331 // Clear flag and prevent MCU to go into low power modes.
332 IsMacProcessPending = 0;
333 }
334 else
335 {
336 // The MCU wakes up through events
337 BoardLowPowerHandler( );
338 }
339 CRITICAL_SECTION_END( );
340 }
341 }
342
OnMacProcessNotify(void)343 static void OnMacProcessNotify( void )
344 {
345 IsMacProcessPending = 1;
346 }
347
OnNvmDataChange(LmHandlerNvmContextStates_t state,uint16_t size)348 static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size )
349 {
350 DisplayNvmDataChange( state, size );
351 }
352
OnNetworkParametersChange(CommissioningParams_t * params)353 static void OnNetworkParametersChange( CommissioningParams_t* params )
354 {
355 DisplayNetworkParametersUpdate( params );
356 }
357
OnMacMcpsRequest(LoRaMacStatus_t status,McpsReq_t * mcpsReq,TimerTime_t nextTxIn)358 static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn )
359 {
360 DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn );
361 }
362
OnMacMlmeRequest(LoRaMacStatus_t status,MlmeReq_t * mlmeReq,TimerTime_t nextTxIn)363 static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn )
364 {
365 DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn );
366 }
367
OnJoinRequest(LmHandlerJoinParams_t * params)368 static void OnJoinRequest( LmHandlerJoinParams_t* params )
369 {
370 DisplayJoinRequestUpdate( params );
371 if( params->Status == LORAMAC_HANDLER_ERROR )
372 {
373 LmHandlerJoin( );
374 }
375 else
376 {
377 LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS );
378 }
379 }
380
OnTxData(LmHandlerTxParams_t * params)381 static void OnTxData( LmHandlerTxParams_t* params )
382 {
383 DisplayTxUpdate( params );
384 }
385
OnRxData(LmHandlerAppData_t * appData,LmHandlerRxParams_t * params)386 static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params )
387 {
388 DisplayRxUpdate( appData, params );
389
390 switch( appData->Port )
391 {
392 case 1: // The application LED can be controlled on port 1 or 2
393 case LORAWAN_APP_PORT:
394 {
395 AppLedStateOn = appData->Buffer[0] & 0x01;
396 GpioWrite( &Led4, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 1 : 0 );
397 }
398 break;
399 default:
400 break;
401 }
402
403 // Switch LED 2 ON for each received downlink
404 GpioWrite( &Led3, 1 );
405 TimerStart( &Led3Timer );
406 }
407
OnClassChange(DeviceClass_t deviceClass)408 static void OnClassChange( DeviceClass_t deviceClass )
409 {
410 DisplayClassUpdate( deviceClass );
411
412 // Inform the server as soon as possible that the end-device has switched to ClassB
413 LmHandlerAppData_t appData =
414 {
415 .Buffer = NULL,
416 .BufferSize = 0,
417 .Port = 0,
418 };
419 LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG );
420 }
421
OnBeaconStatusChange(LoRaMacHandlerBeaconParams_t * params)422 static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params )
423 {
424 switch( params->State )
425 {
426 case LORAMAC_HANDLER_BEACON_RX:
427 {
428 TimerStart( &LedBeaconTimer );
429 break;
430 }
431 case LORAMAC_HANDLER_BEACON_LOST:
432 case LORAMAC_HANDLER_BEACON_NRX:
433 {
434 TimerStop( &LedBeaconTimer );
435 break;
436 }
437 default:
438 {
439 break;
440 }
441 }
442
443 DisplayBeaconUpdate( params );
444 }
445
446 #if( LMH_SYS_TIME_UPDATE_NEW_API == 1 )
OnSysTimeUpdate(bool isSynchronized,int32_t timeCorrection)447 static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection )
448 {
449
450 }
451 #else
OnSysTimeUpdate(void)452 static void OnSysTimeUpdate( void )
453 {
454
455 }
456 #endif
457
458 /*!
459 * Prepares the payload of the frame and transmits it.
460 */
PrepareTxFrame(void)461 static void PrepareTxFrame( void )
462 {
463 if( LmHandlerIsBusy( ) == true )
464 {
465 return;
466 }
467
468 uint8_t channel = 0;
469
470 AppData.Port = LORAWAN_APP_PORT;
471
472 CayenneLppReset( );
473 CayenneLppAddDigitalInput( channel++, AppLedStateOn );
474 CayenneLppAddAnalogInput( channel++, BoardGetBatteryLevel( ) * 100 / 254 );
475
476 CayenneLppCopy( AppData.Buffer );
477 AppData.BufferSize = CayenneLppGetSize( );
478
479 if( LmHandlerSend( &AppData, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS )
480 {
481 // Switch LED 1 ON
482 GpioWrite( &Led1, 1 );
483 TimerStart( &Led1Timer );
484 }
485 }
486
StartTxProcess(LmHandlerTxEvents_t txEvent)487 static void StartTxProcess( LmHandlerTxEvents_t txEvent )
488 {
489 switch( txEvent )
490 {
491 default:
492 // Intentional fall through
493 case LORAMAC_HANDLER_TX_ON_TIMER:
494 {
495 // Schedule 1st packet transmission
496 TimerInit( &TxTimer, OnTxTimerEvent );
497 TimerSetValue( &TxTimer, TxPeriodicity );
498 OnTxTimerEvent( NULL );
499 }
500 break;
501 case LORAMAC_HANDLER_TX_ON_EVENT:
502 {
503 }
504 break;
505 }
506 }
507
UplinkProcess(void)508 static void UplinkProcess( void )
509 {
510 uint8_t isPending = 0;
511 CRITICAL_SECTION_BEGIN( );
512 isPending = IsTxFramePending;
513 IsTxFramePending = 0;
514 CRITICAL_SECTION_END( );
515 if( isPending == 1 )
516 {
517 PrepareTxFrame( );
518 }
519 }
520
OnTxPeriodicityChanged(uint32_t periodicity)521 static void OnTxPeriodicityChanged( uint32_t periodicity )
522 {
523 TxPeriodicity = periodicity;
524
525 if( TxPeriodicity == 0 )
526 { // Revert to application default periodicity
527 TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
528 }
529
530 // Update timer periodicity
531 TimerStop( &TxTimer );
532 TimerSetValue( &TxTimer, TxPeriodicity );
533 TimerStart( &TxTimer );
534 }
535
OnTxFrameCtrlChanged(LmHandlerMsgTypes_t isTxConfirmed)536 static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed )
537 {
538 LmHandlerParams.IsTxConfirmed = isTxConfirmed;
539 }
540
OnPingSlotPeriodicityChanged(uint8_t pingSlotPeriodicity)541 static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity )
542 {
543 LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity;
544 }
545
546 /*!
547 * Function executed on TxTimer event
548 */
OnTxTimerEvent(void * context)549 static void OnTxTimerEvent( void* context )
550 {
551 TimerStop( &TxTimer );
552
553 IsTxFramePending = 1;
554
555 // Schedule next transmission
556 TimerSetValue( &TxTimer, TxPeriodicity );
557 TimerStart( &TxTimer );
558 }
559
560 /*!
561 * Function executed on Led 1 Timeout event
562 */
OnLed1TimerEvent(void * context)563 static void OnLed1TimerEvent( void* context )
564 {
565 TimerStop( &Led1Timer );
566 // Switch LED 1 OFF
567 GpioWrite( &Led1, 0 );
568 }
569
570 /*!
571 * Function executed on Led 2 Timeout event
572 */
OnLed2TimerEvent(void * context)573 static void OnLed2TimerEvent( void* context )
574 {
575 TimerStop( &Led2Timer );
576 // Switch LED 2 OFF
577 GpioWrite( &Led2, 0 );
578 }
579
580 /*!
581 * \brief Function executed on Led 3 Timeout event
582 */
OnLed3TimerEvent(void * context)583 static void OnLed3TimerEvent( void* context )
584 {
585 TimerStop( &Led3Timer );
586 // Switch LED 3 OFF
587 GpioWrite( &Led3, 0 );
588 }
589
590 /*!
591 * \brief Function executed on Beacon timer Timeout event
592 */
OnLedBeaconTimerEvent(void * context)593 static void OnLedBeaconTimerEvent( void* context )
594 {
595 GpioWrite( &Led2, 1 );
596 TimerStart( &Led2Timer );
597
598 TimerStart( &LedBeaconTimer );
599 }
600