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/SKiM880B/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