1376 lines
37 KiB
C
1376 lines
37 KiB
C
/**
|
|
******************************************************************************
|
|
* @file EG91.c
|
|
* @author MCD Application Team
|
|
* @version V0.0.1
|
|
* @date 11-08-2017
|
|
* @brief Functions to manage the EG91 module (C2C cellular modem).
|
|
******************************************************************************
|
|
* @attention
|
|
*
|
|
* <h2><center>© Copyright (c) 2017 STMicroelectronics International N.V.
|
|
* All rights reserved.</center></h2>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted, provided that the following conditions are met:
|
|
*
|
|
* 1. Redistribution 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 STMicroelectronics nor the names of other
|
|
* contributors to this software may be used to endorse or promote products
|
|
* derived from this software without specific written permission.
|
|
* 4. This software, including modifications and/or derivative works of this
|
|
* software, must execute solely and exclusively on microcontroller or
|
|
* microprocessor devices manufactured by or for STMicroelectronics.
|
|
* 5. Redistribution and use of this software other than as permitted under
|
|
* this license is void and will automatically terminate your rights under
|
|
* this license.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
|
|
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
|
|
* SHALL STMICROELECTRONICS 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 "eg91.h"
|
|
#include "main.h"
|
|
|
|
#define CHARISHEXNUM(x) (((x) >= '0' && (x) <= '9') || \
|
|
((x) >= 'a' && (x) <= 'f') || \
|
|
((x) >= 'A' && (x) <= 'F'))
|
|
|
|
#define CHARISNUM(x) ((x) >= '0' && (x) <= '9')
|
|
#define CHAR2NUM(x) ((x) - '0')
|
|
|
|
#define CTRL_Z 26
|
|
|
|
/* Private variable ---------------------------------------------------------*/
|
|
char CmdString[EG91_CMD_SIZE];
|
|
|
|
/* Exported variable ---------------------------------------------------------*/
|
|
|
|
/* This should be reworked, _IO should not depend on component */
|
|
const EG91_RetKeywords_t ReturnKeywords[] =
|
|
{
|
|
/* send receive related keywords */
|
|
{ RET_SENT, "OK\r\n" },
|
|
{ RET_ARROW, ">" },
|
|
{ RET_READ, "CIPRXGET" },
|
|
{ RET_SEND, "CIPSEND" },
|
|
{ RET_POWERED_DOWN, "POWERED DOWN\r\n" },
|
|
{ RET_UART_READY, "RDY\r\n"},
|
|
{ RET_URC_CLOSED, "closed\"" },
|
|
{ RET_URC_RECV, "CIPRXGET:SUCCESS" },
|
|
{ RET_URC_IN_FULL, "incoming full\"" },
|
|
{ RET_URC_INCOM, "incoming\"" },
|
|
{ RET_URC_PDPDEACT, "pdpdeact\"" },
|
|
{ RET_URC_DNS, "+MDNSGIP:" },
|
|
/* errors keywords */
|
|
{ RET_ERROR, "ERROR\r\n" },
|
|
{ RET_CME_ERROR, "+CME ERROR: " },
|
|
//{ RET_CMS_ERROR, "CMS ERROR:" },
|
|
{ RET_BUF_FULL, "ERROR\r\n" },
|
|
/* set-up keywords */
|
|
{ RET_OK, "OK\r\n" },
|
|
{ RET_OPEN, "OPEN:" },
|
|
{ RET_SIM_READY, "ready\r\n" },
|
|
{ RET_RESP, "\r\n\r\nOK" },
|
|
{ RET_NETCLOSE, "\r\n\r\n" },
|
|
{ RET_PING, "+QPING: " },
|
|
{ RET_RDY, "RDY" },
|
|
{ RET_CRLF, "\r\n" }, /* keep RET_CRLF last !!! */
|
|
};
|
|
|
|
/* Private functions ---------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Parses and returns number from string.
|
|
* @param ptr: pointer to string
|
|
* @param cnt: pointer to the number of parsed digit
|
|
* @retval integer value.
|
|
*/
|
|
static int32_t ParseNumber(char *ptr, uint8_t *cnt)
|
|
{
|
|
uint8_t minus = 0, i = 0;
|
|
int32_t sum = 0;
|
|
|
|
if (*ptr == '-')
|
|
{ /* Check for minus character */
|
|
minus = 1;
|
|
ptr++;
|
|
i++;
|
|
}
|
|
while (CHARISNUM(*ptr))
|
|
{ /* Parse number */
|
|
sum = 10 * sum + CHAR2NUM(*ptr);
|
|
ptr++;
|
|
i++;
|
|
}
|
|
if (cnt != NULL)
|
|
{ /* Save number of characters used for number */
|
|
*cnt = i;
|
|
}
|
|
if (minus)
|
|
{ /* Minus detected */
|
|
return 0 - sum;
|
|
}
|
|
return sum; /* Return number */
|
|
}
|
|
|
|
/**
|
|
* @brief Parses and returns QIRQ query response.
|
|
* @param ptr: pointer to string
|
|
* @param arr: pointer to IP array
|
|
* @retval None.
|
|
*/
|
|
static void ParseQIRD(char *ptr, uint16_t *arr)
|
|
{
|
|
uint8_t hexnum = 0, hexcnt;
|
|
|
|
while (*ptr)
|
|
{
|
|
hexcnt = 1;
|
|
if (*ptr != ',')
|
|
{
|
|
arr[hexnum++] = (uint16_t) ParseNumber(ptr, &hexcnt);
|
|
}
|
|
ptr = ptr + hexcnt;
|
|
if ((*ptr == '\r') || (hexnum == 3))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Parses and returns .
|
|
* @param ptr: pointer to string
|
|
* @retval The number of unread data in modem TX buffer.
|
|
*/
|
|
//static void ParseQISEND(char *ptr, uint32_t *unackedbytes)
|
|
//{
|
|
// uint8_t hexnum = 0, hexcnt;
|
|
// uint32_t temp = 0;
|
|
//
|
|
// /* QISEND Response contains 3 kind of information
|
|
// * <total_send_length>,<ackedbytes>,<unackedbytes>
|
|
// * we're only interested to the 3rd one, unackedbytes for now */
|
|
// while (*ptr)
|
|
// {
|
|
// hexcnt = 1;
|
|
// if (*ptr != ',')
|
|
// {
|
|
// temp = (uint32_t) ParseNumber(ptr, &hexcnt);
|
|
// /* Count up the number we've retrieved */
|
|
// hexnum++;
|
|
// if (hexnum == 3)
|
|
// {
|
|
// *unackedbytes = temp;
|
|
// }
|
|
// }
|
|
// ptr = ptr + hexcnt;
|
|
// if ((*ptr == '\r') || (hexnum == 3))
|
|
// {
|
|
// return;
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
/**
|
|
* @brief Parses and returns IP address.
|
|
* @param ptr: pointer to string
|
|
* @param arr: pointer to IP array
|
|
* @retval None.
|
|
*/
|
|
static void ParseIP(char *ptr, uint8_t *arr)
|
|
{
|
|
uint8_t hexnum = 0, hexcnt;
|
|
|
|
while (*ptr)
|
|
{
|
|
hexcnt = 1;
|
|
if (*ptr != '.')
|
|
{
|
|
arr[hexnum++] = ParseNumber(ptr, &hexcnt);
|
|
}
|
|
ptr = ptr + hexcnt;
|
|
if (*ptr == '"')
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Return the integer difference between 'init + timeout' and 'now'.
|
|
* The implementation is robust to uint32_t overflows.
|
|
* @param In: init Reference index.
|
|
* @param In: now Current index.
|
|
* @param In: timeout Target index.
|
|
* @retval Number of ms from now to target (init + timeout).
|
|
*/
|
|
static int32_t TimeLeftFromExpiration(uint32_t init, uint32_t now,
|
|
uint32_t timeout)
|
|
{
|
|
int32_t ret = 0;
|
|
uint32_t wrap_end = 0;
|
|
|
|
if (now < init)
|
|
{ /* Timer wrap-around detected */
|
|
wrap_end = UINT32_MAX - init;
|
|
}
|
|
ret = wrap_end - (now - init) + timeout;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieve Data from the C2C module over the UART interface.
|
|
* This function receives data from the C2C module, the
|
|
* data is fetched from a ring buffer that is asynchronously and continuously
|
|
filled with the received data.
|
|
* @param Obj: pointer to module handle
|
|
* @param pData: a buffer inside which the data will be read.
|
|
* @param Length: Size of the data to receive.
|
|
* @param ScanVals: when param Length = 0 : values to be retrieved in the coming data in order to exit.
|
|
* @retval int32_t: if param Length = 0 : the actual RET_CODE found,
|
|
if param Length > 0 : the actual data size that has been received
|
|
if error (timeout): return -1 (EG91_RETURN_RETRIEVE_ERROR).
|
|
*/
|
|
static int32_t AT_RetrieveData(EG91Object_t *Obj, uint8_t *pData,
|
|
uint16_t Length, uint32_t ScanVals, uint32_t Timeout)
|
|
{
|
|
uint32_t tickstart = Obj->GetTickCb();
|
|
int16_t ReadData = 0;
|
|
uint16_t x;
|
|
uint16_t index[NUM_RESPONSES];
|
|
uint16_t lens[NUM_RESPONSES];
|
|
uint16_t pos;
|
|
uint8_t c;
|
|
int32_t min_requested_time;
|
|
|
|
min_requested_time = 2 * (Length + 15) * 8 * 1000 / EG91_DEFAULT_BAUDRATE; /* 15 is the max length of the return keyword */
|
|
if (Timeout < min_requested_time) /* UART speed 115200 bits per sec */
|
|
{
|
|
Timeout = min_requested_time;
|
|
}
|
|
|
|
for (x = 0; x < NUM_RESPONSES; x++)
|
|
{
|
|
index[x] = 0;
|
|
lens[x] = strlen(ReturnKeywords[x].retStr);
|
|
}
|
|
|
|
if ((Length == 0) && (ScanVals == RET_NONE))
|
|
{
|
|
return 0; /* to avoid waiting a RET_VAL in case the parsed_lenth of payload is zero */
|
|
/* but no code needs to be retrieved */
|
|
}
|
|
|
|
memset(Obj->CmdResp, 0, EG91_CMD_SIZE);
|
|
|
|
while (TimeLeftFromExpiration(tickstart, Obj->GetTickCb(), Timeout) > 0)
|
|
{
|
|
if (Obj->fops.IO_ReceiveOne(&c) == 0) /* Receive one sample from UART */
|
|
{
|
|
/* serial data available, so return data to user */
|
|
pData[ReadData++] = c;
|
|
|
|
if (Length == 0)
|
|
{
|
|
/* Check whether we hit an ESP return values */
|
|
for (x = 0; x < NUM_RESPONSES; x++)
|
|
{
|
|
if (c != ReturnKeywords[x].retStr[index[x]])
|
|
{
|
|
index[x] = 0;
|
|
}
|
|
|
|
if (c == ReturnKeywords[x].retStr[index[x]])
|
|
{
|
|
pos = ++(index[x]);
|
|
if (pos >= lens[x])
|
|
{
|
|
if (ScanVals & ReturnKeywords[x].retVal)
|
|
{
|
|
return ReturnKeywords[x].retVal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else /* end (Length > 0) */
|
|
{
|
|
if (ReadData < Length)
|
|
{
|
|
/* nothing to do except keep reading in the while loop */
|
|
}
|
|
else /* ReadData >= Length */
|
|
{
|
|
return ReadData;
|
|
}
|
|
} /* end (Length == 0) */
|
|
}
|
|
}
|
|
|
|
if ((Length > 0) && (ReadData > 0))
|
|
{
|
|
return ReadData;
|
|
}
|
|
return EG91_RETURN_RETRIEVE_ERROR;
|
|
}
|
|
|
|
/**
|
|
* @brief Execute AT command.
|
|
* @param Obj: pointer to module handle
|
|
* @param cmd: pointer to command string
|
|
* @param resp: expected response
|
|
* @retval Operation Status: OK or ERROR.
|
|
*/
|
|
static int32_t AT_ExecuteCommand(EG91Object_t *Obj, uint32_t timeout,
|
|
uint8_t *cmd, uint32_t resp)
|
|
{
|
|
int32_t ret = EG91_RETURN_SEND_ERROR;
|
|
|
|
if (timeout == 0)
|
|
{
|
|
timeout = EG91_TOUT_300;
|
|
}
|
|
#ifdef EG91_DBG_AT //alecks
|
|
printf("AT Request: %s \n", cmd);
|
|
#endif
|
|
if (Obj->fops.IO_Send(cmd, strlen((char*) cmd)) >= 0)
|
|
{
|
|
HAL_Delay(200); //return to 200
|
|
ret = (AT_RetrieveData(Obj, Obj->CmdResp, 0, resp, timeout));
|
|
if (ret < 0)
|
|
{
|
|
#ifdef EG91_DBG
|
|
printf("EG91 AT_ExecuteCommand() rcv TIMEOUT ret=%ld: %s \r\n", ret, cmd);
|
|
#endif
|
|
}
|
|
#ifdef EG91_DBG_AT
|
|
else
|
|
{
|
|
printf("AT Response: %s \n", Obj->CmdResp);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef EG91_DBG
|
|
printf("EG91 AT_ExecuteCommand() send ERROR: %s \r\n", cmd);
|
|
#endif
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Execute AT command with data.
|
|
* @param Obj: pointer to module handle
|
|
* @param pdata: pointer to returned data
|
|
* @param len: binary data length
|
|
* @param timeout: waiting that the modem confirms confirm that send command has been executed (SEND OK)
|
|
* @retval Operation Status.
|
|
*/
|
|
static EG91_SendRet_t AT_RequestSendData(EG91Object_t *Obj, uint8_t *pdata,
|
|
uint16_t len, uint32_t timeout)
|
|
{
|
|
int32_t confirm;
|
|
|
|
if (Obj->fops.IO_Send(pdata, len) >= 0)
|
|
{
|
|
confirm = AT_RetrieveData(Obj, Obj->CmdResp, 0,
|
|
(RET_SENT | RET_BUF_FULL | RET_ERROR), timeout);
|
|
return (EG91_SendRet_t) confirm;
|
|
}
|
|
return EG91_SEND_RET_UART_FAIL; /* UART error to transmit */
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieve URC header and decode it
|
|
* @param Obj: pointer to module handle
|
|
* @param Timeout : ms
|
|
* @param ParseLength : pointer to return the length parsed from URC info
|
|
* @param AccMode : currently only EG91_BUFFER_MODE is used and tested
|
|
* @retval Operation Status.
|
|
*/
|
|
static int32_t AT_RetrieveUrc(EG91Object_t *Obj, uint32_t Timeout,
|
|
uint16_t *ParseLength, EG91_AccessMode_t AccMode)
|
|
{
|
|
int32_t check_resp, ret;
|
|
uint32_t expected_resp;
|
|
uint8_t parse_ret;
|
|
uint8_t parse_count, ip_count;
|
|
uint32_t count = 0;
|
|
|
|
expected_resp = RET_URC_CLOSED | RET_URC_RECV | RET_URC_IN_FULL
|
|
| RET_URC_INCOM | RET_URC_PDPDEACT | RET_URC_DNS;
|
|
check_resp = AT_RetrieveData(Obj, Obj->CmdResp, 0, expected_resp, Timeout);
|
|
switch (check_resp)
|
|
{
|
|
case RET_URC_CLOSED:
|
|
AT_RetrieveData(Obj, Obj->CmdResp, 0, RET_CRLF, EG91_TOUT_SHORT);
|
|
parse_ret = ParseNumber((char*) Obj->CmdResp + 1, &parse_count);
|
|
break;
|
|
case RET_URC_RECV:
|
|
/* retrieve contextID */
|
|
ret = AT_RetrieveData(Obj, Obj->CmdResp, 3, RET_NONE, EG91_TOUT_SHORT);
|
|
if (ret > 0)
|
|
{
|
|
parse_ret = ParseNumber((char*) Obj->CmdResp + 1, &parse_count);
|
|
if (parse_count == 2)
|
|
{ /* read next comma */
|
|
AT_RetrieveData(Obj, Obj->CmdResp, 1, RET_NONE, EG91_TOUT_SHORT);
|
|
}
|
|
}
|
|
if (AccMode == EG91_BUFFER_MODE)
|
|
{
|
|
AT_RetrieveData(Obj, Obj->CmdResp, 1, RET_NONE, EG91_TOUT_SHORT);
|
|
}
|
|
else
|
|
{
|
|
for (; count < 6; count++)
|
|
{
|
|
if (ret < 0)
|
|
{
|
|
break;
|
|
}
|
|
ret = AT_RetrieveData(Obj, &Obj->CmdResp[count], 1, RET_NONE,EG91_TOUT_SHORT);
|
|
if (ret == 1)
|
|
{
|
|
if (Obj->CmdResp[count] == '\n')
|
|
{
|
|
*ParseLength = (uint16_t) ParseNumber((char*) Obj->CmdResp, &parse_count);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case RET_URC_IN_FULL:
|
|
/* nothing to be done */
|
|
break;
|
|
|
|
case RET_URC_INCOM:
|
|
/* TBD: to be implemented for SERVER MODE*/
|
|
break;
|
|
|
|
case RET_URC_PDPDEACT:
|
|
AT_RetrieveData(Obj, Obj->CmdResp, 0, RET_CRLF, EG91_TOUT_SHORT);
|
|
parse_ret = ParseNumber((char*) Obj->CmdResp + 1, &parse_count);
|
|
break;
|
|
|
|
case RET_URC_DNS:
|
|
AT_RetrieveData(Obj, Obj->CmdResp, 0, RET_CRLF, Timeout);
|
|
parse_ret = ParseNumber((char*) Obj->CmdResp + 1, &parse_count);
|
|
if (parse_ret == 0) /* means no errors */
|
|
{
|
|
ip_count = ParseNumber((char*) Obj->CmdResp + 3, &parse_count);
|
|
for (count = 0; count < ip_count; count++)
|
|
{
|
|
expected_resp = RET_URC_DNS;
|
|
check_resp = AT_RetrieveData(Obj, Obj->CmdResp, 0,
|
|
expected_resp, Timeout);
|
|
AT_RetrieveData(Obj, Obj->CmdResp, 0, RET_CRLF, Timeout);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
check_resp = -1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
check_resp = -1;
|
|
break;
|
|
}
|
|
|
|
return check_resp;
|
|
}
|
|
|
|
/**
|
|
* @brief Synchronize the modem uart with the STM32 uart (autobauding)
|
|
* @param Obj: pointer to module handle
|
|
* @retval Operation status.
|
|
*/
|
|
static int32_t AT_Synchro(EG91Object_t *Obj)
|
|
{
|
|
int32_t ret = RET_ERROR;
|
|
int8_t atSync = 0;
|
|
uint32_t tickstart;
|
|
|
|
/* Init tickstart for timeout management */
|
|
tickstart = Obj->GetTickCb();
|
|
|
|
/* Start AT SYNC: Send AT every 500ms,
|
|
if receive OK, SYNC success,
|
|
if no OK return after sending AT 10 times, SYNC fail */
|
|
do
|
|
{
|
|
if (TimeLeftFromExpiration(tickstart, Obj->GetTickCb(), EG91_TOUT_ATSYNC) < 0)
|
|
{
|
|
ret = AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT\r\n", RET_OK | RET_ERROR);
|
|
atSync++;
|
|
tickstart = Obj->GetTickCb();
|
|
}
|
|
} while ((atSync < 10) && (ret != RET_OK));
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------*/
|
|
/* --- Public functions -----------------------------------------------------*/
|
|
/* --------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Register EG91 BusIO external functions.
|
|
* @param Obj: pointer to module handle
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_RegisterBusIO(EG91Object_t *Obj, IO_Init_Func IO_Init,
|
|
IO_DeInit_Func IO_DeInit, IO_Baudrate_Func IO_Baudrate,
|
|
IO_Send_Func IO_Send, IO_ReceiveOne_Func IO_ReceiveOne,
|
|
IO_Flush_Func IO_Flush)
|
|
{
|
|
if (!Obj || !IO_Init || !IO_DeInit || !IO_Baudrate || !IO_Send
|
|
|| !IO_ReceiveOne || !IO_Flush)
|
|
{
|
|
return EG91_RETURN_ERROR;
|
|
}
|
|
|
|
Obj->fops.IO_Init = IO_Init;
|
|
Obj->fops.IO_DeInit = IO_DeInit;
|
|
Obj->fops.IO_Baudrate = IO_Baudrate;
|
|
Obj->fops.IO_Send = IO_Send;
|
|
Obj->fops.IO_ReceiveOne = IO_ReceiveOne;
|
|
Obj->fops.IO_FlushBuffer = IO_Flush;
|
|
|
|
return EG91_RETURN_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Shut down the module.
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_PowerDown(EG91Object_t *Obj)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
|
|
if ( RET_OK == (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+POWEROFF\r\n",
|
|
RET_OK | RET_ERROR | RET_CME_ERROR))
|
|
{
|
|
// printf("Please wait, disconnecting and saving data. It may last until 60 s\n");
|
|
/* expect for the "POWERED DOWN" */
|
|
/* The maximum time for network log-off is 60 seconds */
|
|
if (AT_RetrieveData(Obj, Obj->CmdResp, 0, RET_POWERED_DOWN, EG91_TOUT_60000) > 0)
|
|
{
|
|
printf("Modem is entered in power down\n");
|
|
ret = EG91_RETURN_OK;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize EG91 module.
|
|
* @param Obj: pointer to module handle
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_InitRet_t EG91_Init(EG91Object_t *Obj)
|
|
{
|
|
EG91_InitRet_t fret = EG91_INIT_OTHER_ERR;
|
|
int32_t ret = RET_ERROR;
|
|
int8_t i;
|
|
char *align_ptr, *token;
|
|
uint8_t parse_count;
|
|
uint32_t tickstart;
|
|
|
|
Obj->APsActive = 0;
|
|
for (i = 0; i < EG91_MAX_SOCKETS; i++)
|
|
{
|
|
Obj->SocketInfo[i].Type = EG91_TCP_CONNECTION;
|
|
Obj->SocketInfo[i].AccessMode = EG91_BUFFER_MODE;
|
|
Obj->SocketInfo[i].ComulatedQirdData = 0;
|
|
Obj->SocketInfo[i].HaveReadLength = 0;
|
|
Obj->SocketInfo[i].UnreadLength = 0;
|
|
}
|
|
|
|
Obj->fops.IO_FlushBuffer(); /* Flush Uart intermediate buffer */
|
|
|
|
if (Obj->fops.IO_Init() == 0) /* configure and initialize UART */
|
|
{
|
|
ret = AT_Synchro(Obj);
|
|
|
|
if (ret != RET_OK)
|
|
{
|
|
printf("Fail to AT SYNC, after several attempts\r\n");
|
|
fret = EG91_INIT_RET_AT_ERR; /* if does not respond to AT command set specific return status */
|
|
}
|
|
else
|
|
{
|
|
// /* Retrieve Quectel Factory Default values */
|
|
// ret = EG91_ResetToFactoryDefault(Obj);
|
|
|
|
/* Retrieve Quectel UART baud rate and flow control*/
|
|
/* If not aligned to the UART of MCU (_io.h), already previous AT command will fail */
|
|
ret = ret | EG91_GetUARTConfig(Obj, &Obj->UART_Config);
|
|
/* Use ATV1 to set the response format */
|
|
ret = ret | AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "ATV1\r\n", RET_OK | RET_ERROR);
|
|
/* Use ATE1 to enable or ATE0 to disable echo mode */
|
|
ret = ret | AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "ATE0\r\n", RET_OK | RET_ERROR);
|
|
/* Use AT+CMEE=1 to enable result code and use "integer" values */
|
|
ret = ret | AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+CMEE=1\r\n", RET_OK | RET_ERROR);
|
|
}
|
|
|
|
/* retrieve module info */
|
|
if (ret == RET_OK)
|
|
{
|
|
ret = AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+CGMI\r\n", RET_OK | RET_ERROR);
|
|
if (ret == RET_OK)
|
|
{
|
|
align_ptr = strtok((char *)Obj->CmdResp, "\r\n");
|
|
strncpy((char*) Obj->Manufacturer, align_ptr, EG91_MFC_SIZE);
|
|
}
|
|
|
|
ret = AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+CGMM\r\n", RET_OK | RET_ERROR);
|
|
if (ret == RET_OK)
|
|
{
|
|
align_ptr = strtok((char *)Obj->CmdResp, "\r\n");
|
|
strncpy((char*) Obj->ProductID, align_ptr, EG91_PROD_ID_SIZE);
|
|
}
|
|
|
|
ret = AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+CGMR\r\n", RET_OK | RET_ERROR);
|
|
if (ret == RET_OK)
|
|
{
|
|
align_ptr = strtok((char *)Obj->CmdResp, "\r\n");
|
|
strncpy((char*) Obj->FW_Rev, align_ptr, EG91_FW_REV_SIZE);
|
|
}
|
|
|
|
/* Use AT+GSN to query the IMEI (International Mobile Equipment Identity) of module */
|
|
ret = AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+CGSN\r\n", RET_OK | RET_ERROR);
|
|
if (ret == RET_OK)
|
|
{
|
|
align_ptr = strtok((char *)Obj->CmdResp, "\r\n");
|
|
strncpy((char*) Obj->Imei, align_ptr, EG91_IMEI_SIZE);
|
|
}
|
|
}
|
|
|
|
/* retrieve SIM info */
|
|
if (ret == RET_OK)
|
|
{
|
|
|
|
/* A tempo is required to get the SIM ready. Set to 2000 ms */
|
|
tickstart = Obj->GetTickCb();
|
|
while ((Obj->GetTickCb() - tickstart) < 2000)
|
|
{
|
|
}
|
|
|
|
ret = AT_ExecuteCommand(Obj, EG91_TOUT_5000, (uint8_t*) "AT+CPIN?\r\n", RET_OK | RET_ERROR | RET_CME_ERROR);
|
|
|
|
if (RET_OK == ret)
|
|
{
|
|
align_ptr = strstr((char*) Obj->CmdResp, "+CPIN: READY");
|
|
if ( NULL != align_ptr)
|
|
{
|
|
Obj->SimInfo.SimStatus = EG91_SIM_READY;
|
|
|
|
if (RET_OK == AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+CIMI\r\n", RET_OK | RET_ERROR | RET_CME_ERROR))
|
|
{
|
|
align_ptr = strtok((char *)Obj->CmdResp, "\r\n");
|
|
strncpy((char*) Obj->SimInfo.IMSI, align_ptr, EG91_IMSI_SIZE);
|
|
}
|
|
|
|
if (RET_OK == AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+ICCID\r\n", RET_OK | RET_ERROR))
|
|
{
|
|
align_ptr = strtok((char *)Obj->CmdResp, "\r\n");
|
|
token = strchr(align_ptr, ':');
|
|
|
|
if (token != NULL)
|
|
{
|
|
token++;
|
|
while(*token == ' ')
|
|
{
|
|
token++;
|
|
}
|
|
}
|
|
strncpy((char*) Obj->SimInfo.ICCID, token, EG91_ICCID_SIZE);
|
|
}
|
|
|
|
fret = EG91_INIT_RET_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (RET_CME_ERROR == ret)
|
|
{
|
|
AT_RetrieveData(Obj, Obj->CmdResp, 0, RET_CRLF, EG91_TOUT_SHORT);
|
|
Obj->SimInfo.SimStatus = (EG91_SIMState_t) ParseNumber( (char*) Obj->CmdResp + 1, &parse_count);
|
|
fret = EG91_INIT_RET_SIM_ERR;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set the radio ON with the full functionality in the modem */
|
|
ret = AT_ExecuteCommand(Obj, EG91_TOUT_15000, (uint8_t*) "AT+CFUN=1\r\n", RET_OK | RET_ERROR | RET_CME_ERROR);
|
|
if (RET_OK != ret)
|
|
{
|
|
fret = EG91_INIT_OTHER_ERR;
|
|
}
|
|
|
|
for (int i = 0; i < 30; i++)
|
|
{
|
|
HAL_Delay(30);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fret = EG91_INIT_RET_IO_ERR;
|
|
}
|
|
|
|
return fret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get Signal Quality value
|
|
* @param Obj: pointer to module handle
|
|
* @retval Operation status.
|
|
*/
|
|
EG91_Return_t EG91_GetSignalQualityStatus(EG91Object_t *Obj, int32_t *Qvalue)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
uint8_t parse_count;
|
|
char *align_ptr;
|
|
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+CSQ\r\n", RET_OK | RET_ERROR | RET_CME_ERROR);
|
|
if (RET_OK == ret)
|
|
{
|
|
align_ptr = strstr((char*) Obj->CmdResp, "+CSQ:") + sizeof("+CSQ:");
|
|
*Qvalue = ParseNumber(align_ptr, &parse_count);
|
|
// AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+QCSQ\r\n", RET_OK | RET_ERROR | RET_CME_ERROR);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Attach the MT to the packet domain service
|
|
* @param Obj: pointer to module handle
|
|
* @retval Operation status.
|
|
*/
|
|
EG91_Return_t EG91_PSAttach(EG91Object_t *Obj)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
|
|
if (Obj->SimInfo.SimStatus == EG91_SIM_READY)
|
|
{
|
|
if (RET_OK != AT_ExecuteCommand(Obj, EG91_TOUT_150000, (uint8_t*) "AT+CGATT=1\r\n", RET_OK | RET_ERROR | RET_CME_ERROR))
|
|
{
|
|
AT_RetrieveData(Obj, Obj->CmdResp, 0, RET_CRLF, EG91_TOUT_SHORT);
|
|
printf("CME ERROR: %s\r\n", Obj->CmdResp);
|
|
ret = EG91_RETURN_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ret = EG91_RETURN_OK;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Force an automatic PLMN selection
|
|
* @param Obj: pointer to module handle
|
|
* @retval Operation status.
|
|
*/
|
|
EG91_Return_t EG91_AutomaticPlmnSelection(EG91Object_t *Obj)
|
|
{
|
|
EG91_Return_t ret;
|
|
|
|
if (RET_OK != AT_ExecuteCommand(Obj, EG91_TOUT_180000, (uint8_t*) "AT+COPS=0\r\n", RET_OK | RET_ERROR | RET_CME_ERROR))
|
|
{
|
|
AT_RetrieveData(Obj, Obj->CmdResp, 0, RET_CRLF, EG91_TOUT_SHORT);
|
|
ret = EG91_RETURN_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ret = EG91_RETURN_OK;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
EG91_Return_t EG91_SetFullFunctionality(EG91Object_t *Obj)
|
|
{
|
|
EG91_Return_t ret;
|
|
|
|
if (RET_OK != AT_ExecuteCommand(Obj, EG91_TOUT_15000, (uint8_t*) "AT+CFUN=1\r\n", RET_OK | RET_ERROR | RET_CME_ERROR))
|
|
{
|
|
AT_RetrieveData(Obj, Obj->CmdResp, 0, RET_CRLF, EG91_TOUT_SHORT);
|
|
ret = EG91_RETURN_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ret = EG91_RETURN_OK;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get Circuit Switch Registration Status
|
|
* @param Obj: pointer to module handle
|
|
* @retval Registration Status.
|
|
*/
|
|
EG91_NetworkRegistrationState_t EG91_GetCsNetworkRegistrationStatus(EG91Object_t *Obj)
|
|
{
|
|
EG91_NetworkRegistrationState_t ret = EG91_NRS_ERROR;
|
|
int n = 0; // n: mode
|
|
int stat = 0; // stat: registration state
|
|
char *cgreg_ptr;
|
|
|
|
if (Obj->SimInfo.SimStatus == EG91_SIM_READY)
|
|
{
|
|
if (RET_OK == AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+CREG?\r\n", RET_OK | RET_ERROR | RET_CME_ERROR))
|
|
{
|
|
cgreg_ptr = strstr((char*) Obj->CmdResp, "+CREG:");
|
|
if (NULL != cgreg_ptr)
|
|
{
|
|
if(sscanf(cgreg_ptr, "+CREG: %d,%d", &n, &stat) == 2)
|
|
{
|
|
ret = (EG91_NetworkRegistrationState_t)stat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get Packet Switch Registration Status
|
|
* @param Obj: pointer to module handle
|
|
* @retval Registration Status.
|
|
*/
|
|
EG91_NetworkRegistrationState_t EG91_GetPsNetworkRegistrationStatus(EG91Object_t *Obj)
|
|
{
|
|
EG91_NetworkRegistrationState_t ret = EG91_NRS_ERROR;
|
|
int n = 0; // n: mode
|
|
int stat = 0; // stat: registration state
|
|
char *cgreg_ptr;
|
|
|
|
if (Obj->SimInfo.SimStatus == EG91_SIM_READY)
|
|
{
|
|
if (RET_OK == AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+CGREG?\r\n", RET_OK | RET_ERROR | RET_CME_ERROR))
|
|
{
|
|
cgreg_ptr = strstr((char*) Obj->CmdResp, "+CGREG:");
|
|
if (NULL != cgreg_ptr)
|
|
{
|
|
if(sscanf(cgreg_ptr, "+CGREG: %d,%d", &n, &stat) == 2)
|
|
{
|
|
ret = (EG91_NetworkRegistrationState_t)stat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
EG91_NetworkRegistrationState_t EG91_GetEpsNetworkRegistrationStatus(EG91Object_t *Obj)
|
|
{
|
|
EG91_NetworkRegistrationState_t ret = EG91_NRS_ERROR;
|
|
int n = 0; // n: mode
|
|
int stat = 0; // stat: registration state
|
|
char *cgreg_ptr;
|
|
|
|
if (Obj->SimInfo.SimStatus == EG91_SIM_READY)
|
|
{
|
|
if (RET_OK == AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+CEREG?\r\n", RET_OK | RET_ERROR | RET_CME_ERROR))
|
|
{
|
|
cgreg_ptr = strstr((char*) Obj->CmdResp, "+CEREG:");
|
|
if (NULL != cgreg_ptr)
|
|
{
|
|
if(sscanf(cgreg_ptr, "+CEREG: %d,%d", &n, &stat) == 2)
|
|
{
|
|
ret = (EG91_NetworkRegistrationState_t)stat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the list of Network Operator available in the area
|
|
* @param Obj: pointer to module handle
|
|
* @param Operator: pointer to a string
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_ListOperators(EG91Object_t *Obj, char *Operators)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
char *align_ptr;
|
|
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_180000, (uint8_t*) "AT+COPS=?\r\n", RET_OK | RET_ERROR | RET_CME_ERROR);
|
|
if (RET_OK == ret)
|
|
{
|
|
align_ptr = strstr((char*) Obj->CmdResp, "+COPS:") + sizeof("+COPS:");
|
|
strncpy((char*) Operators, align_ptr, 100);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get current Network Operator (by string descriptor).
|
|
* @param Obj: pointer to module handle
|
|
* @param Operator: pointer to a string
|
|
* @param bufsize: max string buffer length
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_GetCurrentOperator(EG91Object_t *Obj, char *Operator,
|
|
uint8_t Bufsize)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
char *align_ptr;
|
|
const char s[2] = ",";
|
|
char *token;
|
|
int i;
|
|
AT_ExecuteCommand(Obj, EG91_TOUT_180000, (uint8_t*) "AT+COPS=0\r\n", RET_OK | RET_ERROR | RET_CME_ERROR);
|
|
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_180000, (uint8_t*) "AT+COPS?\r\n", RET_OK | RET_ERROR | RET_CME_ERROR);
|
|
|
|
if (RET_OK == ret)
|
|
{
|
|
align_ptr = strstr((char*) Obj->CmdResp, "+COPS:") + sizeof("+COPS:");
|
|
|
|
strncpy((char*) Operator, align_ptr, Bufsize);
|
|
|
|
/* get the first token */
|
|
token = strtok(Operator, s);
|
|
|
|
/* walk through tokens until operator info */
|
|
i = 0;
|
|
while (token != NULL && i < 2)
|
|
{
|
|
token = strtok(NULL, s);
|
|
i++;
|
|
}
|
|
|
|
if (token != NULL)
|
|
{
|
|
strncpy((char*) (Operator), token, Bufsize);
|
|
}
|
|
else
|
|
{
|
|
ret = EG91_RETURN_ERROR;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Reset To factory defaults.
|
|
* @param Obj: pointer to module handle
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_ResetToFactoryDefault(EG91Object_t *Obj)
|
|
{
|
|
EG91_Return_t ret;
|
|
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT&F\r\n", RET_OK | RET_ERROR);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set UART Configuration on the Quectel modem and on STM32.
|
|
* @param Obj: pointer to module handle
|
|
* @param pconf: pointer to UART config structure
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_SetUARTBaudrate(EG91Object_t *Obj, EG91_UARTConfig_t *pconf)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
|
|
/* change the UART baudrate on EG91 module */
|
|
snprintf(CmdString, 17, "AT+IPR=%lu\r\n", pconf->BaudRate);
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) CmdString, RET_OK | RET_ERROR);
|
|
|
|
/* change the UART baudrate on STM32 microcontroller accordingly */
|
|
Obj->fops.IO_Baudrate(pconf->BaudRate);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get UART Configuration.
|
|
* @param Obj: pointer to module handle
|
|
* @param pconf: pointer to UART config structure
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_GetUARTConfig(EG91Object_t *Obj, EG91_UARTConfig_t *pconf)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
char *align_ptr;
|
|
uint8_t rts, cts;
|
|
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+IPR?\r\n", RET_OK | RET_ERROR);
|
|
if (ret == RET_OK)
|
|
{
|
|
align_ptr = strstr((char*) Obj->CmdResp, "+IPR:") + sizeof("+IPR:");
|
|
Obj->UART_Config.BaudRate = ParseNumber(align_ptr, NULL);
|
|
}
|
|
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) "AT+IFC?\r\n", RET_OK | RET_ERROR);
|
|
if (ret == RET_OK)
|
|
{
|
|
align_ptr = strstr((char*) Obj->CmdResp, "+IFC:") + sizeof("+IPR:");
|
|
rts = ParseNumber(align_ptr, NULL);
|
|
cts = ParseNumber(align_ptr + 2, NULL);
|
|
|
|
if (rts == 2)
|
|
{
|
|
if (cts == 2)
|
|
{
|
|
pconf->FlowControl = EG91_UART_FLW_CTL_RTS_CTS;
|
|
}
|
|
else
|
|
{
|
|
pconf->FlowControl = EG91_UART_FLW_CTL_RTS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cts == 2)
|
|
{
|
|
pconf->FlowControl = EG91_UART_FLW_CTL_CTS;
|
|
}
|
|
else
|
|
{
|
|
pconf->FlowControl = EG91_UART_FLW_CTL_NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Return Manufacturer.
|
|
* @param Obj: pointer to module handle
|
|
* @param Manufacturer: pointer to Manufacturer
|
|
* @retval None.
|
|
*/
|
|
void EG91_GetManufacturer(EG91Object_t *Obj, uint8_t *Manufacturer)
|
|
{
|
|
strncpy((char*) Manufacturer, (char*) Obj->Manufacturer, EG91_MFC_SIZE);
|
|
}
|
|
|
|
/**
|
|
* @brief Return Model.
|
|
* @param Obj: pointer to module handle
|
|
* @param Model: pointer to Model
|
|
* @retval None.
|
|
*/
|
|
void EG91_GetProductID(EG91Object_t *Obj, uint8_t *ProductID)
|
|
{
|
|
strncpy((char*) ProductID, (char*) Obj->ProductID, EG91_PROD_ID_SIZE);
|
|
}
|
|
|
|
/**
|
|
* @brief Return FW revision.
|
|
* @param Obj: pointer to module handle
|
|
* @param Model: pointer to FW revision
|
|
* @retval None.
|
|
*/
|
|
void EG91_GetFWRevID(EG91Object_t *Obj, uint8_t *Fw_ver)
|
|
{
|
|
strncpy((char*) Fw_ver, (char*) Obj->FW_Rev, EG91_FW_REV_SIZE);
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieve last IP error code
|
|
* @param Obj: pointer to module handle
|
|
* @param error_string:
|
|
* @param error_code
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_RetrieveLastErrorDetails(EG91Object_t *Obj,
|
|
char *error_string)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
char *align_ptr;
|
|
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_SHORT, (uint8_t*) "AT+QIGETERROR\r\n", RET_OK | RET_ERROR);
|
|
align_ptr = strstr((char*) Obj->CmdResp, "+QIGETERROR:") + sizeof("+QIGETERROR:");
|
|
strncpy((char*) error_string, align_ptr, EG91_ERROR_STRING_SIZE);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Register EG91 Tick external cb functions to be provided by application (e.g. HAL_GetTick)
|
|
* @param Obj: pointer to module handle
|
|
* @param GetTickCb: pointer to callback function that should provide a Timer Tick in ms
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_RegisterTickCb(EG91Object_t *Obj,
|
|
App_GetTickCb_Func GetTickCb)
|
|
{
|
|
if (!Obj || !GetTickCb)
|
|
{
|
|
return EG91_RETURN_ERROR;
|
|
}
|
|
|
|
Obj->GetTickCb = GetTickCb;
|
|
|
|
return EG91_RETURN_OK;
|
|
}
|
|
|
|
/* ==== AP Connection ==== */
|
|
|
|
EG91_Return_t EG91_ConfigurePDPContext(EG91Object_t *Obj, uint8_t ContextID, const char *apn)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
|
|
snprintf(CmdString, strlen("AT+CGDCONT=%d,\"IP\",\"%s\"\r\n") + strlen(apn) + 1, "AT+CGDCONT=%d,\"IP\",\"%s\"\r\n", ContextID, apn);
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*)CmdString, RET_OK | RET_ERROR);
|
|
|
|
snprintf(CmdString, strlen("AT+CGDCONT?\r\n"), "AT+CGDCONT?\r\n");
|
|
AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*)CmdString, RET_OK | RET_ERROR);
|
|
|
|
if (ret == EG91_RETURN_OK)
|
|
{
|
|
snprintf(CmdString, EG91_CMD_SIZE, "AT+QICSGP=%d,1,\"%s\",\"%s\",\"%s\",%d\r\n",
|
|
ContextID, apn, "", "", 0);
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) CmdString, RET_OK | RET_ERROR);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Join a PDP Access point.
|
|
* @param Obj: pointer to module handle
|
|
* @param ContextID : range is 1-20 (max three can be connected simultaneously)
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_Activate(EG91Object_t *Obj, uint8_t ContextID, const char *apnStr)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
|
|
snprintf(CmdString, strlen("AT+CGDCONT=1,\"IP\", \"%s\"\r\n") + strlen(apnStr), "AT+CGDCONT=1,\"IP\", \"%s\"\r\n", apnStr);
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) CmdString, RET_OK | RET_ERROR);
|
|
if (ret == EG91_RETURN_OK)
|
|
{
|
|
if (Obj->APsActive < 3)
|
|
{
|
|
snprintf(CmdString, 24, "AT+CGACT=1,%d\r\n", ContextID);
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_150000, (uint8_t*) CmdString, RET_OK | RET_ERROR);
|
|
HAL_Delay(10);
|
|
if (ret == EG91_RETURN_OK)
|
|
{
|
|
snprintf(CmdString, 24, "AT+CGACT?\r\n");
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_150000, (uint8_t*) CmdString, RET_OK | RET_ERROR);
|
|
HAL_Delay(10);
|
|
if (ret == EG91_RETURN_OK)
|
|
{
|
|
const char *p = strchr((char *)Obj->CmdResp, ',');
|
|
if (!p)
|
|
{
|
|
return EG91_RETURN_ERROR;
|
|
}
|
|
p++;
|
|
Obj->APContextState[ContextID - 1] = atoi(p);
|
|
Obj->APsActive++;
|
|
}
|
|
}
|
|
}
|
|
HAL_Delay(500);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Leave a PDP Access point.
|
|
* @param Obj: pointer to module handle
|
|
* @param ContextID : range is 1-20 (max three are connected simultaneously)
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_Deactivate(EG91Object_t *Obj, uint8_t ContextID)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
|
|
snprintf(CmdString, 24, "AT+CGACT=0,%d\r\n", ContextID);
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_40000, (uint8_t*) CmdString, RET_OK | RET_ERROR);
|
|
if (ret == EG91_RETURN_OK)
|
|
{
|
|
snprintf(CmdString, 24, "AT+CGACT?\r\n");
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_150000, (uint8_t*) CmdString, RET_OK | RET_ERROR);
|
|
HAL_Delay(10);
|
|
const char *p = strchr((char *)Obj->CmdResp, ',');
|
|
if (!p)
|
|
{
|
|
return EG91_RETURN_ERROR;
|
|
}
|
|
p++;
|
|
Obj->APContextState[ContextID - 1] = atoi(p);
|
|
Obj->APsActive--;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Check whether the contextID is connected to an access point.
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_APState_t EG91_IsActivated(EG91Object_t *Obj, uint8_t ContextID)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
|
|
snprintf(CmdString, strlen("AT+CGACT?\r\n"), "AT+CGACT?\r\n");
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_150000, (uint8_t*) CmdString, RET_OK | RET_ERROR);
|
|
if (ret == EG91_RETURN_OK)
|
|
{
|
|
char *line = strtok((char *)Obj->CmdResp, "\r\n");
|
|
while(line != NULL)
|
|
{
|
|
if (strstr(line, "+CGACT:") != NULL)
|
|
{
|
|
int ctx_id = 0;
|
|
int act_state = 0;
|
|
|
|
if (sscanf(line, "+CGACT: %d,%d", &ctx_id, &act_state) == 2)
|
|
{
|
|
if (ctx_id == ContextID)
|
|
{
|
|
Obj->APContextState[ContextID - 1] = act_state;
|
|
return(EG91_APState_t)act_state;
|
|
}
|
|
}
|
|
}
|
|
line = strtok(NULL, "\r\n");
|
|
}
|
|
}
|
|
return EG91_AP_ERROR;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the list of the current activated context and its IP addresses
|
|
* @param Obj: pointer to module handle
|
|
* @param IPaddr_string: pointer where to retrieve the string with all active IP info
|
|
* @param IPaddr_int: pointer where to retrieve the first active IP address in int_array[] format
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_GetActiveIpAddresses(EG91Object_t *Obj)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
int32_t cmdret;
|
|
|
|
cmdret = AT_ExecuteCommand(Obj, EG91_TOUT_150000, (uint8_t*) "AT+CGPADDR=1\r\n", RET_OK | RET_ERROR);
|
|
if (cmdret == RET_OK)
|
|
{
|
|
const char* p = (const char*)Obj->CmdResp;
|
|
while ((p = strstr(p, "+CGPADDR: ")) != NULL)
|
|
{
|
|
const char *quote_start = strchr(p, '"');
|
|
if (quote_start)
|
|
{
|
|
const char *quote_end = strchr(quote_start + 1, '"');
|
|
if (quote_end)
|
|
{
|
|
char ip[64];
|
|
size_t len = quote_end - quote_start - 1;
|
|
if (len >= sizeof(ip))
|
|
len = sizeof(ip) - 1;
|
|
|
|
strncpy(ip, quote_start + 1, len);
|
|
ip[len] = '\0';
|
|
|
|
printf("IP Address: %s\r\n", ip);
|
|
}
|
|
}
|
|
// Move p forward to avoid infinite loop
|
|
p += strlen("+CGPADDR: ");
|
|
}
|
|
ret = EG91_RETURN_OK;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if (EG91_USE_PING == 1)
|
|
|
|
|
|
int _countCommaStr(const char *str)
|
|
{
|
|
int count = 0;
|
|
while(*str)
|
|
{
|
|
if (*str == ',')
|
|
{
|
|
count++;
|
|
}
|
|
str++;
|
|
}
|
|
return count;
|
|
}
|
|
/**
|
|
* @brief Test the Internet Protocol reachability of a host
|
|
* @param Obj: pointer to module handle
|
|
* @param ContextID : range is 1-20 (max three are connected simultaneously)
|
|
* @param host_addr_string: domain name (e.g. www.amazon.com) or dotted decimal IP addr
|
|
* @param count: PING repetitions (default 4) (max 10)
|
|
* @param rep_delay_sec: timeout for each repetition in seconds
|
|
* @retval Operation Status.
|
|
*/
|
|
EG91_Return_t EG91_Ping(EG91Object_t *Obj)
|
|
{
|
|
EG91_Return_t ret = EG91_RETURN_ERROR;
|
|
snprintf(CmdString, strlen("AT+QPING=1,\"8.8.8.8\",10,10\r\n"), "AT+QPING=1,\"8.8.8.8\",10,10\r\n");
|
|
ret = (EG91_Return_t) AT_ExecuteCommand(Obj, EG91_TOUT_300, (uint8_t*) CmdString, RET_OK | RET_ERROR);
|
|
if(ret == EG91_RETURN_OK)
|
|
{
|
|
uint32_t startTick = HAL_GetTick();
|
|
int res = 0;
|
|
int sent = 0;
|
|
int rcv = 0;
|
|
int loss = 0;
|
|
int min = 0;
|
|
int max = 0;
|
|
int avg = 0;
|
|
uint8_t pingOK = 0;
|
|
|
|
while(((HAL_GetTick() - startTick) < EG91_TOUT_15000))
|
|
{
|
|
AT_RetrieveData(Obj, Obj->CmdResp, 0, RET_NETCLOSE, EG91_TOUT_SHORT);
|
|
if (_countCommaStr((char *)Obj->CmdResp) == 6)
|
|
{
|
|
sscanf((char *)Obj->CmdResp, " %d,%d,%d,%d,%d,%d,%d", &res, &sent, &rcv, &loss, &min, &max, &avg);
|
|
printf("PING Result: %d out of %d\r\n\r\n", rcv, sent);
|
|
pingOK = 1;
|
|
}
|
|
}
|
|
if(pingOK == 0)
|
|
{
|
|
printf("PING Result: NOK\r\n\r\n");
|
|
ret = EG91_RETURN_ERROR;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
|
|