A demo application for HXC900 LoRaWAN module using Nucleo-L053R8.

Dependencies:   mbed

Revision:
0:a0c5877bd360
diff -r 000000000000 -r a0c5877bd360 hxc_client.c
--- /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>&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 <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*****/
+