Fahad Mirza
/
Nucleo_HXC900
A demo application for HXC900 LoRaWAN module using Nucleo-L053R8.
Diff: hxc_client.c
- Revision:
- 0:a0c5877bd360
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hxc_client.c Thu Jul 12 00:50:48 2018 +0000 @@ -0,0 +1,581 @@ +/* + _ _ _____ _______ + | | | | |_ _| |__ __| + | |__| | __ ___ __ | | ___ | | + | __ |/ _` \ \/ / | | / _ \| | + | | | | (_| |> < _| || (_) | | + |_| |_|\__,_/_/\_\_____\___/|_| + (C)2017 HaxIoT +*/ +/******************************************************************************* + * File : hxc_client.c + * Author : Fahad (Haxiot) + * Version : V1.0.0 + * Modified: 15-February-2018 + * Brief : HXC AT command API + ****************************************************************************** + * @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 <stdbool.h> +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include "hw_usart.h" +#include "utilities.h" +#include "tiny_vsnprintf.h" +#include "hxc_client.h" +#include "hw_conf.h" +#include "delay.h" +#include "time_server.h" +#include "hw_gpio.h" + +/* External variables --------------------------------------------------------*/ +/* Private typedef -----------------------------------------------------------*/ + +// Type definition for return code analysis +typedef char* AtErrorStr_t; + +typedef struct sAtRetCode +{ + AtErrorStr_t RetCodeStr; + uint8_t SizeRetCodeStr; + eAtStatus_t RetCode; +}sAtRetCode_t; + +/* Private functions ---------------------------------------------------------*/ + +static uint16_t at_cmd_format(ATGroup_t at_group, ATCmd_t Cmd, const void *ptr); +static uint16_t at_set_cmd_format(ATCmd_t Cmd, const void *ptr); +static eAtStatus_t at_cmd_send(uint16_t len); +static eAtStatus_t at_cmd_receive(void *pdata); +static eAtStatus_t at_cmd_responseAnalysing(const char *ReturnResp); +static uint16_t at_cmd_vprintf(const char *format, ...); + +/* Private macro -------------------------------------------------------------*/ +#define AT_VPRINTF(...) at_cmd_vprintf(__VA_ARGS__) +#define AT_VSSCANF(...) tiny_sscanf(__VA_ARGS__) + +#define BAUD_RATE 9600 +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +#define DATA_RX_MAX_BUFF_SIZE 64 /* Max size of the received buffer. + * To optimize we can match with device key sizeof */ + +#define DATA_TX_MAX_BUFF_SIZE 78 // Max size of the transmit buffer +#define HXC_TIMEOUT 2000 // 2 seconds + +/* + * RetCode used to compare the return code from HXC modem. + * In direct relation with sAtRetCode_t + */ +#define RET_OK "OK" +#define RET_ERROR "AT_ERROR" +#define RET_PARAM_ERROR "AT_PARAM_ERROR" +#define RET_BUSY_ERROR "AT_BUSY_ERROR" +#define RET_PARAM_OVERFLOW "AT_PARAM_OVERFLOW" +#define RET_INVALID_MODE "AT_INVALID_MODE" +#define RET_NO_NETWORK_JOINED "AT_NO_NETWORK_JOINED" +#define RET_PAYLOAD_SIZE_ERROR "AT_PAYLOAD_SIZE_ERROR" + + +/* Private Variables --------------------------------------------------------*/ +static const sAtRetCode_t AT_RetCode[] = { +{RET_OK, sizeof(RET_OK), AT_OK}, +{RET_ERROR, sizeof(RET_ERROR), AT_ERROR}, +{RET_PARAM_ERROR, sizeof(RET_PARAM_ERROR), AT_PARAM_ERROR}, +{RET_NO_NETWORK_JOINED, sizeof(RET_NO_NETWORK_JOINED), AT_NO_NET_JOINED}, +{RET_BUSY_ERROR, sizeof(RET_BUSY_ERROR), AT_BUSY_ERROR}, +{RET_PARAM_OVERFLOW, sizeof(RET_PARAM_OVERFLOW), AT_PARAM_OVERFLOW}, +{RET_INVALID_MODE, sizeof(RET_INVALID_MODE), AT_INVALID_MODE}, +{RET_PAYLOAD_SIZE_ERROR, sizeof(RET_PAYLOAD_SIZE_ERROR), AT_PAYLOAD_SIZE_ERROR}}; + + +/* + * List of AT cmd supported by the HXC Client Module: + * HXC900 and HXC400 + */ +static const char *CmdTab[] = +{ + "", // AT + "+RESET", // Reset modem + "+FD", // Factory default + "+DEVEUI", // Device identifier + "+DEVADR", // Device address + "+APPKEY", // Application key + "+NWKSKEY", // Network session key + "+APPSKEY", // Application session key + "+APPEUI", // Application identifier + "+ADR", // Adaptive data rate + "+TXP", // Transmit power + "+DR", // Data rate + "+DCS", // DCS duty cycle settings + "+PNM", // Public/Private network + "+RX2WND", // Rx2 window frequency and datarate + "+RX1DL", // Delay of the Rx1 window + "+RX2DL", // Delay of the Rx2 window + "+JN1DL", // Join delay on Rx1 window + "+JN2DL", // Join delay on Rx2 window + "+NJM", // Network join mode + "+NWKID", // Network ID + "+FCU", // Uplink frame counter + "+FCD", // Downlink frame counter + "+CLASS", // LoRa class + "+CH", // Channel configuration + "+JOIN", // Join network server + "+NJS", // Network join mode + "+SENDB", // Send binary formatted data + "+SEND", // Send data in ASCII format + "+RECVB", // Get the last received data + "+CFS", // Confirm status + "+SNR", // Signal to noise ratio + "+RSSI", // Signal strength indicator on received radio signal + "+MODE", // Modem mode + "+RFCFG", // LoRa only configuration + "+TXCW", // Continuous Tx + "+TX", // Send LoRa only data in raw format + "+RX", // Continuous Rx + "+BAT", /// Battery level + "+VER", // Firmware version of the HXC Client +}; + + +char LoRa_AT_Cmd_Buff[DATA_TX_MAX_BUFF_SIZE];// Buffer used for AT cmd transmission + +static uint16_t Offset = 0; // Write position needed for LoRa_AT_Cmd_Buff[] during AT_SET + +/* Has to be the largest of the response + * not only for return code but also for + * return value e.g. KEY. + */ +static char response[DATA_RX_MAX_BUFF_SIZE]; + + +/* Exported functions ------------------------------------------------------- */ + +/******************************************************************** + * Brief : Configures HXC UART interface, Reset Pin + * Param : None + * Retval : AT_OK in case of success + * AT_UART_LINK_ERROR in case of UART init failure +*********************************************************************/ +eAtStatus_t Modem_Init( void ) +{ + // ToDO: hardware initialization should be out of this library + if (HW_UART_Modem_Init(BAUD_RATE) == false) + { + return AT_UART_LINK_ERROR; + } + + // Reset pin initialization + HXC_RESET_GPIO_CLK_ENABLE(); + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + HW_GPIO_Init(HXC_RESET_PORT, HXC_RESET_PIN, &GPIO_InitStruct); + + return AT_OK; +} + + +/******************************************************************** + * @brief Deinitialize modem UART interface. + * @param None + * @retval None +*********************************************************************/ +void Modem_IO_DeInit( void ) +{ + HW_UART_Modem_DeInit(); +} + + +/******************************************************************** + * @brief Handle the AT cmd following their Group type + * @param at_group AT group (control, set , get) + * Cmd AT command + * pdata pointer to the IN/OUT buffer + * @retval module status + ********************************************************************/ +eAtStatus_t Modem_AT_Cmd(ATGroup_t at_group, ATCmd_t atCmd, void *pdata ) +{ + eAtStatus_t atStatus = AT_END_ERROR; + uint16_t atCmdLen; + + // Reset At_cmd buffer for each transmission + memset1((uint8_t *)LoRa_AT_Cmd_Buff, 0x00, sizeof(LoRa_AT_Cmd_Buff)); + /* Reset the UART circular buffer for each transmission to make sure + * the responses we will get are for the current AT cmd. */ + HW_UART_ResetBuffer(); + + // Format AT cmd + atCmdLen = at_cmd_format(at_group, atCmd, pdata); + if(atCmdLen == 0) + { + /* + * You are trying to use a command behavior that HXC Client doesn't support. + * Check manual for possible AT command behaviors. + */ + return AT_CMD_ERROR; + } + + // Send AT cmd string + if(at_cmd_send(atCmdLen) != AT_OK) + { + // Problem on UART transmission + return AT_UART_LINK_ERROR; + } + + // Read response from HXC client for the AT cmd + if (at_group == AT_GET) + { + // Get the value + atStatus = at_cmd_receive(pdata); + } + else + { + // Check for the return status + atStatus = at_cmd_receive(NULL); + } + + return atStatus; +} + + +/******************************************************************** + * Brief : format the cmd in order to be send + * Param : at_group - the behavior of AT cmd + * Cmd - AT command + * ptr - generic pointer to the IN/OUT buffer + * Retval: length of the formated frame to be send + ********************************************************************/ +static uint16_t at_cmd_format(ATGroup_t at_group, ATCmd_t Cmd, const void *ptr) +{ + uint16_t len = 0; /*length of the formated command*/ + + switch (at_group) + { + case AT_CTRL: + { + len = AT_VPRINTF("AT%s\r\n", CmdTab[Cmd]); + break; + } + case AT_GET: + { + len = AT_VPRINTF("AT%s=?\r\n", CmdTab[Cmd]); + break; + } + case AT_SET: + { + len = at_set_cmd_format(Cmd, ptr); + break; + } + default: + { + break; + } + } + + return len; +} + + +/******************************************************************** + * Brief : format the at set cmd + * Param : Cmd - AT command + * ptr - generic pointer to the IN/OUT buffer + * Retval: length of the formated frame to be send + ********************************************************************/ +static uint16_t at_set_cmd_format(ATCmd_t Cmd, const void *ptr) +{ + uint32_t value; /*for 32_02X and 32_D*/ + + + Offset = AT_VPRINTF("AT%s=", CmdTab[Cmd]); + switch (Cmd) + { + case AT_SEND: + { + sSendDataString_t *SendData = (sSendDataString_t *)ptr; + Offset += AT_VPRINTF("%d,%d:%s", SendData->Ack, SendData->Port, SendData->Buffer); + break; + } + case AT_SENDB: + { + sSendDataBinary_t *SendData = (sSendDataBinary_t *)ptr; + + Offset += AT_VPRINTF("%d,%d:", SendData->Ack, SendData->Port); + unsigned i; + for (i = 0; i < SendData->DataSize; i++) + { + Offset+=AT_VPRINTF("%02x", SendData->Buffer[i]); + } + + break; + } + case AT_APPKEY: + case AT_NWKSKEY: + case AT_APPSKEY: + { + char *key = (char*) ptr; + Offset += AT_VPRINTF("%s", key); + break; + } + case AT_DEVADR: + { + value = *(uint32_t*)ptr; + Offset += AT_VPRINTF("%02x%02x%02x%02x", (unsigned)((unsigned char *)(&value))[3], + (unsigned)((unsigned char *)(&value))[2], + (unsigned)((unsigned char *)(&value))[1], + (unsigned)((unsigned char *)(&value))[0]); + break; + } + case AT_APPEUI: + case AT_DEVEUI: + { + char *eui = (char*)ptr; + Offset += AT_VPRINTF("%s", eui); + break; + } + case AT_RX1DL: /* could be unsigned format - need to be analyze */ + case AT_RX2DL: + case AT_JN1DL: + case AT_JN2DL: + case AT_FCU: + case AT_FCD: + { + value = *(uint32_t*)ptr; + Offset += AT_VPRINTF("%u", value); + break; + } + case AT_DR: + case AT_TXP: + case AT_PNM: + case AT_DCS: + case AT_ADR: + case AT_BAT: + { + uint8_t value_8 = *(uint8_t*)ptr; + Offset += AT_VPRINTF("%d", value_8); + break; + } + case AT_CLASS: + { + char value_c = *(char*)ptr; + Offset += AT_VPRINTF("%c", value_c); + break; + } + case AT_NJM: + { + Offset += AT_VPRINTF("%s", (char*)ptr); + break; + } + default: + { + //DBG_PRINTF ("Not supported\r\n"); + break; + } + } + + Offset += AT_VPRINTF("\r\n"); + uint16_t len = Offset; + Offset = 0; + + return len; +} + + +/******************************************************************** + * Brief : This function sends an AT cmd to the slave device + * Param : len - length of the AT cmd to be sent + * Retval: eAtStatus_t return code +*********************************************************************/ +static eAtStatus_t at_cmd_send(uint16_t len) +{ + /*transmit the command from master to slave*/ + if( HW_UART_Modem_SendBytes(LoRa_AT_Cmd_Buff, len) == false) + { + return AT_UART_LINK_ERROR; + } + + return AT_OK; +} + + +/******************************************************************** + * Brief : This function receives response from the slave device + * Param : pdata - pointer to the value returned by the slave + * Retval: return code coming from slave +*********************************************************************/ +static eAtStatus_t at_cmd_receive(void *pdata) +{ + bool ResponseComplete = false; + uint8_t i = 0; + eAtStatus_t RetCode = AT_END_ERROR; + + // Cleanup the response buffer + memset1((uint8_t *)response, 0x00, sizeof(response)); + + uint32_t currentTime = TimerGetCurrentTime(); + while(ResponseComplete != true) + { + if(HW_UART_Modem_IsNewCharReceived() == false) + { + if(TimerGetElapsedTime(currentTime) > HXC_TIMEOUT) + { + ResponseComplete = true; + RetCode = AT_TIMEOUT; + } + } + else + { + // Process the response + response[i++] = HW_UART_Modem_GetNewChar(); + + // Wait up to line feed marker + if (response[i - 1] == '\n') // ToDo: potential infinite loop, need a timeout. + { + // Last two bytes are <CR><LF>, set CR as NULL byte. + response[i - 2] = '\0'; + // Reset index + i = 0; + + // Compare response with AT statuses + RetCode = at_cmd_responseAnalysing(response); + if(RetCode != AT_END_ERROR) + { + // We received a status. Response complete. + ResponseComplete = true; + } + else if(pdata != NULL) + { + // If pdata isn't null that means we are using GET cmd to get + // return value. Copy the return value into pdata. + + strcpy(pdata, response); + memset1((uint8_t *)response, 0x00, 16); + // Now, let's get the status + } + } + else + { + if (i == (DATA_RX_MAX_BUFF_SIZE-1)) + { + // Frame overflow. Reset index and stop reading. + i = 0; + RetCode = AT_PARAM_OVERFLOW; + ResponseComplete = true; + } + } + } + } // End while(ResponseComplete != true) + + HW_UART_Modem_Ready(); /* to be checked since was validated with previous */ + return RetCode; /* version of HAL .. there was not Rx field state */ +} + + +/******************************************************************** + * Brief : Analyze the response received by the device + * Param : response: pointer to the received response + * Retval: eAtStatus_t error type +*********************************************************************/ +static eAtStatus_t at_cmd_responseAnalysing(const char *ReturnResp) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(AT_RetCode); i++) + { + if (strncmp(ReturnResp, AT_RetCode[i].RetCodeStr, (AT_RetCode[i].SizeRetCodeStr-1)) == 0) + { + /* Command has been found */ + return AT_RetCode[i].RetCode; + } + } + return AT_END_ERROR; +} + + +/******************************************************************** + * Brief : format the AT frame to be sent to the modem (slave) + * Param : pointer to the format string + * Retval: len of the string to be sent +*********************************************************************/ +uint16_t at_cmd_vprintf(const char *format, ...) +{ + va_list args; + uint16_t len; + + va_start(args, format); + + len = tiny_vsnprintf_like(LoRa_AT_Cmd_Buff+Offset, sizeof(LoRa_AT_Cmd_Buff)-Offset, format, args); + + va_end(args); + + return len; +} + +/******************************************************************** + * @brief Reset the HXC client modem using HXC RESET pin + * @param None + * @retval module status + ********************************************************************/ +eAtStatus_t Modem_HardReset(void) +{ + HW_GPIO_Write(HXC_RESET_PORT, HXC_RESET_PIN, GPIO_PIN_RESET); + DelayMs(200); + HW_GPIO_Write(HXC_RESET_PORT, HXC_RESET_PIN, GPIO_PIN_SET); + + // Check for the return status - OK + return at_cmd_receive(NULL); +} + +/******************************************************************** + * @brief Check if any downlink packet is received + * @param None + * @retval TRUE or FALSE + ********************************************************************/ +bool Modem_IsNewDataReceived(void) +{ + if(HW_UART_Modem_IsNewCharReceived() == false) + { + return false; + } + // Cleanup the response buffer + memset1((uint8_t *)response, 0x00, sizeof(response)); + HW_UART_Modem_GetCharactersUntilNewLine(response, sizeof(response), HXC_TIMEOUT); + if(strncmp("rxdata", response, 6) == 0) + { + return true; + } + return false; +} +/************************ (C) COPYRIGHT Haxiot *****END OF FILE*****/ +