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

Dependencies:   mbed

LoRa/hxc_client.c

Committer:
fahadmirza
Date:
2019-01-24
Revision:
39:cb0e5a76ab15
Parent:
37:5488257a4876

File content as of revision 39:cb0e5a76ab15:

/**
  ******************************************************************************
  * @file     hxc_client.c
  * @author   Fahad (Haxiot)
  * @version  V1.0.0
  * @date     15-July-2018
  * @brief    This file provides set of firmware functions to communicate
  * 		  with HXC Client using AT commands:
  * 		   - AT_SET
  * 		   - AT_GET
  * 		   - AT_RUN
  ******************************************************************************
  * @attention
  *
  *  _    _            _____   _______
  * | |  | |          |_   _| |__   __|
  * | |__| | __ ___  __ | |  ___ | |
  * |  __  |/ _` \ \/ / | | / _ \| |
  * | |  | | (_| |>  < _| || (_) | |
  * |_|  |_|\__,_/_/\_\_____\___/|_|
  *  (C)2017 HaxIoT
  *
  *
  * <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"


/* Private macro -------------------------------------------------------------*/
#define AT_VPRINTF(...)         at_cmd_vprintf(__VA_ARGS__)
#define AT_VSSCANF(...)         tiny_sscanf(__VA_ARGS__)

#define BAUD_RATE               9600U
#define ARRAY_SIZE(a)           (sizeof(a) / sizeof(a[0]))

#define AT_RESPONSE_BUFF_SIZE   64U    // Max size of the received buffer.
#define DATA_TX_MAX_BUFF_SIZE   78U    // Max size of the transmit buffer
#define HXC_TIMEOUT             2000U  // 2 seconds


// These strings will be used to compare the responses return from HXC modem.
// In direct relation with sAtRetCode_t
#define OK                 "OK"
#define ERROR              "AT_ERROR"
#define PARAM_ERROR        "AT_PARAM_ERROR"
#define BUSY_ERROR         "AT_BUSY_ERROR"
#define PARAM_OVERFLOW     "AT_PARAM_OVERFLOW"
#define INVALID_MODE       "AT_INVALID_MODE"
#define NO_NETWORK_JOINED  "AT_NO_NETWORK_JOINED"
#define PAYLOAD_SIZE_ERROR "AT_PAYLOAD_SIZE_ERROR"


/* Private typedef -----------------------------------------------------------*/
// Type definition for return code analysis
typedef struct sAtRetCode
{
    char* 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_analyzeResponse(const char *ReturnResp);
static uint16_t at_cmd_vprintf(const char *format, ...);


/* Private Variables --------------------------------------------------------*/
// NOTE: sizeof of a string take account of the NULL character too, unlike
//       strlen(). Hence subtract one from the sizeof.
static const sAtRetCode_t AT_RetCode[] = {
{OK,                 sizeof(OK) - 1,                 AT_OK},
{ERROR,              sizeof(ERROR) - 1,              AT_ERROR},
{PARAM_ERROR,        sizeof(PARAM_ERROR) - 1,        AT_PARAM_ERROR},
{NO_NETWORK_JOINED,  sizeof(NO_NETWORK_JOINED) - 1,  AT_NO_NET_JOINED},
{BUSY_ERROR,         sizeof(BUSY_ERROR) - 1,         AT_BUSY_ERROR},
{PARAM_OVERFLOW,     sizeof(PARAM_OVERFLOW) - 1,     AT_PARAM_OVERFLOW},
{INVALID_MODE,       sizeof(INVALID_MODE) - 1,       AT_INVALID_MODE},
{PAYLOAD_SIZE_ERROR, sizeof(PAYLOAD_SIZE_ERROR) - 1, 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
};


static char AtCmdBuff[DATA_TX_MAX_BUFF_SIZE];
// Write position needed for AtCmdBuff[] during AT_SET
static uint16_t Offset = 0;

// Has to be the largest of the response e.g. APPKEY.
static char AtResponseBuff[AT_RESPONSE_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 *)AtCmdBuff, 0x00, DATA_TX_MAX_BUFF_SIZE);
	
	// 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)
	{
	    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(AtCmdBuff, 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 *)AtResponseBuff, 0x00, AT_RESPONSE_BUFF_SIZE);

	uint32_t currentTime = TimerGetCurrentTime();
    while(ResponseComplete != true)
    {
    	if(HW_UART_Modem_IsNewCharReceived() == false)
        {
    		if(TimerGetElapsedTime(currentTime) > HXC_TIMEOUT)
    		{
    			ResponseComplete = true;
    			RetCode = AT_TIMEOUT;
    		}
        }
    	else
    	{
    	    AtResponseBuff[i++] = HW_UART_Modem_GetNewChar();
   
            // Wait up to line feed marker
            if (AtResponseBuff[i - 1] == '\n')
            {
            	// Last two bytes are <CR><LF>, set CR as NULL byte.
                AtResponseBuff[i - 2] = '\0';
    	        i = 0;

		        RetCode = at_cmd_analyzeResponse(AtResponseBuff);
		        if(RetCode != AT_END_ERROR)
		        {
			        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, AtResponseBuff);
			        memset1((uint8_t *)AtResponseBuff, 0x00, AT_RESPONSE_BUFF_SIZE);
			        // Now, let's get the status
		        }
            }
            else
            {
                if (i == (AT_RESPONSE_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_analyzeResponse(const char *ReturnResp)
{
	uint8_t i;

    for (i = 0; i < ARRAY_SIZE(AT_RetCode); i++)
    {   
		if (strncmp(ReturnResp, AT_RetCode[i].RetCodeStr, (AT_RetCode[i].SizeRetCodeStr)) == 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
*******************************************************************************/
static uint16_t at_cmd_vprintf(const char *format, ...)
{
	va_list args;
    uint16_t len;
   
    va_start(args, format);
  
    len = tiny_vsnprintf_like(AtCmdBuff + Offset, DATA_TX_MAX_BUFF_SIZE - 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 *)AtResponseBuff, 0x00, AT_RESPONSE_BUFF_SIZE);

	HW_UART_Modem_GetCharactersUntilNewLine(AtResponseBuff, AT_RESPONSE_BUFF_SIZE, HXC_TIMEOUT);

	if(strncmp("rxdata", AtResponseBuff, 6) == 0)
	{
		return true;
	}
	return false;
}

char* Modem_GetResponseBuffer(void)
{
	return AtResponseBuff;
}
/************************ (C) COPYRIGHT Haxiot *****END OF FILE*****/