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