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