Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
lora_driver.c
- Committer:
- fahadmirza
- Date:
- 2018-07-12
- Revision:
- 1:168a6afffbff
- Parent:
- 0:a0c5877bd360
- Child:
- 2:1ef859bc5cd2
File content as of revision 1:168a6afffbff:
/*
_ _ _____ _______
| | | | |_ _| |__ __|
| |__| | __ ___ __ | | ___ | |
| __ |/ _` \ \/ / | | / _ \| |
| | | | (_| |> < _| || (_) | |
|_| |_|\__,_/_/\_\_____\___/|_|
(C)2017 HaxIoT
*/
/*******************************************************************************
* @File : lora_driver.c
* @Author : Fahad Mirza (Haxiot)
* @Version : V1.0.0
* @Modified: 12-Apr-2018
* @Brief : LoRa Driver
******************************************************************************
* @attention
*
* <h2><center>© COPYRIGHT(c) 2017 Haxiot</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of Haxiot nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include "hxc_client.h"
#include "debug.h"
#include "stm32l0xx_nucleo.h"
#include "time_server.h"
#include "lora_driver.h"
#include "tiny_sscanf.h"
#include "utilities.h"
/* Private Macros ------------------------------------------------------------*/
#define JOIN_SEND_DELAY_MAX (10000U) // Randomization range - 10s
#define JOIN_STATUS_REQ_DELAY (7000U) // milliseconds. Join req takes 6s.
/* Private type definitions --------------------------------------------------*/
// LoRaWAN State Machine states
typedef enum eDevicState
{
DEVICE_INIT,
DEVICE_CONFIG,
DEVICE_JOIN,
DEVICE_SEND,
SLEEP_DEVICE,
DEVICE_DATA_RECEIVED,
DEVICE_JOIN_STATUS_CHECK
} eDeviceState_t;
/* Private global variables --------------------------------------------------*/
static sLoraConfig_t *LoraConfigParam;
static sLoraDriverParam_t *LoraDriverParam;
static volatile eDeviceState_t DeviceState = DEVICE_INIT;
static TimerEvent_t JoinRequestTimer;
static TimerEvent_t SensorMeasureTimer;
static TimerEvent_t NucleoLedTimer;
static TimerEvent_t JoinStatusDelayTimer;
// Object definition for data to be sent to loRa application server
static uint8_t DataBinaryBuff[64];
static sSendDataBinary_t SendDataBinary = {DataBinaryBuff, 0 , 0, 0};
// RNG handler declaration
RNG_HandleTypeDef RngHandle = {.Instance = RNG};
/* Private functions ---------------------------------------------------------*/
static void Lora_ReadData(sRecvDataBinary_t *receivedData);
static void Lora_OnLedTimerEvent(void);
static void Lora_OnJoinRequestTimerEvt(void);
static void Lora_OnJoinStatusDelayTimerEvt(void);
static void Lora_OnSensorMeasureTimerEvt(void);
static void setJoinRequestTimer(void);
/* Function definitions ------------------------------------------------------*/
/******************************************************************************
* @Brief : Initialize LoRa Modem
* @Param : sLoraConfig_t
* @Return : none
******************************************************************************/
void Lora_init(sLoraConfig_t *loraConfig, sLoraDriverParam_t *loraDriverParam)
{
LoraConfigParam = loraConfig;
LoraDriverParam = loraDriverParam;
if(Modem_Init() != AT_OK)
{
DBG_PRINTF("Modem_Init failed\r\n");
}
// Initialize RNG for join request send randomization
HAL_RNG_Init(&RngHandle);
// Timer for join request send
TimerInit(&JoinRequestTimer, Lora_OnJoinRequestTimerEvt);
// Timer for join status check
TimerInit(&JoinStatusDelayTimer, Lora_OnJoinStatusDelayTimerEvt);
// Timer for sensor occurrence measure
TimerInit(&SensorMeasureTimer, Lora_OnSensorMeasureTimerEvt);
// Timer for Nucleo LED
TimerInit(&NucleoLedTimer, Lora_OnLedTimerEvent);
}
/******************************************************************************
* @Brief : Check if the modem responds
* @Param : void
* @Return : AT_OK or other eAtStatus_t
******************************************************************************/
static eAtStatus_t is_modem_working(void)
{
return Modem_AT_Cmd(AT_CTRL, AT, NULL);
}
/******************************************************************************
* @Brief : Set Device EUI
* @Param : Pointer to Device EUI
* @Return : AT_OK or other eAtStatus_t
******************************************************************************/
eAtStatus_t Lora_setDevEui(char *devEui)
{
return Modem_AT_Cmd(AT_SET, AT_DEVEUI, devEui);
}
/******************************************************************************
* @Brief : Set Application EUI
* @Param : Pointer to Application EUI
* @Return : AT_OK or other eAtStatus_t
******************************************************************************/
eAtStatus_t Lora_setAppEui(char *appEui)
{
return Modem_AT_Cmd(AT_SET, AT_APPEUI, appEui);
}
/******************************************************************************
* @Brief : Set Application Key
* @Param : Pointer to Application Key
* @Return : AT_OK or other eAtStatus_t
******************************************************************************/
eAtStatus_t Lora_setAppKey(char *appKey)
{
return Modem_AT_Cmd(AT_SET, AT_APPKEY, appKey);
}
/******************************************************************************
* @Brief : Set join mode
* @Param : OTAA or ABP
* @Retval : AT_OK if successful, otherwise other eAtStatus_t
******************************************************************************/
eAtStatus_t Lora_setJoinMode(eJoinMode_t joinMode)
{
if(joinMode == OTAA)
{
return Modem_AT_Cmd(AT_SET, AT_NJM, "OTAA");
}
return Modem_AT_Cmd(AT_SET, AT_NJM, "ABP");
}
/******************************************************************************
* @Brief : Set Class
* @Param : CLASS_A or CLASS_C
* @Retval : AT_OK if successful, otherwise other eAtStatus_t
******************************************************************************/
eAtStatus_t Lora_setClass(char class)
{
return Modem_AT_Cmd(AT_SET, AT_CLASS, &class);
}
/******************************************************************************
* @Brief : Join network and initiate join sleep transition timer
* @Param : None
* @Retval : AT_OK or other eAtStatus_t
******************************************************************************/
static eAtStatus_t Lora_Join(void)
{
return Modem_AT_Cmd(AT_CTRL, AT_JOIN, NULL);
}
/******************************************************************************
* @Brief : Check JOIN status
* @Param : None
* @Retval : JOINED or NOT_JOINED
******************************************************************************/
static eJoinStatus_t Lora_getJoinStatus(void)
{
char joinStatus[12]; // "NOT JOINED" is 10 characters + 1 Null char
Modem_AT_Cmd(AT_GET, AT_NJS, joinStatus);
if(strncmp("JOINED", joinStatus, 6) == 0)
{
return JOINED;
}
return NOT_JOINED;
}
/******************************************************************************
* @Brief : Send uplink packet using binary payload
* @Param : Pointer of sSendDataBinary_t variable
* @Retval : AT_OK or other eAtStatus_t statuses
******************************************************************************/
static eAtStatus_t Lora_SendDataBinary(sSendDataBinary_t *binaryData)
{
return Modem_AT_Cmd(AT_SET, AT_SENDB, binaryData);
}
/******************************************************************************
* @Brief : LoRa Modem state machine
* @Param : Void
* @Return : None
******************************************************************************/
void Lora_fsm(void)
{
switch(DeviceState)
{
case DEVICE_INIT:
{
if(is_modem_working() != AT_OK)
{
DBG_PRINTF("AT failed. Resetting HW...\r\n");
// Modem isn't responding. Execute hard reset.
Modem_HardReset();
// We stay in DEVICE_INIT state and try again.
}
else
{
DeviceState = DEVICE_CONFIG;
}
break;
}
case DEVICE_CONFIG:
{
eAtStatus_t loraModemRetCode = Lora_setDevEui(LoraConfigParam->devEui);
loraModemRetCode |= Lora_setAppEui(LoraConfigParam->appEui);
loraModemRetCode |= Lora_setAppKey(LoraConfigParam->appKey);
loraModemRetCode |= Lora_setJoinMode(LoraConfigParam->joinMode);
loraModemRetCode |= Lora_setClass(LoraConfigParam->class_);
// ToDo: Maybe add FW version checking
if(loraModemRetCode == AT_OK)
{
setJoinRequestTimer();
DeviceState = SLEEP_DEVICE;
}
else if(loraModemRetCode == AT_TIMEOUT)
{
DeviceState = DEVICE_INIT;
}
else
{
DBG_PRINTF("Check your keys\r\n");
// We stay in DEVICE_CONFIG and try again
}
break;
}
case DEVICE_JOIN:
{
DBG_PRINTF("Joining...\r\n");
// Indicate a Join request using Nucleo LED
BSP_LED_On(LED_GREEN);
TimerStart(&NucleoLedTimer, 200);
switch(Lora_Join())
{
case AT_OK:
{
// Start the Join status request timer and go to sleep
TimerStart(&JoinStatusDelayTimer, JOIN_STATUS_REQ_DELAY);
DeviceState = SLEEP_DEVICE;
break;
}
case AT_TIMEOUT:
{
// The modem isn't responding. Check if it is working
DeviceState = DEVICE_INIT;
break;
}
default:
{
DBG_PRINTF("Join cmd failed\n");
// We stay in DEVICE_JOIN state and redo Lora_Join()
break;
}
}
break;
}
case DEVICE_JOIN_STATUS_CHECK:
{
if(Lora_getJoinStatus() == JOINED)
{
DBG_PRINTF("Nwk Joined\n");
// Start timer for uplink transmission for sensor-data
TimerStart( &SensorMeasureTimer, LoraDriverParam->SensorMeasureTime);
DeviceState = SLEEP_DEVICE;
}
else
{
// Try joining again
setJoinRequestTimer();
DeviceState = SLEEP_DEVICE;
}
break;
}
case SLEEP_DEVICE:
{
/* Wake up through RTC events or asynchronous event coming from HXC modem*/
if(Modem_IsNewDataReceived() == true)
{
DeviceState = DEVICE_DATA_RECEIVED;
}
break;
}
case DEVICE_SEND:
{
// Read sensor data and populate payload
LoraDriverParam->SendDataHandler(SendDataBinary.Buffer, &SendDataBinary.DataSize, &SendDataBinary.Ack, &SendDataBinary.Port);
// Initiate uplink transmission
eAtStatus_t status = Lora_SendDataBinary(&SendDataBinary);
if (status == AT_OK)
{
// Schedule the next packet
TimerStart( &SensorMeasureTimer, LoraDriverParam->SensorMeasureTime );
DeviceState = SLEEP_DEVICE;
}
else if(status == AT_TIMEOUT)
{
// Device isn't responding. Go to init.
DeviceState = DEVICE_INIT;
}
break;
}
case DEVICE_DATA_RECEIVED:
{
uint8_t rBuffer[64];
sRecvDataBinary_t rxPacket = {.Buffer = rBuffer};
Lora_ReadData(&rxPacket);
// Execute users ReceivedPacketHandler function
LoraDriverParam->ReceiveDataHandler(rxPacket.Buffer, rxPacket.DataSize, rxPacket.Ack, rxPacket.Port);
DeviceState = SLEEP_DEVICE;
break;
}
default:
{
DeviceState = DEVICE_INIT;
break;
}
}
}
static void setJoinRequestTimer(void)
{
// Use a random delay to avoid synchronized join request from all LoRa node after power up
uint32_t joinDelay = (HAL_RNG_GetRandomNumber(&RngHandle) % JOIN_SEND_DELAY_MAX) + 1;
TimerStart( &JoinRequestTimer, joinDelay);
}
/******************************************************************************
* @Brief : Function executed on JoinStatusDelayTimer Timeout event
* @Param : none
* @Return : none
******************************************************************************/
static void Lora_OnJoinRequestTimerEvt(void)
{
TimerStop(&JoinRequestTimer);
DeviceState = DEVICE_JOIN;
}
/******************************************************************************
* @Brief : Function executed on NucleoLedTimer Timeout event
* @Param : none
* @Return : none
******************************************************************************/
static void Lora_OnLedTimerEvent(void)
{
TimerStop(&NucleoLedTimer);
BSP_LED_Off(LED_GREEN);
}
/******************************************************************************
* @Brief : Function executed on JoinStatusDelayTimer Timeout event
* @Param : none
* @Return : none
******************************************************************************/
static void Lora_OnJoinStatusDelayTimerEvt(void)
{
TimerStop( &JoinStatusDelayTimer );
DeviceState = DEVICE_JOIN_STATUS_CHECK;
}
/******************************************************************************
* @Brief : Function executed on SensorMeasureTimer Timeout event
* @Param : none
* @Return : none
******************************************************************************/
static void Lora_OnSensorMeasureTimerEvt(void)
{
TimerStop( &SensorMeasureTimer );
DeviceState = DEVICE_SEND;
}
/******************************************************************************
* @Brief : Read the received downlink packet
* @Param : Pointer to sRecvDataBinary_t variable
* @Return : none
******************************************************************************/
static void Lora_ReadData(sRecvDataBinary_t *receivedData)
{
char receiveString[64];
Modem_AT_Cmd(AT_GET, AT_RECVB, receiveString);
tiny_sscanf(receiveString, "%hhu,%hhu", &(receivedData->Ack), &(receivedData->Port));
// Find the position after the second comma
char *hexString = strchr((strchr(receiveString,',') + 1),',') + 1;
receivedData->DataSize = stringHexToByteArray(hexString, receivedData->Buffer, 64);
}
/******************************************************************************
* @brief RNG MSP Initialization
* This function configures the hardware resources used in this example:
* - Peripheral's clock enable
* @param hrng: RNG handle pointer
* @retval None
******************************************************************************/
void HAL_RNG_MspInit(RNG_HandleTypeDef *hrng)
{
/* RNG Peripheral clock enable */
__HAL_RCC_RNG_CLK_ENABLE();
}