/*********************************************************************************
|
* Copyright: (C) 2023 LingYun IoT System Studio.
|
* All rights reserved.
|
*
|
* Filename: atcmd.c
|
* Description: This file is lowlevel AT command send and parser API functions
|
*
|
* Version: 1.0.0(11/08/23)
|
* Author: Guo Wenxue <guowenxue@gmail.com>
|
* ChangeLog: 1, Release initial version on "11/08/23 16:18:43"
|
*
|
********************************************************************************/
|
|
#include <ctype.h>
|
#include "logger.h"
|
#include "atcmd.h"
|
|
/* Description: this function used to send an AT command from serial port and wait for reply message from
|
* the communication module, and it will return once get expet/error string or timeout.
|
*
|
* Arugments:
|
* $comport: the serial port which connected to GPRS/3G/4G/NB-IoT/WiFi/BLE/Zigbee/LoRa...
|
* $at: the AT command need to be sent, without "\r\n"
|
* $timeout: wait for module reply for AT command timeout value, unit micro seconds(ms)
|
* $exepct: the expect string reply from communication module
|
* $error: the error string reply from communication module
|
* $reply: the module reply message output buffer
|
* $size: the output buffer ($reply) size
|
*
|
* Return value: <0: Function error 0: Got error string 1: Got expect string 2: Receive util timeout
|
*
|
*/
|
|
int send_atcmd(comport_t *comport, char *at, unsigned long timeout, char *expect, char *error, char *reply, int size)
|
{
|
int i, rv = 0;
|
int res = ATRES_TIMEOUT;
|
int bytes = 0;
|
char buf[ATCMD_REPLY_LEN] = {'\0'};
|
char atcmd[ATCMD_LEN] = {'\0'};
|
|
if( !comport || !at )
|
{
|
log_error("Invalid input arguments\n");
|
return -1;
|
}
|
|
if( comport->fd <= 0 )
|
{
|
log_error("comport[%s] not opened\n");
|
return -2;
|
}
|
|
/* flushes both data received but not read, and data written but not transmitted in serial port */
|
tcflush(comport->fd, TCIOFLUSH);
|
|
snprintf(atcmd, sizeof(atcmd), "%s%s", at, AT_SUFFIX);
|
rv=comport_send( comport, atcmd, strlen(atcmd) );
|
if(rv < 0)
|
{
|
log_error("send AT command \"%s\" to \"%s\" failed, rv=%d\n", at, comport->devname, rv);
|
return -3;
|
}
|
|
res = ATRES_TIMEOUT;
|
memset( buf, 0, sizeof(buf) );
|
|
for(i=0; i<timeout/10; i++)
|
{
|
if( bytes >= sizeof(buf) )
|
break;
|
|
rv=comport_recv( comport, buf+bytes, sizeof(buf)-bytes, 10);
|
if(rv < 0)
|
{
|
log_error("send AT command \'%s\' to \'%s\' failed, rv=%d\n", at, comport->devname, rv);
|
return -3;
|
}
|
|
bytes += rv;
|
|
if( expect && strstr(buf, expect) )
|
{
|
log_debug("send AT command \"%s\" and got reply \"OK\"\n", at);
|
res = ATRES_EXPECT;
|
break;
|
}
|
|
if( error && strstr(buf, error) )
|
{
|
log_debug("send AT command \"%s\" and got reply \"ERROR\"\n", at);
|
res = ATRES_ERROR;
|
break;
|
}
|
}
|
|
if( bytes > 0 )
|
log_trace("AT command reply:%s", buf);
|
|
if( reply && size>0 )
|
{
|
bytes = strlen(buf)>size ? size : strlen(buf);
|
memset(reply, 0, size);
|
strncpy(reply, buf, bytes);
|
|
log_debug("copy out AT command \"%s\" reply message: \n%s", at, reply);
|
}
|
|
return res;
|
}
|
|
|
/*
|
* Description: Send AT command which will only reply by "OK" or "ERROR", such as AT:
|
* Reply: \r\nOK\r\n
|
* Return Value: 0: OK -X: Failure
|
*/
|
int send_atcmd_check_ok(comport_t *comport, char *at, unsigned long timeout)
|
{
|
int rv;
|
|
if( !comport || !at )
|
{
|
log_error("Invalid input arguments\n");
|
return -1;
|
}
|
|
rv=send_atcmd(comport, at, timeout, AT_OKSTR, AT_ERRSTR, NULL, 0);
|
if( ATRES_EXPECT == rv )
|
{
|
return 0;
|
}
|
else
|
{
|
return -2;
|
}
|
}
|
|
|
/*
|
* Description: Send AT command which will reply by a value directly in a single line, such as AT+CGMM:
|
* Reply: \r\nEC20F\r\nOK\r\n
|
*
|
* Return Value: 0: OK -X: Failure
|
*/
|
int send_atcmd_check_value(comport_t *comport, char *at, unsigned long timeout, char *reply, int size)
|
{
|
int rv, len;
|
char buf[ATCMD_REPLY_LEN];
|
char *ptr_start = buf;
|
char *ptr_end;
|
|
if( !comport || !at || !reply || size<=0 )
|
{
|
log_error("Invalid input arguments\n");
|
return -1;
|
}
|
|
rv = send_atcmd(comport, at, timeout, AT_OKSTR, AT_ERRSTR, buf, ATCMD_REPLY_LEN);
|
if( rv <= 0 )
|
{
|
return -2;
|
}
|
|
/* Skip the echo back command line */
|
if( !strncmp(buf, at, strlen(at)) )
|
{
|
ptr_start=strchr(buf, '\n');
|
if( !ptr_start )
|
{
|
log_error("reply message got wrong\n");
|
return -3;
|
}
|
|
ptr_start++; /* skip '\n' */
|
}
|
|
/* find end reply string "\r\nOK\r\n" */
|
ptr_end = strstr(ptr_start, AT_OKSTR);
|
if( ptr_end )
|
{
|
len = ptr_end - ptr_start;
|
}
|
else
|
{
|
len = strlen(buf) - (ptr_start-buf);
|
}
|
|
memset(reply, 0, size);
|
|
len = len>size ? size : len;
|
memcpy(reply, ptr_start, len);
|
|
return 0;
|
}
|
|
/*
|
* Description: Parser the $value from $key like "xxx: " line, such as AT+CSQ:
|
* Reply: \r\n+CSQ: 26,99\r\nOK\r\n
|
*
|
* Return Value: 0: OK -X: Failure
|
*/
|
int parser_request_value(char *buf, char *key, char *value, int size)
|
{
|
char *ptr;
|
int i = 0;
|
|
if( !buf || !key || !value || size<=0 )
|
{
|
log_error("Invalid input arguments\n");
|
return -1;
|
}
|
|
ptr = strstr(buf, key);
|
if( !ptr )
|
{
|
log_debug("Not found key \"%s\" in %s\n", key, buf);
|
return -2;
|
}
|
|
ptr=strchr(ptr, ':'); /* found ':' before the value */
|
if( !ptr )
|
{
|
log_debug("Not found ':' before value\n");
|
return -3;
|
}
|
ptr++; /* skip ':' */
|
|
if( *ptr == '\"' ) /* skip " */
|
ptr++;
|
|
memset(value, 0, size);
|
while(*ptr!='\r' && i<size-1)
|
{
|
if( !isspace(*ptr) && *ptr!='\"') /* skip space,\r,\n ... */
|
value[i++] = *ptr;
|
ptr ++;
|
}
|
|
ptr++; /* skip */
|
|
return 0;
|
}
|
|
/*
|
* Description: Send AT command which will reply by the value with a prefix "+CMD: " line, such as AT+CSQ:
|
* Reply: \r\n+CSQ: 26,99\r\nOK\r\n
|
*
|
* Return Value: 0: OK -X: Failure
|
*/
|
int send_atcmd_check_request(comport_t *comport, char *at, unsigned long timeout, char *reply, int size)
|
{
|
int i = 0;
|
int rv;
|
char buf[ATCMD_REPLY_LEN];
|
char *ptr;
|
|
if( !comport || !at || !reply || size<=0 )
|
{
|
log_error("Invalid input arguments\n");
|
return -1;
|
}
|
|
rv = send_atcmd(comport, at, timeout, AT_OKSTR, AT_ERRSTR, buf, ATCMD_REPLY_LEN);
|
if( rv <= 0 )
|
{
|
return -2;
|
}
|
|
ptr=strchr(buf, '+'); /* found '+' before the value */
|
if( !ptr )
|
{
|
log_error("reply message got wrong\n");
|
return -3;
|
}
|
ptr++; /* skip '+' */
|
|
|
ptr=strchr(buf, ':'); /* found ':' before the value */
|
if( !ptr )
|
{
|
log_error("reply message got wrong\n");
|
return -3;
|
}
|
ptr++; /* skip ':' */
|
|
if( *ptr == '\"' ) /* skip " */
|
ptr++;
|
|
memset(reply, 0, size);
|
while(*ptr!='\r' && i<size-1)
|
{
|
if( !isspace(*ptr) && *ptr!='\"') /* skip space,\r,\n ... */
|
reply[i++] = *ptr;
|
ptr ++;
|
}
|
|
return 0;
|
}
|