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