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