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.
Diff: lora_driver.c
- Revision:
- 0:a0c5877bd360
- Child:
- 1:168a6afffbff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lora_driver.c Thu Jul 12 00:50:48 2018 +0000
@@ -0,0 +1,469 @@
+/*
+ _ _ _____ _______
+ | | | | |_ _| |__ __|
+ | |__| | __ ___ __ | | ___ | |
+ | __ |/ _` \ \/ / | | / _ \| |
+ | | | | (_| |> < _| || (_) | |
+ |_| |_|\__,_/_/\_\_____\___/|_|
+ (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:
+ {
+ DBG_PRINTF("INIT\r\n");
+ if(is_modem_working() != AT_OK)
+ {
+ DBG_PRINTF("AT failed\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:
+ {
+ DBG_PRINTF("Config\r\n");
+ 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("Join\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();
+}
+
+