Fahad Mirza
/
Nucleo_HXC900
A demo application for HXC900 LoRaWAN module using Nucleo-L053R8.
LoRa/hxc_client.c
- Committer:
- fahadmirza
- Date:
- 2018-10-09
- Revision:
- 21:f1d561ed31a1
- Parent:
- 18:24c232951ff8
- Child:
- 32:2d0678039a09
File content as of revision 21:f1d561ed31a1:
/* _ _ _____ _______ | | | | |_ _| |__ __| | |__| | __ ___ __ | | ___ | | | __ |/ _` \ \/ / | | / _ \| | | | | | (_| |> < _| || (_) | | |_| |_|\__,_/_/\_\_____\___/|_| (C)2017 HaxIoT */ /******************************************************************************* * File : hxc_client.c * Author : Fahad (Haxiot) * Version : V1.0.0 * Modified: 15-July-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" /* 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. APPKEY. */ static char response[DATA_RX_MAX_BUFF_SIZE]; /* Exported functions ------------------------------------------------------- */ /******************************************************************************* * @Brief : Configures HXC UART interface, Reset Pin * @Param : None * @Return : AT_OK in case of success * AT_UART_LINK_ERROR in case of UART init failure *******************************************************************************/ eAtStatus_t Modem_Init( void ) { 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 * @Return : 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 * @Return : 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 * @Return: 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 * @Return: 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: case AT_DEVADR: case AT_APPEUI: case AT_DEVEUI: { char *key = (char*) ptr; Offset += AT_VPRINTF("%s", key); break; } case AT_RX1DL: 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 * @Return: 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 * @Return: Return code coming from HXC 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') { // 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(); return RetCode; } /******************************************************************************* * @Brief : Analyze the response received by the device * @Param : response: pointer to the received response * @Return: 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 * @Return: Length 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 * @Return : 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 * @Return: 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*****/