HXC Client Shield Repository.

Dependencies:   mbed

Revision:
0:bacc6e701fb4
Child:
1:d4df183e5c12
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LoRa/lora_driver.c	Mon Mar 29 15:37:08 2021 +0000
@@ -0,0 +1,616 @@
+/*
+  _    _            _____   _______
+ | |  | |          |_   _| |__   __|
+ | |__| | __ ___  __ | |  ___ | |
+ |  __  |/ _` \ \/ / | | / _ \| |
+ | |  | | (_| |>  < _| || (_) | |
+ |_|  |_|\__,_/_/\_\_____\___/|_|
+    (C)2017 HaxIoT
+*/
+/*******************************************************************************
+  * @File    : lora_driver.c
+  * @Author  : Fahad Mirza (Haxiot)
+  * @Version : V1.0.0
+  * @Modified: 18 October, 2018
+  * @Brief   : LoRa Driver
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; 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"
+#include "hxcclient_bsp.h"
+
+/* Private Macros ------------------------------------------------------------*/
+#define JOIN_SEND_DELAY_MAX   (10000U) // Randomization range - 10s
+#define JOIN_STATUS_REQ_DELAY (7000U)  // milliseconds. Join req takes 6s.
+
+// Re Authenticate every REJOIN_TIME
+#define REJOIN_TIME           (1 * 60 * 60 * 1000U) // 1 hour
+
+/* Private global variables --------------------------------------------------*/
+static sLoraConfig_t *LoraConfigParam;
+static sLoraDriverParam_t *LoraDriverParam;
+static volatile eDeviceState_t DeviceState = CLIENT_INIT;
+
+static TimerEvent_t JoinRequestTimer;
+static TimerEvent_t RejoinTimer;
+static TimerEvent_t SensorMeasureTimer;
+static TimerEvent_t JoinStatusReqTimer;
+
+// Object definition for data to be sent to loRa application server
+static uint8_t DataBinaryBuff[MAX_PAYLOAD_SIZE];
+static sSendDataBinary_t SendDataBinary = {DataBinaryBuff, 0 , 0, 0};
+
+// RNG handler declaration
+RNG_HandleTypeDef RngHandle = {.Instance = RNG};
+
+/* Private functions ---------------------------------------------------------*/
+static void OnRejoinTimerEvent(void);
+static void OnJoinRequestTimerEvt(void);
+static void OnJoinStatusReqTimerEvt(void);
+static void 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, OnJoinRequestTimerEvt);
+	// Timer for join status check
+	TimerInit(&JoinStatusReqTimer, OnJoinStatusReqTimerEvt);
+	TimerSetValue(&JoinStatusReqTimer, JOIN_STATUS_REQ_DELAY);
+	// Timer for sensor occurrence measure
+	TimerInit(&SensorMeasureTimer, OnSensorMeasureTimerEvt);
+	TimerSetValue(&SensorMeasureTimer, LoraDriverParam->UplinkCycle);
+	// Timer for Re-Authenticate / Re-Join
+	TimerInit(&RejoinTimer, OnRejoinTimerEvent);
+	TimerSetValue(&RejoinTimer, REJOIN_TIME);
+}
+
+/******************************************************************************
+  * @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
+******************************************************************************/
+static eAtStatus_t Lora_setDevEui(char *devEui)
+{
+	return Modem_AT_Cmd(AT_SET, AT_DEVEUI, devEui);
+}
+
+/******************************************************************************
+  * @Brief  : Print Device EUI
+  * @Param  : None
+  * @Retval : AT_OK or other eAtStatus_t
+******************************************************************************/
+static void Lora_printDevEui(void)
+{
+	char deveui_str[25];
+	
+	Modem_AT_Cmd(AT_GET, AT_DEVEUI, deveui_str);
+	
+	DBG_PRINTF("DevEui: %s\n", deveui_str);
+}
+
+/******************************************************************************
+  * @Brief  : Print Device Address
+  * @Param  : None
+  * @Retval : AT_OK or other eAtStatus_t
+******************************************************************************/
+static void Lora_printDevAdr(void)
+{
+    char devadr_str[15];
+
+    Modem_AT_Cmd(AT_GET, AT_DEVADR, devadr_str);
+
+    DBG_PRINTF("DevAdr: %s\n", devadr_str);
+}
+
+/******************************************************************************
+  * @Brief  : Turn on or off ADR
+  * @Param  : ADR_ON or ADR_OFF
+  * @Return : AT_OK or other eAtStatus_t
+******************************************************************************/
+static eAtStatus_t Lora_setAdr(eAdrStatus_t adrStatus)
+{
+	//char adr = (adrStatus == ADR_ON ? '1' : '0');
+	return Modem_AT_Cmd(AT_SET, AT_ADR, (uint8_t *)(&adrStatus));
+}
+
+/******************************************************************************
+  * @Brief  : Set Application EUI
+  * @Param  : Pointer to Application EUI
+  * @Return : AT_OK or other eAtStatus_t
+******************************************************************************/
+static 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
+******************************************************************************/
+static 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
+******************************************************************************/
+static 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
+******************************************************************************/
+static 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  : Get firmware version
+  * @Param  : None
+  * @Retval : Integer firmware version
+******************************************************************************/
+uint16_t Lora_getFwVersion(void)
+{
+	char fwVersion_str[5];
+	uint16_t fwVer_int;
+
+	eAtStatus_t status = Modem_AT_Cmd(AT_GET, AT_VER, fwVersion_str);
+	
+	if(status != AT_OK)
+	{
+		return 0;
+	}
+	
+	// FW version is a decimal number e.g. 1.18
+	// Convert the float number into integer.
+	uint8_t i = 0;
+	while(fwVersion_str[++i] != '.');
+
+	while(fwVersion_str[i] != '\0')
+	{
+		fwVersion_str[i] = fwVersion_str[i + 1];
+		i++;
+	}
+
+	tiny_sscanf(fwVersion_str, "%hu", &fwVer_int);
+
+	return fwVer_int;
+}
+
+/******************************************************************************
+  * @Brief  : Get battery level
+  * @Param  : None
+  * @Retval : Battery level
+******************************************************************************/
+uint8_t Lora_getBatteryLevel(void)
+{
+	char batteryLevel_str[5];
+	uint8_t batteryLevel = 0;
+
+	eAtStatus_t status = Modem_AT_Cmd(AT_GET, AT_BAT, batteryLevel_str);
+	
+	if(status == AT_OK)
+	{
+	    tiny_sscanf(batteryLevel_str, "%hhu", &batteryLevel);
+	}
+
+	return batteryLevel;
+}
+
+/******************************************************************************
+  * @Brief  : Update the uplink rate
+  * @Param  : time_s seconds
+  * @Retval : None
+******************************************************************************/
+void Lora_updateUplinkRate(uint32_t time_s)
+{
+    TimerSetValue(&SensorMeasureTimer, (time_s * 1000));
+    TimerReset(&SensorMeasureTimer);
+}
+
+/******************************************************************************
+  * @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  : Read the received downlink packet
+ * @Param  : Pointer to sRecvDataBinary_t variable
+ * @Return : AT_OK or other eAtStatus_t statuses
+******************************************************************************/
+static eAtStatus_t Lora_ReadData(sRecvDataBinary_t *rxData)
+{
+	char *rxString = Modem_GetResponseBuffer();
+
+	// Find the position after :
+	// e.g. rxdata:0,1,86GF25
+	rxString = (strchr(rxString,':') + 1);
+
+	if(rxString == NULL)
+	{
+		return AT_ERROR;
+	}
+
+	if(tiny_sscanf(rxString, "%hhu,%hhu", &(rxData->Ack), &(rxData->Port)) != 2)
+	{
+		return AT_ERROR;
+	}
+
+	// Find the position after the second comma
+	rxString = strchr((strchr(rxString,',') + 1),',') + 1;
+	if(rxString == NULL)
+	{
+		return AT_ERROR;
+	}
+
+	rxData->DataSize = stringHexToByteArray(rxString, rxData->Buffer, MAX_PAYLOAD_SIZE);
+
+	return AT_OK;
+}
+
+/******************************************************************************
+  * @Brief  : LoRa Modem state machine
+  * @Param  : Void
+  * @Return : None
+******************************************************************************/
+void Lora_fsm(void)
+{
+    switch(DeviceState)
+    {
+        case CLIENT_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 CLIENT_INIT state and try again.
+    	    }
+    	    else
+    	    {
+    	    	DeviceState = CLIENT_CONFIG;
+    	    }
+
+            break;
+        }
+        case CLIENT_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);
+			loraModemRetCode |= Lora_setAdr(LoraConfigParam->AdrStatus);				
+
+			if(loraModemRetCode == AT_OK)
+			{
+				// If users use AUTO as Device EUI, print the DevEUI
+			    // so that they can add that on X-ON
+			    Lora_printDevEui();
+				
+				setJoinRequestTimer();
+				DeviceState = CLIENT_SLEEP;
+			}
+			else if(loraModemRetCode == AT_TIMEOUT)
+			{
+				DeviceState = CLIENT_INIT;
+			}
+			else
+			{
+				DBG_PRINTF("Check your keys\r\n");
+				while(1);
+			}
+
+			break;
+        }
+        case CLIENT_JOIN:
+        {
+            DBG_PRINTF("Joining...\r\n");
+            HXC_BSP_RGB_Off();
+
+            switch(Lora_Join())
+            {
+                case AT_OK:
+                {
+                	// Start the Join status request timer and go to sleep
+                	TimerStart(&JoinStatusReqTimer);
+                    DeviceState = CLIENT_SLEEP;
+                    break;
+                }
+                case AT_TIMEOUT:
+                {
+                	// The modem isn't responding. Execute hard reset
+                	DeviceState = CLIENT_INIT;
+                	break;
+                }
+                default:
+                {
+                	DBG_PRINTF("Join cmd failed\n");
+                    // We stay in CLIENT_JOIN state and redo Lora_Join()
+                	break;
+                }
+            }
+
+            break;
+        }
+        case CLIENT_JOIN_STATUS_CHECK:
+        {
+        	if(Lora_getJoinStatus() == JOINED)
+        	{
+        		// Indicate Join status using LED
+    			HXC_BSP_RGB_On(PINK);
+        		DBG_PRINTF("Nwk Joined\n");
+        		Lora_printDevAdr();
+        		
+        		// Start timer for Re-Authentication
+        		TimerStart(&RejoinTimer);
+        		
+				// Start timer for uplink transmission for sensor-data
+				TimerStart(&SensorMeasureTimer);
+				DeviceState = CLIENT_SLEEP;
+        	}
+        	else
+        	{
+        		// Try joining again
+        		setJoinRequestTimer();
+                DeviceState = CLIENT_SLEEP;
+        	}
+        	break;
+        }
+		case CLIENT_SLEEP:
+        {
+            /* Wake up through RTC events or asynchronous event coming from HXC modem*/
+        	if(Modem_IsNewDataReceived() == true)
+        	{
+        		DeviceState = CLIENT_DATA_RECEIVED;
+        	}
+            break;
+        }
+		case CLIENT_SEND:
+		{
+			// Read sensor data and populate payload
+			SendDataBinary.DataSize = LoraDriverParam->UplinkHandler(\
+			                          SendDataBinary.Buffer         ,\
+			                          MAX_PAYLOAD_SIZE              ,\
+			                          &SendDataBinary.Ack           ,\
+			                          &SendDataBinary.Port          );
+			
+			// Initiate uplink transmission
+			eAtStatus_t status = Lora_SendDataBinary(&SendDataBinary);
+			
+			if (status == AT_OK)
+			{
+				DBG_PRINTF("Uplink sent\n");
+				// Schedule the next packet
+				TimerStart(&SensorMeasureTimer);
+				DeviceState = CLIENT_SLEEP;
+			}
+			else if(status == AT_TIMEOUT)
+			{
+				// Device isn't responding. Go to init.
+				DeviceState = CLIENT_INIT;
+			}
+			else if(status == AT_NO_NET_JOINED)
+            {
+                DeviceState = CLIENT_JOIN;
+            }
+			else
+			{
+				DBG_PRINTF("Uplink Failed (Error: %d)\n", (uint8_t)status);
+			}
+		    break;
+		}
+		case CLIENT_DATA_RECEIVED:
+		{
+			uint8_t rBuffer[64];
+			sRecvDataBinary_t rxPacket = {.Buffer = rBuffer};
+
+			if(Lora_ReadData(&rxPacket) == AT_OK)
+			{
+				// Execute users ReceivedPacketHandler function
+				LoraDriverParam->DownlinkHandler(rxPacket.Buffer, rxPacket.DataSize, rxPacket.Ack, rxPacket.Port);
+			}
+			else
+			{
+				DBG_PRINTF("Dwlink read failed\n");
+			}
+
+			DeviceState = CLIENT_SLEEP;
+			break;
+		}
+		default:
+		{
+			DeviceState = CLIENT_INIT;
+		    break;
+		}
+    }
+}
+
+/******************************************************************************
+ * @Brief  : Set Join request timer
+ * @Param  : none
+ * @Return : none
+******************************************************************************/
+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;
+	
+	TimerSetValue(&JoinRequestTimer, joinDelay);
+	TimerStart(&JoinRequestTimer);
+}
+
+/******************************************************************************
+ * @Brief  : Function executed on JoinStatusDelayTimer Timeout event
+ * @Param  : none
+ * @Return : none
+******************************************************************************/
+static void OnJoinRequestTimerEvt(void)
+{
+	TimerStop(&JoinRequestTimer);
+	// If this is a re-join SensorMeasureTimer is active
+	TimerStop(&SensorMeasureTimer);
+	
+	DeviceState = CLIENT_JOIN;
+}
+
+/******************************************************************************
+ * @Brief  : Function executed on JoinStatusDelayTimer Timeout event
+ * @Param  : none
+ * @Return : none
+******************************************************************************/
+static void OnJoinStatusReqTimerEvt(void)
+{
+	TimerStop(&JoinStatusReqTimer);
+	DeviceState = CLIENT_JOIN_STATUS_CHECK;
+}
+
+/******************************************************************************
+ * @Brief  : Function executed on SensorMeasureTimer Timeout event
+ * @Param  : none
+ * @Return : none
+******************************************************************************/
+static void OnSensorMeasureTimerEvt(void)
+{
+	TimerStop(&SensorMeasureTimer);
+    DeviceState = CLIENT_SEND;
+}
+
+/******************************************************************************
+ * @Brief  : Function executed on RejoinTimer Timeout event
+ * @Param  : none
+ * @Return : none
+******************************************************************************/
+static void OnRejoinTimerEvent(void)
+{
+	TimerStop(&RejoinTimer);
+	setJoinRequestTimer();
+}
+
+/******************************************************************************
+  * @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();
+}
+
+