Fahad Mirza
/
Nucleo_HXC900
A demo application for HXC900 LoRaWAN module using Nucleo-L053R8.
LoRa/lora_driver.c
- Committer:
- fahadmirza
- Date:
- 2018-11-01
- Revision:
- 25:50414f44a431
- Parent:
- 24:f3b987589609
- Child:
- 26:176e648c03f6
File content as of revision 25:50414f44a431:
/* _ _ _____ _______ | | | | |_ _| |__ __| | |__| | __ ___ __ | | ___ | | | __ |/ _` \ \/ / | | / _ \| | | | | | (_| |> < _| || (_) | | |_| |_|\__,_/_/\_\_____\___/|_| (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>© 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. // 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 = DEVICE_INIT; static TimerEvent_t JoinRequestTimer; static TimerEvent_t RejoinTimer; 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 OnRejoinTimerEvent(void); static void OnLedTimerEvent(void); static void OnJoinRequestTimerEvt(void); static void OnJoinStatusDelayTimerEvt(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(&JoinStatusDelayTimer, OnJoinStatusDelayTimerEvt); // Timer for sensor occurrence measure TimerInit(&SensorMeasureTimer, OnSensorMeasureTimerEvt); // Timer for Nucleo LED TimerInit(&NucleoLedTimer, OnLedTimerEvent); // Timer for Re Authenticate / Join TimerInit(&RejoinTimer, OnRejoinTimerEvent); } /****************************************************************************** * @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 : 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 : 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 : 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 : Change Lora_fsm() DeviceState * @Param : Any of eDeviceState_t type * @Return : None ******************************************************************************/ void Lora_ChangeDeviceState(eDeviceState_t newDeviceState) { //ToDo: Check for valid eDeviceState_t states DeviceState = newDeviceState; } /****************************************************************************** * @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); loraModemRetCode |= Lora_setAdr(LoraConfigParam->AdrStatus); // ToDo: 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. Execute hard reset 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) { // Inidicate Join status using LED GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); DBG_PRINTF("Nwk Joined\n"); // Start timer for Re-Authentication TimerStart(&RejoinTimer, REJOIN_TIME); // 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) { DBG_PRINTF("Uplink sent\n"); // 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; } else { DBG_PRINTF("Uplink Failed (Error: %d)\n", (uint8_t)status); } 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; } } } /****************************************************************************** * @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; TimerStart(&JoinRequestTimer, joinDelay); } /****************************************************************************** * @Brief : Function executed on JoinStatusDelayTimer Timeout event * @Param : none * @Return : none ******************************************************************************/ static void OnJoinRequestTimerEvt(void) { TimerStop(&JoinRequestTimer); DeviceState = DEVICE_JOIN; } /****************************************************************************** * @Brief : Function executed on NucleoLedTimer Timeout event * @Param : none * @Return : none ******************************************************************************/ static void OnLedTimerEvent(void) { TimerStop(&NucleoLedTimer); BSP_LED_Off(LED_GREEN); } /****************************************************************************** * @Brief : Function executed on JoinStatusDelayTimer Timeout event * @Param : none * @Return : none ******************************************************************************/ static void OnJoinStatusDelayTimerEvt(void) { TimerStop(&JoinStatusDelayTimer); DeviceState = DEVICE_JOIN_STATUS_CHECK; } /****************************************************************************** * @Brief : Function executed on SensorMeasureTimer Timeout event * @Param : none * @Return : none ******************************************************************************/ static void OnSensorMeasureTimerEvt(void) { TimerStop(&SensorMeasureTimer); DeviceState = DEVICE_SEND; } /****************************************************************************** * @Brief : Function executed on RejoinTimer Timeout event * @Param : none * @Return : none ******************************************************************************/ static void OnRejoinTimerEvent(void) { 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(); }