/********************************************************************************* * 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 * ChangeLog: 1, Release initial version on "11/08/23 16:18:43" * ********************************************************************************/ #include #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= 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