Lora Node

Architecture

lora_node.svg


Project

Source Project

WORKSPACE_B-L072Z-LRWAN1_ENIB.zip

main.c
#include "hw.h"
#include "low_power_manager.h"
#include "lora.h"
#include "bsp.h"
#include "timeServer.h"
#include "vcom.h"
#include "version.h"
#include "hts221.h"
#include "lps25hb.h"

// CAYENNE_LPP is myDevices Application server.
#define CAYENNE_LPP
#define LPP_DATATYPE_DIGITAL_INPUT  0x0
#define LPP_DATATYPE_DIGITAL_OUTPUT 0x1
#define LPP_DATATYPE_HUMIDITY       0x68
#define LPP_DATATYPE_TEMPERATURE    0x67
#define LPP_DATATYPE_BAROMETER      0x73
#define LPP_APP_PORT 99

// Defines the application data transmission duty cycle. 5s, value in [ms].
#define APP_TX_DUTYCYCLE                            2000

// LoRaWAN Adaptive Data Rate
// @note Please note that when ADR is enabled the end-device should be static
#define LORAWAN_ADR_STATE LORAWAN_ADR_ON
/*!
 * LoRaWAN Default data Rate Data Rate
 * @note Please note that LORAWAN_DEFAULT_DATA_RATE is used only when ADR is disabled 
 */
#define LORAWAN_DEFAULT_DATA_RATE DR_0

// LoRaWAN application port
// @note do not use 224. It is reserved for certification

#define LORAWAN_APP_PORT                            2
#define LORAWAN_DEFAULT_CLASS                       CLASS_A
#define LORAWAN_DEFAULT_CONFIRM_MSG_STATE           LORAWAN_UNCONFIRMED_MSG
#define LORAWAN_APP_DATA_BUFF_SIZE                           64

static uint8_t AppDataBuff[LORAWAN_APP_DATA_BUFF_SIZE];
static lora_AppData_t AppData={ AppDataBuff,  0 ,0 };
static void LORA_RxData( lora_AppData_t *AppData); 		/* call back when LoRa endNode has received a frame*/
static void LORA_HasJoined( void );						/* call back when LoRa endNode has just joined*/
static void LORA_ConfirmClass ( DeviceClass_t Class );	/* call back when LoRa endNode has just switch the class*/
static void LORA_TxNeeded ( void );						/* call back when server needs endNode to send a frame*/
static void Send( void );								/* LoRa endNode send request*/
static void LoraStartTx(TxEventType_t EventType);		/* start the tx process*/

static void OnTxTimerEvent( void );						/* tx timer callback function*/

static LoRaMainCallback_t LoRaMainCallbacks = { HW_GetBatteryLevel,
                                                HW_GetTemperatureLevel,
                                                HW_GetUniqueId,
                                                HW_GetRandomSeed,
                                                LORA_RxData,
                                                LORA_HasJoined,
                                                LORA_ConfirmClass,
                                                LORA_TxNeeded};
static uint8_t AppLedStateOn = RESET;
static TimerEvent_t TxTimer;
static TimerEvent_t TxLedTimer;
static void OnTimerLedEvent( void );
static  LoRaParam_t LoRaParamInit= {LORAWAN_ADR_STATE,
                                    LORAWAN_DEFAULT_DATA_RATE,  
                                    LORAWAN_PUBLIC_NETWORK};

//###############################################################################
//							MAIN
//###############################################################################
int main( void )
{
  HAL_Init();
  SystemClock_Config();
  DBG_Init();
  HW_Init();
  i2c1_Init();
  LPM_SetOffMode(LPM_APPLI_Id , LPM_Disable );		  /*Disbale Stand-by mode*/
  LORA_Init( &LoRaMainCallbacks, &LoRaParamInit);		/* Configure the Lora Stack*/
  PRINTF("VERSION: %X\n\r", VERSION);
  LORA_Join();
  LoraStartTx( TX_ON_TIMER) ;
  //------------------------------------------------
  // 				INIT SENSORS
  //------------------------------------------------
  hts221_activate();
  hts221_storeCalibration();
  lps25hb_setup();
  //------------------------------------------------

  while( 1 )
  {
			DISABLE_IRQ( );
			/* if an interrupt has occurred after DISABLE_IRQ, it is kept pending
			 * and cortex will not enter low power anyway  */
			#ifndef LOW_POWER_DISABLE
				LPM_EnterLowPower( );
			#endif
			ENABLE_IRQ();
  }
}
//###############################################################################
//###############################################################################

static void LORA_HasJoined( void )
{
			#if( OVER_THE_AIR_ACTIVATION != 0 )
			  PRINTF("JOINED\n\r");
			#endif
			  LORA_RequestClass( LORAWAN_DEFAULT_CLASS );
}
//===============================================================================

static void Send( void )
{
  uint16_t pressure = 0;
  int16_t temperature = 0;
  uint16_t humidity = 0;
  uint8_t batteryLevel;
  
  if ( LORA_JoinStatus () != LORA_SET)
  {
    // Not joined, try again later
    LORA_Join();
    return;
  }
  
  DBG_PRINTF("SEND REQUEST\n\r");
  TimerInit( &TxLedTimer, OnTimerLedEvent );
  TimerSetValue(  &TxLedTimer, 200);
  LED_On( LED_RED1 ) ; 
  TimerStart( &TxLedTimer );  

  uint8_t cchannel=0;
  temperature = ( int16_t )( hts221_getTemperature() * 10 );     /* in °C * 10 */
  pressure    = ( uint16_t )( lps25hb_getPressure() * 100 / 10 );  /* in hPa / 10 */
  humidity    = ( uint16_t )( hts221_getHumidity()  );        /* in %*2     */
  uint32_t i = 0;
  batteryLevel = HW_GetBatteryLevel( );                     /* 1 (very low) to 254 (fully charged) */
  AppData.Port = LPP_APP_PORT;
  AppData.Buff[i++] = cchannel++;
  AppData.Buff[i++] = LPP_DATATYPE_BAROMETER;
  AppData.Buff[i++] = ( pressure >> 8 ) & 0xFF;
  AppData.Buff[i++] = pressure & 0xFF;
  AppData.Buff[i++] = cchannel++;
  AppData.Buff[i++] = LPP_DATATYPE_TEMPERATURE; 
  AppData.Buff[i++] = ( temperature >> 8 ) & 0xFF;
  AppData.Buff[i++] = temperature & 0xFF;
  AppData.Buff[i++] = cchannel++;
  AppData.Buff[i++] = LPP_DATATYPE_HUMIDITY;
  AppData.Buff[i++] = humidity & 0xFF;

  AppData.Buff[i++] = cchannel++;
  AppData.Buff[i++] = LPP_DATATYPE_DIGITAL_INPUT; 
  AppData.Buff[i++] = batteryLevel*100/254;
  AppData.Buff[i++] = cchannel++;
  AppData.Buff[i++] = LPP_DATATYPE_DIGITAL_OUTPUT; 
  AppData.Buff[i++] = AppLedStateOn;

  AppData.BuffSize = i;
  
  LORA_send( &AppData, LORAWAN_DEFAULT_CONFIRM_MSG_STATE);
}
//===============================================================================

static void LORA_RxData( lora_AppData_t *AppData )
{
  DBG_PRINTF("PACKET RECEIVED ON PORT %d\n\r", AppData->Port);
  switch (AppData->Port)
  {
    case 3:
    // this port switches the class
    if( AppData->BuffSize == 1 )
    {
      switch (  AppData->Buff[0] )
      {
        case 0:
        {
          LORA_RequestClass(CLASS_A);
          break;
        }
        case 1:
        {
          LORA_RequestClass(CLASS_B);
          break;
        }
        case 2:
        {
          LORA_RequestClass(CLASS_C);
          break;
        }
        default:
          break;
      }
    }
    break;
    case LORAWAN_APP_PORT:
    if( AppData->BuffSize == 1 )
    {
      AppLedStateOn = AppData->Buff[0] & 0x01;
      if ( AppLedStateOn == RESET )
      {
        PRINTF("LED OFF\n\r");
        LED_Off( LED_BLUE ) ; 
      }
      else
      {
        PRINTF("LED ON\n\r");
        LED_On( LED_BLUE ) ; 
      }
    }
    break;
  case LPP_APP_PORT:
  {
    AppLedStateOn= (AppData->Buff[2] == 100) ?  0x01 : 0x00;
    if ( AppLedStateOn == RESET )
    {
      PRINTF("LED OFF\n\r");
      LED_Off( LED_BLUE ) ; 
      
    }
    else
    {
      PRINTF("LED ON\n\r");
      LED_On( LED_BLUE ) ; 
    }
    break;
  }
  default:
    break;
  }
}
//===============================================================================
static void OnTxTimerEvent( void )
{
  Send( );
  TimerStart( &TxTimer);		  // Wait for next tx slot
}
//===============================================================================
static void LoraStartTx(TxEventType_t EventType)
{
  if (EventType == TX_ON_TIMER)
  {
    // send everytime timer elapses
    TimerInit( &TxTimer, OnTxTimerEvent );
    TimerSetValue( &TxTimer,  APP_TX_DUTYCYCLE); 
    OnTxTimerEvent();
  }
  else
  {
    // send everytime button is pushed
    GPIO_InitTypeDef initStruct={0};
  
    initStruct.Mode =GPIO_MODE_IT_RISING;
    initStruct.Pull = GPIO_PULLUP;
    initStruct.Speed = GPIO_SPEED_HIGH;

    //USER_BUTTON_GPIO_PORT
    HW_GPIO_Init( GPIOB, GPIO_PIN_2, &initStruct );
    HW_GPIO_SetIrq( GPIOB, GPIO_PIN_2, 0, Send );
  }
}
//===============================================================================
static void LORA_ConfirmClass ( DeviceClass_t Class )
{
  PRINTF("switch to class %c done\n\r","ABC"[Class] );

  /*Optionnal*/
  /*informs the server that switch has occurred ASAP*/
  AppData.BuffSize = 0;
  AppData.Port = LORAWAN_APP_PORT;
  
  LORA_send( &AppData, LORAWAN_UNCONFIRMED_MSG);
}
//===============================================================================
static void LORA_TxNeeded ( void )
{
  AppData.BuffSize = 0;
  AppData.Port = LORAWAN_APP_PORT;
  
  LORA_send( &AppData, LORAWAN_UNCONFIRMED_MSG);
}
//===============================================================================
static void OnTimerLedEvent( void )
{
  LED_Off( LED_RED1 ) ; 
}
//===============================================================================

On startup, note the Device ID printed on the serial Terminal ( gtkterm or minicom, configured /dev/ttyACM0 115200-8-N-1 ) :

DevEui= 34-31-37-32-5E-36-7E-0D
AppEui= 70-B3-D5-7E-D0-00-AE-BD
AppKey= FD 5E 2B A6 84 49 26 DA EA 33 6C 59 E0 C0 ED DD

VERSION: 44101150
txDone
rxTimeOut
rxDone
JOINED
txDone
rxTimeOut