text: /*!
   * \file      main.c
   *
   * \brief     Performs a periodic uplink
   *
   * \copyright Revised BSD License, see section \ref LICENSE.
   *
   * \code
   *                ______                              _
   *               / _____)             _              | |
   *              ( (____  _____ ____ _| |_ _____  ____| |__
   *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
   *               _____) ) ____| | | || |_| ____( (___| | | |
   *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
   *              (C)2013-2018 Semtech
   *
   * \endcode
   *
   * \author    Miguel Luis ( Semtech )
   */
  
  /*! \file periodic-uplink/SKiM880B/main.c */
  
  #include <stdio.h>
  #include "../firmwareVersion.h"
  #include "../../common/githubVersion.h"
  #include "utilities.h"
  #include "board.h"
  #include "gpio.h"
  #include "uart.h"
  #include "RegionCommon.h"
  
  #include "cli.h"
  #include "Commissioning.h"
  #include "LmHandler.h"
  #include "LmhpCompliance.h"
  #include "CayenneLpp.h"
  #include "LmHandlerMsgDisplay.h"
  
  #ifndef ACTIVE_REGION
  
  #warning "No active region defined, LORAMAC_REGION_EU868 will be used as default."
  
  #define ACTIVE_REGION LORAMAC_REGION_EU868
  
  #endif
  
  /*!
   * LoRaWAN default end-device class
   */
  #define LORAWAN_DEFAULT_CLASS                       CLASS_A
  
  /*!
   * Defines the application data transmission duty cycle. 5s, value in [ms].
   */
  #define APP_TX_DUTYCYCLE                            5000
  
  /*!
   * Defines a random delay for application data transmission duty cycle. 1s,
   * value in [ms].
   */
  #define APP_TX_DUTYCYCLE_RND                        1000
  
  /*!
   * LoRaWAN Adaptive Data Rate
   *
   * \remark Please note that when ADR is enabled the end-device should be static
   */
  #define LORAWAN_ADR_STATE                           LORAMAC_HANDLER_ADR_ON
  
  /*!
   * Default datarate
   *
   * \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled 
   */
  #define LORAWAN_DEFAULT_DATARATE                    DR_0
  
  /*!
   * LoRaWAN confirmed messages
   */
  #define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE         LORAMAC_HANDLER_UNCONFIRMED_MSG
  
  /*!
   * User application data buffer size
   */
  #define LORAWAN_APP_DATA_BUFFER_MAX_SIZE            242
  
  /*!
   * LoRaWAN ETSI duty cycle control enable/disable
   *
   * \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes
   */
  #define LORAWAN_DUTYCYCLE_ON                        true
  
  /*!
   * LoRaWAN application port
   * @remark The allowed port range is from 1 up to 223. Other values are reserved.
   */
  #define LORAWAN_APP_PORT                            3
  
  /*!
   *
   */
  typedef enum
  {
      LORAMAC_HANDLER_TX_ON_TIMER,
      LORAMAC_HANDLER_TX_ON_EVENT,
  }LmHandlerTxEvents_t;
  
  /*!
   * User application data
   */
  static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE];
  
  /*!
   * User application data structure
   */
  static LmHandlerAppData_t AppData =
  {
      .Buffer = AppDataBuffer,
      .BufferSize = 0,
      .Port = 0,
  };
  
  /*!
   * Specifies the state of the application LED
   */
  static bool AppLedStateOn = false;
  
  /*!
   * Timer to handle the application data transmission duty cycle
   */
  static TimerEvent_t TxTimer;
  
  /*!
   * Timer to handle the state of LED4
   */
  static TimerEvent_t Led4Timer;
  
  /*!
   * Timer to handle the state of LED2
   */
  static TimerEvent_t Led2Timer;
  
  /*!
   * Timer to handle the state of LED beacon indicator
   */
  static TimerEvent_t LedBeaconTimer;
  
  static void OnMacProcessNotify( void );
  static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size );
  static void OnNetworkParametersChange( CommissioningParams_t* params );
  static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn );
  static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn );
  static void OnJoinRequest( LmHandlerJoinParams_t* params );
  static void OnTxData( LmHandlerTxParams_t* params );
  static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params );
  static void OnClassChange( DeviceClass_t deviceClass );
  static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params );
  #if( LMH_SYS_TIME_UPDATE_NEW_API == 1 )
  static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection );
  #else
  static void OnSysTimeUpdate( void );
  #endif
  static void PrepareTxFrame( void );
  static void StartTxProcess( LmHandlerTxEvents_t txEvent );
  static void UplinkProcess( void );
  
  static void OnTxPeriodicityChanged( uint32_t periodicity );
  static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed );
  static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity );
  
  /*!
   * Function executed on TxTimer event
   */
  static void OnTxTimerEvent( void* context );
  
  /*!
   * Function executed on Led 4 Timeout event
   */
  static void OnLed4TimerEvent( void* context );
  
  /*!
   * Function executed on Led 2 Timeout event
   */
  static void OnLed2TimerEvent( void* context );
  
  /*!
   * \brief Function executed on Beacon timer Timeout event
   */
  static void OnLedBeaconTimerEvent( void* context );
  
  static LmHandlerCallbacks_t LmHandlerCallbacks =
  {
      .GetBatteryLevel = BoardGetBatteryLevel,
      .GetTemperature = NULL,
      .GetRandomSeed = BoardGetRandomSeed,
      .OnMacProcess = OnMacProcessNotify,
      .OnNvmDataChange = OnNvmDataChange,
      .OnNetworkParametersChange = OnNetworkParametersChange,
      .OnMacMcpsRequest = OnMacMcpsRequest,
      .OnMacMlmeRequest = OnMacMlmeRequest,
      .OnJoinRequest = OnJoinRequest,
      .OnTxData = OnTxData,
      .OnRxData = OnRxData,
      .OnClassChange= OnClassChange,
      .OnBeaconStatusChange = OnBeaconStatusChange,
      .OnSysTimeUpdate = OnSysTimeUpdate,
  };
  
  static LmHandlerParams_t LmHandlerParams =
  {
      .Region = ACTIVE_REGION,
      .AdrEnable = LORAWAN_ADR_STATE,
      .IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE,
      .TxDatarate = LORAWAN_DEFAULT_DATARATE,
      .PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK,
      .DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON,
      .DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE,
      .DataBuffer = AppDataBuffer,
      .PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY,
  };
  
  static LmhpComplianceParams_t LmhpComplianceParams =
  {
      .FwVersion.Value = FIRMWARE_VERSION,
      .OnTxPeriodicityChanged = OnTxPeriodicityChanged,
      .OnTxFrameCtrlChanged = OnTxFrameCtrlChanged,
      .OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged,
  };
  
  /*!
   * Indicates if LoRaMacProcess call is pending.
   * 
   * \warning If variable is equal to 0 then the MCU can be set in low power mode
   */
  static volatile uint8_t IsMacProcessPending = 0;
  
  static volatile uint8_t IsTxFramePending = 0;
  
  static volatile uint32_t TxPeriodicity = 0;
  
  /*!
   * LED GPIO pins objects
   */
  extern Gpio_t Led4; // Tx
  extern Gpio_t Led2; // Rx and blinks every 5 seconds when beacon is acquired
  extern Gpio_t Led3; // App
  
  /*!
   * UART object used for command line interface handling
   */
  extern Uart_t Uart1;
  
  /*!
   * Main application entry point.
   */
  int main( void )
  {
      BoardInitMcu( );
      BoardInitPeriph( );
  
      TimerInit( &Led4Timer, OnLed4TimerEvent );
      TimerSetValue( &Led4Timer, 25 );
  
      TimerInit( &Led2Timer, OnLed2TimerEvent );
      TimerSetValue( &Led2Timer, 25 );
  
      TimerInit( &LedBeaconTimer, OnLedBeaconTimerEvent );
      TimerSetValue( &LedBeaconTimer, 5000 );
  
      // Initialize transmission periodicity variable
      TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
  
      const Version_t appVersion = { .Value = FIRMWARE_VERSION };
      const Version_t gitHubVersion = { .Value = GITHUB_VERSION };
      DisplayAppInfo( "periodic-uplink-lpp", 
                      &appVersion,
                      &gitHubVersion );
  
      if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS )
      {
          printf( "LoRaMac wasn't properly initialized\n" );
          // Fatal error, endless loop.
          while ( 1 )
          {
          }
      }
  
      // Set system maximum tolerated rx error in milliseconds
      LmHandlerSetSystemMaxRxError( 20 );
  
      // The LoRa-Alliance Compliance protocol package should always be
      // initialized and activated.
      LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams );
  
      LmHandlerJoin( );
  
      StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER );
  
      while( 1 )
      {
          // Process characters sent over the command line interface
          CliProcess( &Uart1 );
  
          // Processes the LoRaMac events
          LmHandlerProcess( );
  
          // Process application uplinks management
          UplinkProcess( );
  
          CRITICAL_SECTION_BEGIN( );
          if( IsMacProcessPending == 1 )
          {
              // Clear flag and prevent MCU to go into low power modes.
              IsMacProcessPending = 0;
          }
          else
          {
              // The MCU wakes up through events
              BoardLowPowerHandler( );
          }
          CRITICAL_SECTION_END( );
      }
  }
  
  static void OnMacProcessNotify( void )
  {
      IsMacProcessPending = 1;
  }
  
  static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size )
  {
      DisplayNvmDataChange( state, size );
  }
  
  static void OnNetworkParametersChange( CommissioningParams_t* params )
  {
      DisplayNetworkParametersUpdate( params );
  }
  
  static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn )
  {
      DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn );
  }
  
  static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn )
  {
      DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn );
  }
  
  static void OnJoinRequest( LmHandlerJoinParams_t* params )
  {
      DisplayJoinRequestUpdate( params );
      if( params->Status == LORAMAC_HANDLER_ERROR )
      {
          LmHandlerJoin( );
      }
      else
      {
          LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS );
      }
  }
  
  static void OnTxData( LmHandlerTxParams_t* params )
  {
      DisplayTxUpdate( params );
  }
  
  static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params )
  {
      DisplayRxUpdate( appData, params );
  
      switch( appData->Port )
      {
      case 1: // The application LED can be controlled on port 1 or 2
      case LORAWAN_APP_PORT:
          {
              AppLedStateOn = appData->Buffer[0] & 0x01;
              GpioWrite( &Led3, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 1 : 0 );
          }
          break;
      default:
          break;
      }
  
      // Switch LED 2 ON for each received downlink
      GpioWrite( &Led2, 1 );
      TimerStart( &Led2Timer );
  }
  
  static void OnClassChange( DeviceClass_t deviceClass )
  {
      DisplayClassUpdate( deviceClass );
  
      // Inform the server as soon as possible that the end-device has switched to ClassB
      LmHandlerAppData_t appData =
      {
          .Buffer = NULL,
          .BufferSize = 0,
          .Port = 0,
      };
      LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG );
  }
  
  static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params )
  {
      switch( params->State )
      {
          case LORAMAC_HANDLER_BEACON_RX:
          {
              TimerStart( &LedBeaconTimer );
              break;
          }
          case LORAMAC_HANDLER_BEACON_LOST:
          case LORAMAC_HANDLER_BEACON_NRX:
          {
              TimerStop( &LedBeaconTimer );
              break;
          }
          default:
          {
              break;
          }
      }
  
      DisplayBeaconUpdate( params );
  }
  
  #if( LMH_SYS_TIME_UPDATE_NEW_API == 1 )
  static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection )
  {
  
  }
  #else
  static void OnSysTimeUpdate( void )
  {
  
  }
  #endif
  
  /*!
   * Prepares the payload of the frame and transmits it.
   */
  static void PrepareTxFrame( void )
  {
      if( LmHandlerIsBusy( ) == true )
      {
          return;
      }
  
      uint8_t channel = 0;
  
      AppData.Port = LORAWAN_APP_PORT;
  
      CayenneLppReset( );
  
      uint8_t potiPercentage = 0;
      uint16_t vdd = 0;
  
      // Read the current potentiometer setting in percent
      potiPercentage = BoardGetPotiLevel( );
  
      // Read the current voltage level
      BoardGetBatteryLevel( ); // Updates the value returned by BoardGetBatteryVoltage( ) function.
      vdd = BoardGetBatteryVoltage( );
  
      CayenneLppAddDigitalInput( channel++, AppLedStateOn );
      CayenneLppAddAnalogInput( channel++, BoardGetBatteryLevel( ) * 100 / 254 );
      CayenneLppAddAnalogInput( channel++, potiPercentage );
      CayenneLppAddAnalogInput( channel++, vdd );
  
      CayenneLppCopy( AppData.Buffer );
      AppData.BufferSize = CayenneLppGetSize( );
  
      if( LmHandlerSend( &AppData, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS )
      {
          // Switch LED 4 ON
          GpioWrite( &Led4, 1 );
          TimerStart( &Led4Timer );
      }
  }
  
  static void StartTxProcess( LmHandlerTxEvents_t txEvent )
  {
      switch( txEvent )
      {
      default:
          // Intentional fall through
      case LORAMAC_HANDLER_TX_ON_TIMER:
          {
              // Schedule 1st packet transmission
              TimerInit( &TxTimer, OnTxTimerEvent );
              TimerSetValue( &TxTimer, TxPeriodicity );
              OnTxTimerEvent( NULL );
          }
          break;
      case LORAMAC_HANDLER_TX_ON_EVENT:
          {
          }
          break;
      }
  }
  
  static void UplinkProcess( void )
  {
      uint8_t isPending = 0;
      CRITICAL_SECTION_BEGIN( );
      isPending = IsTxFramePending;
      IsTxFramePending = 0;
      CRITICAL_SECTION_END( );
      if( isPending == 1 )
      {
          PrepareTxFrame( );
      }
  }
  
  static void OnTxPeriodicityChanged( uint32_t periodicity )
  {
      TxPeriodicity = periodicity;
  
      if( TxPeriodicity == 0 )
      { // Revert to application default periodicity
          TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
      }
  }
  
  static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed )
  {
      LmHandlerParams.IsTxConfirmed = isTxConfirmed;
  }
  
  static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity )
  {
      LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity;
  }
  
  /*!
   * Function executed on TxTimer event
   */
  static void OnTxTimerEvent( void* context )
  {
      TimerStop( &TxTimer );
  
      IsTxFramePending = 1;
  
      // Schedule next transmission
      TimerSetValue( &TxTimer, TxPeriodicity );
      TimerStart( &TxTimer );
  }
  
  /*!
   * Function executed on Led 4 Timeout event
   */
  static void OnLed4TimerEvent( void* context )
  {
      TimerStop( &Led4Timer );
      // Switch LED 4 OFF
      GpioWrite( &Led4, 0 );
  }
  
  /*!
   * Function executed on Led 2 Timeout event
   */
  static void OnLed2TimerEvent( void* context )
  {
      TimerStop( &Led2Timer );
      // Switch LED 2 OFF
      GpioWrite( &Led2, 0 );
  }
  
  /*!
   * \brief Function executed on Beacon timer Timeout event
   */
  static void OnLedBeaconTimerEvent( void* context )
  {
      GpioWrite( &Led2, 1 );
      TimerStart( &Led2Timer );
  
      TimerStart( &LedBeaconTimer );
  }
  