初始提交

This commit is contained in:
2025-06-27 00:32:57 +08:00
commit e40bb2ba8a
5134 changed files with 2158460 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
menu "AT commands"
config RT_USING_AT
bool "Enable AT commands"
default n
if RT_USING_AT
config AT_DEBUG
bool "Enable debug log output"
default n
config AT_USING_SERVER
bool "Enable AT commands server"
default n
if AT_USING_SERVER
config AT_SERVER_DEVICE
string "Server device name"
default "uart3"
config AT_SERVER_RECV_BUFF_LEN
int "The maximum length of server data accepted"
default 256
choice
prompt "The commands new line sign"
help
This end mark can used for AT server determine the end of commands ,
it can choose "\r", "\n" or "\r\n"
default AT_CMD_END_MARK_CRLF
config AT_CMD_END_MARK_CRLF
bool "\\r\\n"
config AT_CMD_END_MARK_CR
bool "\\r"
config AT_CMD_END_MARK_LF
bool "\\n"
endchoice
endif
config AT_USING_CLIENT
bool "Enable AT commands client"
default n
if AT_USING_CLIENT
config AT_CLIENT_NUM_MAX
int "The maximum number of supported clients"
default 1
range 1 65535
config AT_USING_SOCKET
bool "Enable BSD Socket API support by AT commnads"
select RT_USING_LIBC
select RT_USING_SAL
default n
endif
if AT_USING_SERVER || AT_USING_CLIENT
config AT_USING_CLI
bool "Enable CLI(Command-Line Interface) for AT commands"
default y
depends on FINSH_USING_MSH
config AT_PRINT_RAW_CMD
bool "Enable print RAW format AT command communication data"
default n
endif
config AT_SW_VERSION_NUM
hex
default 0x10200
help
sfotware module version number
endif
endmenu

View File

@@ -0,0 +1,26 @@
from building import *
cwd = GetCurrentDir()
path = [cwd + '/include']
src = Glob('src/at_utils.c')
if GetDepend(['AT_USING_CLI']):
src += Glob('src/at_cli.c')
if GetDepend(['AT_USING_SERVER']):
src += Split('''
src/at_server.c
src/at_base_cmd.c
''')
if GetDepend(['AT_USING_CLIENT']):
src += Glob('src/at_client.c')
if GetDepend(['AT_USING_SOCKET']):
src += Glob('at_socket/*.c')
path += [cwd + '/at_socket']
group = DefineGroup('AT', src, depend = ['RT_USING_AT'], CPPPATH = path)
Return('group')

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-06-06 chenYong first version
*/
#ifndef __AT_SOCKET_H__
#define __AT_SOCKET_H__
#include <rtthread.h>
#include <rtdevice.h>
#include <rthw.h>
#include <netdb.h>
#include <sys/socket.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef AT_SOCKET_RECV_BFSZ
#define AT_SOCKET_RECV_BFSZ 512
#endif
#define AT_DEFAULT_RECVMBOX_SIZE 10
#define AT_DEFAULT_ACCEPTMBOX_SIZE 10
/* sal socket magic word */
#define AT_SOCKET_MAGIC 0xA100
/* Current state of the AT socket. */
enum at_socket_state
{
AT_SOCKET_NONE,
AT_SOCKET_LISTEN,
AT_SOCKET_CONNECT,
AT_SOCKET_CLOSED
};
enum at_socket_type
{
AT_SOCKET_INVALID = 0,
AT_SOCKET_TCP = 0x10, /* TCP IPv4 */
AT_SOCKET_UDP = 0x20, /* UDP IPv4 */
};
typedef enum
{
AT_SOCKET_EVT_RECV,
AT_SOCKET_EVT_CLOSED,
} at_socket_evt_t;
typedef void (*at_evt_cb_t)(int socket, at_socket_evt_t event, const char *buff, size_t bfsz);
struct at_socket;
/* A callback prototype to inform about events for AT socket */
typedef void (* at_socket_callback)(struct at_socket *conn, int event, uint16_t len);
/* AT device socket options function */
struct at_device_ops
{
int (*at_connect)(int socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client);
int (*at_closesocket)(int socket);
int (*at_send)(int socket, const char *buff, size_t bfsz, enum at_socket_type type);
int (*at_domain_resolve)(const char *name, char ip[16]);
void (*at_set_event_cb)(at_socket_evt_t event, at_evt_cb_t cb);
};
/* AT receive package list structure */
struct at_recv_pkt
{
rt_slist_t list;
size_t bfsz_totle;
size_t bfsz_index;
char *buff;
};
typedef struct at_recv_pkt *at_recv_pkt_t;
struct at_socket
{
/* AT socket magic word */
uint32_t magic;
int socket;
/* type of the AT socket (TCP, UDP or RAW) */
enum at_socket_type type;
/* current state of the AT socket */
enum at_socket_state state;
/* receive semaphore, received data release semaphore */
rt_sem_t recv_notice;
rt_mutex_t recv_lock;
rt_slist_t recvpkt_list;
/* timeout to wait for send or received data in milliseconds */
int32_t recv_timeout;
int32_t send_timeout;
/* A callback function that is informed about events for this AT socket */
at_socket_callback callback;
/* number of times data was received, set by event_callback() */
uint16_t rcvevent;
/* number of times data was ACKed (free send buffer), set by event_callback() */
uint16_t sendevent;
/* error happened for this socket, set by event_callback() */
uint16_t errevent;
#ifdef SAL_USING_POSIX
rt_wqueue_t wait_head;
#endif
};
int at_socket(int domain, int type, int protocol);
int at_closesocket(int socket);
int at_shutdown(int socket, int how);
int at_bind(int socket, const struct sockaddr *name, socklen_t namelen);
int at_connect(int socket, const struct sockaddr *name, socklen_t namelen);
int at_sendto(int socket, const void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen);
int at_send(int socket, const void *data, size_t size, int flags);
int at_recvfrom(int socket, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
int at_recv(int socket, void *mem, size_t len, int flags);
int at_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen);
int at_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen);
struct hostent *at_gethostbyname(const char *name);
int at_getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res);
void at_freeaddrinfo(struct addrinfo *ai);
struct at_socket *at_get_socket(int socket);
void at_socket_device_register(const struct at_device_ops *ops);
#ifndef RT_USING_SAL
#define socket(domain, type, protocol) at_socket(domain, type, protocol)
#define closesocket(socket) at_closesocket(socket)
#define shutdown(socket, how) at_shutdown(socket, how)
#define bind(socket, name, namelen) at_bind(socket, name, namelen)
#define connect(socket, name, namelen) at_connect(socket, name, namelen)
#define sendto(socket, data, size, flags, to, tolen) at_sendto(socket, data, size, flags, to, tolen)
#define send(socket, data, size, flags) at_send(socket, data, size, flags)
#define recvfrom(socket, mem, len, flags, from, fromlen) at_recvfrom(socket, mem, len, flags, from, fromlen)
#define getsockopt(socket, level, optname, optval, optlen) at_getsockopt(socket, level, optname, optval, optlen)
#define setsockopt(socket, level, optname, optval, optlen) at_setsockopt(socket, level, optname, optval, optlen)
#define gethostbyname(name) at_gethostbyname(name)
#define getaddrinfo(nodename, servname, hints, res) at_getaddrinfo(nodename, servname, hints, res)
#define freeaddrinfo(ai) at_freeaddrinfo(ai)
#endif /* RT_USING_SAL */
#ifdef __cplusplus
}
#endif
#endif /* AT_SOCKET_H__ */

View File

@@ -0,0 +1,256 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-03-30 chenyong first version
* 2018-08-17 chenyong multiple client support
*/
#ifndef __AT_H__
#define __AT_H__
#include <rtthread.h>
#ifdef __cplusplus
extern "C" {
#endif
#define AT_SW_VERSION "1.2.0"
#define AT_CMD_NAME_LEN 16
#define AT_END_MARK_LEN 4
#ifndef AT_CMD_MAX_LEN
#define AT_CMD_MAX_LEN 128
#endif
/* the server AT commands new line sign */
#if defined(AT_CMD_END_MARK_CRLF)
#define AT_CMD_END_MARK "\r\n"
#elif defined(AT_CMD_END_MARK_CR)
#define AT_CMD_END_MARK "\r"
#elif defined(AT_CMD_END_MARK_LF)
#define AT_CMD_END_MARK "\n"
#endif
#ifndef AT_SERVER_RECV_BUFF_LEN
#define AT_SERVER_RECV_BUFF_LEN 256
#endif
#ifndef AT_SERVER_DEVICE
#define AT_SERVER_DEVICE "uart2"
#endif
/* the maximum number of supported AT clients */
#ifndef AT_CLIENT_NUM_MAX
#define AT_CLIENT_NUM_MAX 1
#endif
#define AT_CMD_EXPORT(_name_, _args_expr_, _test_, _query_, _setup_, _exec_) \
RT_USED static const struct at_cmd __at_cmd_##_test_##_query_##_setup_##_exec_ SECTION("RtAtCmdTab") = \
{ \
_name_, \
_args_expr_, \
_test_, \
_query_, \
_setup_, \
_exec_, \
};
enum at_status
{
AT_STATUS_UNINITIALIZED = 0,
AT_STATUS_INITIALIZED,
AT_STATUS_BUSY,
};
typedef enum at_status at_status_t;
#ifdef AT_USING_SERVER
enum at_result
{
AT_RESULT_OK = 0, /* AT result is no error */
AT_RESULT_FAILE = -1, /* AT result have a generic error */
AT_RESULT_NULL = -2, /* AT result not need return */
AT_RESULT_CMD_ERR = -3, /* AT command format error or No way to execute */
AT_RESULT_CHECK_FAILE = -4, /* AT command expression format is error */
AT_RESULT_PARSE_FAILE = -5, /* AT command arguments parse is error */
};
typedef enum at_result at_result_t;
struct at_cmd
{
char name[AT_CMD_NAME_LEN];
char *args_expr;
at_result_t (*test)(void);
at_result_t (*query)(void);
at_result_t (*setup)(const char *args);
at_result_t (*exec)(void);
};
typedef struct at_cmd *at_cmd_t;
struct at_server
{
rt_device_t device;
at_status_t status;
char (*get_char)(void);
rt_bool_t echo_mode;
char recv_buffer[AT_SERVER_RECV_BUFF_LEN];
rt_size_t cur_recv_len;
rt_sem_t rx_notice;
char end_mark[AT_END_MARK_LEN];
rt_thread_t parser;
void (*parser_entry)(struct at_server *server);
};
typedef struct at_server *at_server_t;
#endif /* AT_USING_SERVER */
#ifdef AT_USING_CLIENT
enum at_resp_status
{
AT_RESP_OK = 0, /* AT response end is OK */
AT_RESP_ERROR = -1, /* AT response end is ERROR */
AT_RESP_TIMEOUT = -2, /* AT response is timeout */
AT_RESP_BUFF_FULL= -3, /* AT response buffer is full */
};
typedef enum at_resp_status at_resp_status_t;
struct at_response
{
/* response buffer */
char *buf;
/* the maximum response buffer size */
rt_size_t buf_size;
/* the number of setting response lines
* == 0: the response data will auto return when received 'OK' or 'ERROR'
* != 0: the response data will return when received setting lines number data */
rt_size_t line_num;
/* the count of received response lines */
rt_size_t line_counts;
/* the maximum response time */
rt_int32_t timeout;
};
typedef struct at_response *at_response_t;
/* URC(Unsolicited Result Code) object, such as: 'RING', 'READY' request by AT server */
struct at_urc
{
const char *cmd_prefix;
const char *cmd_suffix;
void (*func)(const char *data, rt_size_t size);
};
typedef struct at_urc *at_urc_t;
struct at_client
{
rt_device_t device;
at_status_t status;
char end_sign;
char *recv_buffer;
rt_size_t recv_bufsz;
rt_size_t cur_recv_len;
rt_sem_t rx_notice;
rt_mutex_t lock;
at_response_t resp;
rt_sem_t resp_notice;
at_resp_status_t resp_status;
const struct at_urc *urc_table;
rt_size_t urc_table_size;
rt_thread_t parser;
};
typedef struct at_client *at_client_t;
#endif /* AT_USING_CLIENT */
#ifdef AT_USING_SERVER
/* AT server initialize and start */
int at_server_init(void);
/* AT server send command execute result to AT device */
void at_server_printf(const char *format, ...);
void at_server_printfln(const char *format, ...);
void at_server_print_result(at_result_t result);
/* AT server request arguments parse */
int at_req_parse_args(const char *req_args, const char *req_expr, ...);
#endif /* AT_USING_SERVER */
#ifdef AT_USING_CLIENT
/* AT client initialize and start*/
int at_client_init(const char *dev_name, rt_size_t recv_bufsz);
/* ========================== multiple AT client function ============================ */
/* get AT client object */
at_client_t at_client_get(const char *dev_name);
at_client_t at_client_get_first(void);
/* AT client wait for connection to external devices. */
int at_client_obj_wait_connect(at_client_t client, rt_uint32_t timeout);
/* AT client send or receive data */
rt_size_t at_client_obj_send(at_client_t client, const char *buf, rt_size_t size);
rt_size_t at_client_obj_recv(at_client_t client, char *buf, rt_size_t size, rt_int32_t timeout);
/* set AT client a line end sign */
void at_obj_set_end_sign(at_client_t client, char ch);
/* Set URC(Unsolicited Result Code) table */
void at_obj_set_urc_table(at_client_t client, const struct at_urc * table, rt_size_t size);
/* AT client send commands to AT server and waiter response */
int at_obj_exec_cmd(at_client_t client, at_response_t resp, const char *cmd_expr, ...);
/* AT response object create and delete */
at_response_t at_create_resp(rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout);
void at_delete_resp(at_response_t resp);
at_response_t at_resp_set_info(at_response_t resp, rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout);
/* AT response line buffer get and parse response buffer arguments */
const char *at_resp_get_line(at_response_t resp, rt_size_t resp_line);
const char *at_resp_get_line_by_kw(at_response_t resp, const char *keyword);
int at_resp_parse_line_args(at_response_t resp, rt_size_t resp_line, const char *resp_expr, ...);
int at_resp_parse_line_args_by_kw(at_response_t resp, const char *keyword, const char *resp_expr, ...);
/* ========================== single AT client function ============================ */
/**
* NOTE: These functions can be used directly when there is only one AT client.
* If there are multiple AT Client in the program, these functions can operate on the first initialized AT client.
*/
#define at_exec_cmd(resp, ...) at_obj_exec_cmd(at_client_get_first(), resp, __VA_ARGS__)
#define at_client_wait_connect(timeout) at_client_obj_wait_connect(at_client_get_first(), timeout)
#define at_client_send(buf, size) at_client_obj_send(at_client_get_first(), buf, size)
#define at_client_recv(buf, size, timeout) at_client_obj_recv(at_client_get_first(), buf, size, timeout)
#define at_set_end_sign(ch) at_obj_set_end_sign(at_client_get_first(), ch)
#define at_set_urc_table(urc_table, table_sz) at_obj_set_urc_table(at_client_get_first(), urc_table, table_sz)
#endif /* AT_USING_CLIENT */
/* ========================== User port function ============================ */
#ifdef AT_USING_SERVER
/* AT server device reset */
void at_port_reset(void);
/* AT server device factory reset */
void at_port_factory_reset(void);
#endif
#ifdef __cplusplus
}
#endif
#endif /* __AT_H__ */

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-10-12 armink first version
*/
/*
* NOTE: DO NOT include this file on the header file.
*/
#define DBG_ENABLE
#define DBG_COLOR
#ifndef LOG_TAG
#define DBG_SECTION_NAME "at"
#else
#define DBG_SECTION_NAME LOG_TAG
#endif /* LOG_TAG */
#ifdef AT_DEBUG
#define DBG_LEVEL DBG_LOG
#else
#define DBG_LEVEL DBG_INFO
#endif /* AT_DEBUG */
#include <rtdbg.h>

View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-04-01 armink first version
* 2018-04-04 chenyong add base commands
*/
#include <at.h>
#include <stdlib.h>
#include <string.h>
#include <rtdevice.h>
#ifdef AT_USING_SERVER
#define AT_ECHO_MODE_CLOSE 0
#define AT_ECHO_MODE_OPEN 1
extern at_server_t at_get_server(void);
static at_result_t at_exec(void)
{
return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT", RT_NULL, RT_NULL, RT_NULL, RT_NULL, at_exec);
static at_result_t atz_exec(void)
{
at_server_printfln("OK");
at_port_factory_reset();
return AT_RESULT_NULL;
}
AT_CMD_EXPORT("ATZ", RT_NULL, RT_NULL, RT_NULL, RT_NULL, atz_exec);
static at_result_t at_rst_exec(void)
{
at_server_printfln("OK");
at_port_reset();
return AT_RESULT_NULL;
}
AT_CMD_EXPORT("AT+RST", RT_NULL, RT_NULL, RT_NULL, RT_NULL, at_rst_exec);
static at_result_t ate_setup(const char *args)
{
int echo_mode = atoi(args);
if(echo_mode == AT_ECHO_MODE_CLOSE || echo_mode == AT_ECHO_MODE_OPEN)
{
at_get_server()->echo_mode = echo_mode;
}
else
{
return AT_RESULT_FAILE;
}
return AT_RESULT_OK;
}
AT_CMD_EXPORT("ATE", "<value>", RT_NULL, RT_NULL, ate_setup, RT_NULL);
static at_result_t at_show_cmd_exec(void)
{
extern void rt_at_server_print_all_cmd(void);
rt_at_server_print_all_cmd();
return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT&L", RT_NULL, RT_NULL, RT_NULL, RT_NULL, at_show_cmd_exec);
static at_result_t at_uart_query(void)
{
struct rt_serial_device *serial = (struct rt_serial_device *)at_get_server()->device;
at_server_printfln("AT+UART=%d,%d,%d,%d,%d", serial->config.baud_rate, serial->config.data_bits,
serial->config.stop_bits, serial->config.parity, 1);
return AT_RESULT_OK;
}
static at_result_t at_uart_setup(const char *args)
{
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
int baudrate, databits, stopbits, parity, flow_control, argc;
const char *req_expr = "=%d,%d,%d,%d,%d";
argc = at_req_parse_args(args, req_expr, &baudrate, &databits, &stopbits, &parity, &flow_control);
if (argc != 5)
{
return AT_RESULT_PARSE_FAILE;
}
at_server_printfln("UART baudrate : %d", baudrate);
at_server_printfln("UART databits : %d", databits);
at_server_printfln("UART stopbits : %d", stopbits);
at_server_printfln("UART parity : %d", parity);
at_server_printfln("UART control : %d", flow_control);
config.baud_rate = baudrate;
config.data_bits = databits;
config.stop_bits = stopbits;
config.parity = parity;
if(rt_device_control(at_get_server()->device, RT_DEVICE_CTRL_CONFIG, &config) != RT_EOK)
{
return AT_RESULT_FAILE;
}
return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+UART", "=<baudrate>,<databits>,<stopbits>,<parity>,<flow_control>", RT_NULL, at_uart_query, at_uart_setup, RT_NULL);
#endif /* AT_USING_SERVER */

View File

@@ -0,0 +1,335 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-04-02 armink first version
*/
#include <at.h>
#include <stdio.h>
#include <string.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <rthw.h>
#ifdef AT_USING_CLI
#define AT_CLI_FIFO_SIZE 256
static struct rt_semaphore console_rx_notice;
static struct rt_ringbuffer *console_rx_fifo = RT_NULL;
static rt_err_t (*odev_rx_ind)(rt_device_t dev, rt_size_t size) = RT_NULL;
#ifdef AT_USING_CLIENT
static struct rt_semaphore client_rx_notice;
static struct rt_ringbuffer *client_rx_fifo = RT_NULL;
#endif
static char console_getchar(void)
{
char ch;
rt_sem_take(&console_rx_notice, RT_WAITING_FOREVER);
rt_ringbuffer_getchar(console_rx_fifo, (rt_uint8_t *)&ch);
return ch;
}
static rt_err_t console_getchar_rx_ind(rt_device_t dev, rt_size_t size)
{
uint8_t ch;
rt_size_t i;
for (i = 0; i < size; i++)
{
/* read a char */
if (rt_device_read(dev, 0, &ch, 1))
{
rt_ringbuffer_put_force(console_rx_fifo, &ch, 1);
rt_sem_release(&console_rx_notice);
}
}
return RT_EOK;
}
void at_cli_init(void)
{
rt_base_t int_lvl;
rt_device_t console;
rt_sem_init(&console_rx_notice, "cli_c", 0, RT_IPC_FLAG_FIFO);
/* create RX FIFO */
console_rx_fifo = rt_ringbuffer_create(AT_CLI_FIFO_SIZE);
/* created must success */
RT_ASSERT(console_rx_fifo);
int_lvl = rt_hw_interrupt_disable();
console = rt_console_get_device();
if (console)
{
/* backup RX indicate */
odev_rx_ind = console->rx_indicate;
rt_device_set_rx_indicate(console, console_getchar_rx_ind);
}
rt_hw_interrupt_enable(int_lvl);
}
void at_cli_deinit(void)
{
rt_base_t int_lvl;
rt_device_t console;
int_lvl = rt_hw_interrupt_disable();
console = rt_console_get_device();
if (console && odev_rx_ind)
{
/* restore RX indicate */
rt_device_set_rx_indicate(console, odev_rx_ind);
}
rt_hw_interrupt_enable(int_lvl);
rt_sem_detach(&console_rx_notice);
rt_ringbuffer_destroy(console_rx_fifo);
}
#ifdef AT_USING_SERVER
static void server_cli_parser(void)
{
extern at_server_t at_get_server(void);
at_server_t server = at_get_server();
rt_base_t int_lvl;
static rt_device_t device_bak;
static char (*getchar_bak)(void);
static char endmark_back[AT_END_MARK_LEN];
/* backup server device and getchar function */
{
int_lvl = rt_hw_interrupt_disable();
device_bak = server->device;
getchar_bak = server->get_char;
memset(endmark_back, 0x00, AT_END_MARK_LEN);
memcpy(endmark_back, server->end_mark, strlen(server->end_mark));
/* setup server device as console device */
server->device = rt_console_get_device();
server->get_char = console_getchar;
memset(server->end_mark, 0x00, AT_END_MARK_LEN);
server->end_mark[0] = '\r';
rt_hw_interrupt_enable(int_lvl);
}
if (server)
{
rt_kprintf("======== Welcome to using RT-Thread AT command server cli ========\n");
rt_kprintf("Input your at command for test server. Press 'ESC' to exit.\n");
server->parser_entry(server);
}
else
{
rt_kprintf("AT client not initialized\n");
}
/* restore server device and getchar function */
{
int_lvl = rt_hw_interrupt_disable();
server->device = device_bak;
server->get_char = getchar_bak;
memset(server->end_mark, 0x00, AT_END_MARK_LEN);
memcpy(server->end_mark, endmark_back, strlen(endmark_back));
rt_hw_interrupt_enable(int_lvl);
}
}
#endif /* AT_USING_SERVER */
#ifdef AT_USING_CLIENT
static char client_getchar(void)
{
char ch;
rt_sem_take(&client_rx_notice, RT_WAITING_FOREVER);
rt_ringbuffer_getchar(client_rx_fifo, (rt_uint8_t *)&ch);
return ch;
}
static void at_client_entry(void *param)
{
char ch;
while(1)
{
ch = client_getchar();
rt_kprintf("%c", ch);
}
}
static rt_err_t client_getchar_rx_ind(rt_device_t dev, rt_size_t size)
{
uint8_t ch;
rt_size_t i;
for (i = 0; i < size; i++)
{
/* read a char */
if (rt_device_read(dev, 0, &ch, 1))
{
rt_ringbuffer_put_force(client_rx_fifo, &ch, 1);
rt_sem_release(&client_rx_notice);
}
}
return RT_EOK;
}
static void client_cli_parser(at_client_t client)
{
#define ESC_KEY 0x1B
#define BACKSPACE_KEY 0x08
#define DELECT_KEY 0x7F
char ch;
char cur_line[FINSH_CMD_SIZE] = { 0 };
rt_size_t cur_line_len = 0;
static rt_err_t (*client_odev_rx_ind)(rt_device_t dev, rt_size_t size) = RT_NULL;
rt_base_t int_lvl;
rt_thread_t at_client;
if (client)
{
/* backup client device RX indicate */
{
int_lvl = rt_hw_interrupt_disable();
client_odev_rx_ind = client->device->rx_indicate;
rt_device_set_rx_indicate(client->device, client_getchar_rx_ind);
rt_hw_interrupt_enable(int_lvl);
}
rt_sem_init(&client_rx_notice, "cli_r", 0, RT_IPC_FLAG_FIFO);
client_rx_fifo = rt_ringbuffer_create(AT_CLI_FIFO_SIZE);
at_client = rt_thread_create("at_cli", at_client_entry, RT_NULL, 512, 8, 8);
if (client_rx_fifo && at_client)
{
rt_kprintf("======== Welcome to using RT-Thread AT command client cli ========\n");
rt_kprintf("Cli will forward your command to server port(%s). Press 'ESC' to exit.\n", client->device->parent.name);
rt_thread_startup(at_client);
/* process user input */
while (ESC_KEY != (ch = console_getchar()))
{
if (ch == BACKSPACE_KEY || ch == DELECT_KEY)
{
if (cur_line_len)
{
cur_line[--cur_line_len] = 0;
rt_kprintf("\b \b");
}
continue;
}
else if (ch == '\r' || ch == '\n')
{
/* execute a AT request */
if (cur_line_len)
{
rt_kprintf("\n");
at_obj_exec_cmd(client, RT_NULL, "%.*s", cur_line_len, cur_line);
}
cur_line_len = 0;
}
else
{
rt_kprintf("%c", ch);
cur_line[cur_line_len++] = ch;
}
}
/* restore client device RX indicate */
{
int_lvl = rt_hw_interrupt_disable();
rt_device_set_rx_indicate(client->device, client_odev_rx_ind);
rt_hw_interrupt_enable(int_lvl);
}
rt_thread_delete(at_client);
rt_sem_detach(&client_rx_notice);
rt_ringbuffer_destroy(client_rx_fifo);
}
else
{
rt_kprintf("No mem for AT cli client\n");
}
}
else
{
rt_kprintf("AT client not initialized\n");
}
}
#endif /* AT_USING_CLIENT */
static void at(int argc, char **argv)
{
if (argc != 2 && argc != 3)
{
rt_kprintf("Please input '<server|client [dev_name]>' \n");
return;
}
at_cli_init();
if (!strcmp(argv[1], "server"))
{
#ifdef AT_USING_SERVER
server_cli_parser();
#else
rt_kprintf("Not support AT server, please check your configure!\n");
#endif /* AT_USING_SERVER */
}
else if (!strcmp(argv[1], "client"))
{
#ifdef AT_USING_CLIENT
at_client_t client = RT_NULL;
if (argc == 2)
{
client_cli_parser(at_client_get_first());
}
else if (argc == 3)
{
client = at_client_get(argv[2]);
if (client == RT_NULL)
{
rt_kprintf("input AT client device name(%s) error.\n", argv[2]);
}
else
{
client_cli_parser(client);
}
}
#else
rt_kprintf("Not support AT client, please check your configure!\n");
#endif /* AT_USING_CLIENT */
}
else
{
rt_kprintf("Please input '<server|client [dev_name]>' \n");
}
at_cli_deinit();
}
MSH_CMD_EXPORT(at, RT-Thread AT component cli: at <server|client [dev_name]>);
#endif /* AT_USING_CLI */

View File

@@ -0,0 +1,918 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-03-30 chenyong first version
* 2018-04-12 chenyong add client implement
* 2018-08-17 chenyong multiple client support
*/
#include <at.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LOG_TAG "at.clnt"
#include <at_log.h>
#ifdef AT_USING_CLIENT
#define AT_RESP_END_OK "OK"
#define AT_RESP_END_ERROR "ERROR"
#define AT_RESP_END_FAIL "FAIL"
#define AT_END_CR_LF "\r\n"
static struct at_client at_client_table[AT_CLIENT_NUM_MAX] = { 0 };
extern rt_size_t at_vprintfln(rt_device_t device, const char *format, va_list args);
extern void at_print_raw_cmd(const char *type, const char *cmd, rt_size_t size);
extern const char *at_get_last_cmd(rt_size_t *cmd_size);
/**
* Create response object.
*
* @param buf_size the maximum response buffer size
* @param line_num the number of setting response lines
* = 0: the response data will auto return when received 'OK' or 'ERROR'
* != 0: the response data will return when received setting lines number data
* @param timeout the maximum response time
*
* @return != RT_NULL: response object
* = RT_NULL: no memory
*/
at_response_t at_create_resp(rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout)
{
at_response_t resp = RT_NULL;
resp = (at_response_t) rt_calloc(1, sizeof(struct at_response));
if (resp == RT_NULL)
{
LOG_E("AT create response object failed! No memory for response object!");
return RT_NULL;
}
resp->buf = (char *) rt_calloc(1, buf_size);
if (resp->buf == RT_NULL)
{
LOG_E("AT create response object failed! No memory for response buffer!");
rt_free(resp);
return RT_NULL;
}
resp->buf_size = buf_size;
resp->line_num = line_num;
resp->line_counts = 0;
resp->timeout = timeout;
return resp;
}
/**
* Delete and free response object.
*
* @param resp response object
*/
void at_delete_resp(at_response_t resp)
{
if (resp && resp->buf)
{
rt_free(resp->buf);
}
if (resp)
{
rt_free(resp);
resp = RT_NULL;
}
}
/**
* Set response object information
*
* @param resp response object
* @param buf_size the maximum response buffer size
* @param line_num the number of setting response lines
* = 0: the response data will auto return when received 'OK' or 'ERROR'
* != 0: the response data will return when received setting lines number data
* @param timeout the maximum response time
*
* @return != RT_NULL: response object
* = RT_NULL: no memory
*/
at_response_t at_resp_set_info(at_response_t resp, rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout)
{
RT_ASSERT(resp);
if (resp->buf_size != buf_size)
{
resp->buf_size = buf_size;
resp->buf = (char *) rt_realloc(resp->buf, buf_size);
if (!resp->buf)
{
LOG_D("No memory for realloc response buffer size(%d).", buf_size);
return RT_NULL;
}
}
resp->line_num = line_num;
resp->timeout = timeout;
return resp;
}
/**
* Get one line AT response buffer by line number.
*
* @param resp response object
* @param resp_line line number, start from '1'
*
* @return != RT_NULL: response line buffer
* = RT_NULL: input response line error
*/
const char *at_resp_get_line(at_response_t resp, rt_size_t resp_line)
{
char *resp_buf = resp->buf;
char *resp_line_buf = RT_NULL;
rt_size_t line_num = 1;
RT_ASSERT(resp);
if (resp_line > resp->line_counts || resp_line <= 0)
{
LOG_E("AT response get line failed! Input response line(%d) error!", resp_line);
return RT_NULL;
}
for (line_num = 1; line_num <= resp->line_counts; line_num++)
{
if (resp_line == line_num)
{
resp_line_buf = resp_buf;
return resp_line_buf;
}
resp_buf += strlen(resp_buf) + 1;
}
return RT_NULL;
}
/**
* Get one line AT response buffer by keyword
*
* @param resp response object
* @param keyword query keyword
*
* @return != RT_NULL: response line buffer
* = RT_NULL: no matching data
*/
const char *at_resp_get_line_by_kw(at_response_t resp, const char *keyword)
{
char *resp_buf = resp->buf;
char *resp_line_buf = RT_NULL;
rt_size_t line_num = 1;
RT_ASSERT(resp);
RT_ASSERT(keyword);
for (line_num = 1; line_num <= resp->line_counts; line_num++)
{
if (strstr(resp_buf, keyword))
{
resp_line_buf = resp_buf;
return resp_line_buf;
}
resp_buf += strlen(resp_buf) + 1;
}
return RT_NULL;
}
/**
* Get and parse AT response buffer arguments by line number.
*
* @param resp response object
* @param resp_line line number, start from '1'
* @param resp_expr response buffer expression
*
* @return -1 : input response line number error or get line buffer error
* 0 : parsed without match
* >0 : the number of arguments successfully parsed
*/
int at_resp_parse_line_args(at_response_t resp, rt_size_t resp_line, const char *resp_expr, ...)
{
va_list args;
int resp_args_num = 0;
const char *resp_line_buf = RT_NULL;
RT_ASSERT(resp);
RT_ASSERT(resp_expr);
if ((resp_line_buf = at_resp_get_line(resp, resp_line)) == RT_NULL)
{
return -1;
}
va_start(args, resp_expr);
resp_args_num = vsscanf(resp_line_buf, resp_expr, args);
va_end(args);
return resp_args_num;
}
/**
* Get and parse AT response buffer arguments by keyword.
*
* @param resp response object
* @param keyword query keyword
* @param resp_expr response buffer expression
*
* @return -1 : input keyword error or get line buffer error
* 0 : parsed without match
* >0 : the number of arguments successfully parsed
*/
int at_resp_parse_line_args_by_kw(at_response_t resp, const char *keyword, const char *resp_expr, ...)
{
va_list args;
int resp_args_num = 0;
const char *resp_line_buf = RT_NULL;
RT_ASSERT(resp);
RT_ASSERT(resp_expr);
if ((resp_line_buf = at_resp_get_line_by_kw(resp, keyword)) == RT_NULL)
{
return -1;
}
va_start(args, resp_expr);
resp_args_num = vsscanf(resp_line_buf, resp_expr, args);
va_end(args);
return resp_args_num;
}
/**
* Send commands to AT server and wait response.
*
* @param client current AT client object
* @param resp AT response object, using RT_NULL when you don't care response
* @param cmd_expr AT commands expression
*
* @return 0 : success
* -1 : response status error
* -2 : wait timeout
*/
int at_obj_exec_cmd(at_client_t client, at_response_t resp, const char *cmd_expr, ...)
{
va_list args;
rt_size_t cmd_size = 0;
rt_err_t result = RT_EOK;
const char *cmd = RT_NULL;
RT_ASSERT(cmd_expr);
if (client == RT_NULL)
{
LOG_E("input AT Client object is NULL, please create or get AT Client object!");
return -RT_ERROR;
}
rt_mutex_take(client->lock, RT_WAITING_FOREVER);
client->resp_status = AT_RESP_OK;
client->resp = resp;
va_start(args, cmd_expr);
at_vprintfln(client->device, cmd_expr, args);
va_end(args);
if (resp != RT_NULL)
{
resp->line_counts = 0;
if (rt_sem_take(client->resp_notice, resp->timeout) != RT_EOK)
{
cmd = at_get_last_cmd(&cmd_size);
LOG_E("execute command (%.*s) timeout (%d ticks)!", cmd_size, cmd, resp->timeout);
client->resp_status = AT_RESP_TIMEOUT;
result = -RT_ETIMEOUT;
goto __exit;
}
if (client->resp_status != AT_RESP_OK)
{
cmd = at_get_last_cmd(&cmd_size);
LOG_E("execute command (%.*s) failed!", cmd_size, cmd);
result = -RT_ERROR;
goto __exit;
}
}
__exit:
client->resp = RT_NULL;
rt_mutex_release(client->lock);
return result;
}
/**
* Waiting for connection to external devices.
*
* @param client current AT client object
* @param timeout millisecond for timeout
*
* @return 0 : success
* -2 : timeout
* -5 : no memory
*/
int at_client_obj_wait_connect(at_client_t client, rt_uint32_t timeout)
{
rt_err_t result = RT_EOK;
at_response_t resp = RT_NULL;
rt_tick_t start_time = 0;
if (client == RT_NULL)
{
LOG_E("input AT Client object is NULL, please create or get AT Client object!");
return -RT_ERROR;
}
resp = at_create_resp(16, 0, rt_tick_from_millisecond(500));
if (resp == RT_NULL)
{
LOG_E("No memory for response object!");
return -RT_ENOMEM;
}
rt_mutex_take(client->lock, RT_WAITING_FOREVER);
client->resp = resp;
start_time = rt_tick_get();
while (1)
{
/* Check whether it is timeout */
if (rt_tick_get() - start_time > rt_tick_from_millisecond(timeout))
{
LOG_E("wait connect timeout (%d millisecond)!", timeout);
result = -RT_ETIMEOUT;
break;
}
/* Check whether it is already connected */
resp->line_counts = 0;
rt_device_write(client->device, 0, "AT\r\n", 4);
if (rt_sem_take(client->resp_notice, resp->timeout) != RT_EOK)
continue;
else
break;
}
at_delete_resp(resp);
client->resp = RT_NULL;
rt_mutex_release(client->lock);
return result;
}
/**
* Send data to AT server, send data don't have end sign(eg: \r\n).
*
* @param client current AT client object
* @param buf send data buffer
* @param size send fixed data size
*
* @return >0: send data size
* =0: send failed
*/
rt_size_t at_client_obj_send(at_client_t client, const char *buf, rt_size_t size)
{
RT_ASSERT(buf);
if (client == RT_NULL)
{
LOG_E("input AT Client object is NULL, please create or get AT Client object!");
return 0;
}
#ifdef AT_PRINT_RAW_CMD
at_print_raw_cmd("send", buf, size);
#endif
return rt_device_write(client->device, 0, buf, size);
}
static rt_err_t at_client_getchar(at_client_t client, char *ch, rt_int32_t timeout)
{
rt_err_t result = RT_EOK;
while (rt_device_read(client->device, 0, ch, 1) == 0)
{
rt_sem_control(client->rx_notice, RT_IPC_CMD_RESET, RT_NULL);
result = rt_sem_take(client->rx_notice, rt_tick_from_millisecond(timeout));
if (result != RT_EOK)
{
return result;
}
}
return RT_EOK;
}
/**
* AT client receive fixed-length data.
*
* @param client current AT client object
* @param buf receive data buffer
* @param size receive fixed data size
* @param timeout receive data timeout (ms)
*
* @note this function can only be used in execution function of URC data
*
* @return >0: receive data size
* =0: receive failed
*/
rt_size_t at_client_obj_recv(at_client_t client, char *buf, rt_size_t size, rt_int32_t timeout)
{
rt_size_t read_idx = 0;
rt_err_t result = RT_EOK;
char ch;
RT_ASSERT(buf);
if (client == RT_NULL)
{
LOG_E("input AT Client object is NULL, please create or get AT Client object!");
return 0;
}
while (1)
{
if (read_idx < size)
{
result = at_client_getchar(client, &ch, timeout);
if (result != RT_EOK)
{
LOG_E("AT Client receive failed, uart device get data error(%d)", result);
return 0;
}
buf[read_idx++] = ch;
}
else
{
break;
}
}
#ifdef AT_PRINT_RAW_CMD
at_print_raw_cmd("urc_recv", buf, size);
#endif
return read_idx;
}
/**
* AT client set end sign.
*
* @param client current AT client object
* @param ch the end sign, can not be used when it is '\0'
*/
void at_obj_set_end_sign(at_client_t client, char ch)
{
if (client == RT_NULL)
{
LOG_E("input AT Client object is NULL, please create or get AT Client object!");
return;
}
client->end_sign = ch;
}
/**
* set URC(Unsolicited Result Code) table
*
* @param client current AT client object
* @param table URC table
* @param size table size
*/
void at_obj_set_urc_table(at_client_t client, const struct at_urc *urc_table, rt_size_t table_sz)
{
rt_size_t idx;
if (client == RT_NULL)
{
LOG_E("input AT Client object is NULL, please create or get AT Client object!");
return;
}
for (idx = 0; idx < table_sz; idx++)
{
RT_ASSERT(urc_table[idx].cmd_prefix);
RT_ASSERT(urc_table[idx].cmd_suffix);
}
client->urc_table = urc_table;
client->urc_table_size = table_sz;
}
/**
* get AT client object by AT device name.
*
* @dev_name AT client device name
*
* @return AT client object
*/
at_client_t at_client_get(const char *dev_name)
{
int idx = 0;
RT_ASSERT(dev_name);
for (idx = 0; idx < AT_CLIENT_NUM_MAX; idx++)
{
if (rt_strcmp(at_client_table[idx].device->parent.name, dev_name) == 0)
{
return &at_client_table[idx];
}
}
return RT_NULL;
}
/**
* get first AT client object in the table.
*
* @return AT client object
*/
at_client_t at_client_get_first(void)
{
if (at_client_table[0].device == RT_NULL)
{
return RT_NULL;
}
return &at_client_table[0];
}
static const struct at_urc *get_urc_obj(at_client_t client)
{
rt_size_t i, prefix_len, suffix_len;
rt_size_t buf_sz;
char *buffer = RT_NULL;
if (client->urc_table == RT_NULL)
{
return RT_NULL;
}
buffer = client->recv_buffer;
buf_sz = client->cur_recv_len;
for (i = 0; i < client->urc_table_size; i++)
{
prefix_len = strlen(client->urc_table[i].cmd_prefix);
suffix_len = strlen(client->urc_table[i].cmd_suffix);
if (buf_sz < prefix_len + suffix_len)
{
continue;
}
if ((prefix_len ? !strncmp(buffer, client->urc_table[i].cmd_prefix, prefix_len) : 1)
&& (suffix_len ? !strncmp(buffer + buf_sz - suffix_len, client->urc_table[i].cmd_suffix, suffix_len) : 1))
{
return &client->urc_table[i];
}
}
return RT_NULL;
}
static int at_recv_readline(at_client_t client)
{
rt_size_t read_len = 0;
char ch = 0, last_ch = 0;
rt_bool_t is_full = RT_FALSE;
memset(client->recv_buffer, 0x00, client->recv_bufsz);
client->cur_recv_len = 0;
while (1)
{
at_client_getchar(client, &ch, RT_WAITING_FOREVER);
if (read_len < client->recv_bufsz)
{
client->recv_buffer[read_len++] = ch;
client->cur_recv_len = read_len;
}
else
{
is_full = RT_TRUE;
}
/* is newline or URC data */
if ((ch == '\n' && last_ch == '\r') || (client->end_sign != 0 && ch == client->end_sign)
|| get_urc_obj(client))
{
if (is_full)
{
LOG_E("read line failed. The line data length is out of buffer size(%d)!", client->recv_bufsz);
memset(client->recv_buffer, 0x00, client->recv_bufsz);
client->cur_recv_len = 0;
return -RT_EFULL;
}
break;
}
last_ch = ch;
}
#ifdef AT_PRINT_RAW_CMD
at_print_raw_cmd("recvline", client->recv_buffer, read_len);
#endif
return read_len;
}
static void client_parser(at_client_t client)
{
int resp_buf_len = 0;
const struct at_urc *urc;
rt_size_t line_counts = 0;
while(1)
{
if (at_recv_readline(client) > 0)
{
if ((urc = get_urc_obj(client)) != RT_NULL)
{
/* current receive is request, try to execute related operations */
if (urc->func != RT_NULL)
{
urc->func(client->recv_buffer, client->cur_recv_len);
}
}
else if (client->resp != RT_NULL)
{
/* current receive is response */
client->recv_buffer[client->cur_recv_len - 1] = '\0';
if (resp_buf_len + client->cur_recv_len < client->resp->buf_size)
{
/* copy response lines, separated by '\0' */
memcpy(client->resp->buf + resp_buf_len, client->recv_buffer, client->cur_recv_len);
resp_buf_len += client->cur_recv_len;
line_counts++;
}
else
{
client->resp_status = AT_RESP_BUFF_FULL;
LOG_E("Read response buffer failed. The Response buffer size is out of buffer size(%d)!", client->resp->buf_size);
}
/* check response result */
if (memcmp(client->recv_buffer, AT_RESP_END_OK, strlen(AT_RESP_END_OK)) == 0
&& client->resp->line_num == 0)
{
/* get the end data by response result, return response state END_OK. */
client->resp_status = AT_RESP_OK;
}
else if (strstr(client->recv_buffer, AT_RESP_END_ERROR)
|| (memcmp(client->recv_buffer, AT_RESP_END_FAIL, strlen(AT_RESP_END_FAIL)) == 0))
{
client->resp_status = AT_RESP_ERROR;
}
else if (line_counts == client->resp->line_num && client->resp->line_num)
{
/* get the end data by response line, return response state END_OK.*/
client->resp_status = AT_RESP_OK;
}
else
{
continue;
}
client->resp->line_counts = line_counts;
client->resp = RT_NULL;
rt_sem_release(client->resp_notice);
resp_buf_len = 0, line_counts = 0;
}
else
{
// log_d("unrecognized line: %.*s", client->cur_recv_len, client->recv_buffer);
}
}
}
}
static rt_err_t at_client_rx_ind(rt_device_t dev, rt_size_t size)
{
int idx = 0;
for (idx = 0; idx < AT_CLIENT_NUM_MAX; idx++)
{
if (at_client_table[idx].device == dev && size > 0)
{
rt_sem_release(at_client_table[idx].rx_notice);
}
}
return RT_EOK;
}
/* initialize the client object parameters */
static int at_client_para_init(at_client_t client)
{
#define AT_CLIENT_LOCK_NAME "at_c"
#define AT_CLIENT_SEM_NAME "at_cs"
#define AT_CLIENT_RESP_NAME "at_cr"
#define AT_CLIENT_THREAD_NAME "at_clnt"
int result = RT_EOK;
static int at_client_num = 0;
char name[RT_NAME_MAX];
client->status = AT_STATUS_UNINITIALIZED;
client->cur_recv_len = 0;
client->recv_buffer = (char *) rt_calloc(1, client->recv_bufsz);
if (client->recv_buffer == RT_NULL)
{
LOG_E("AT client initialize failed! No memory for receive buffer.");
result = -RT_ENOMEM;
goto __exit;
}
rt_snprintf(name, RT_NAME_MAX, "%s%d", AT_CLIENT_LOCK_NAME, at_client_num);
client->lock = rt_mutex_create(name, RT_IPC_FLAG_FIFO);
if (client->lock == RT_NULL)
{
LOG_E("AT client initialize failed! at_client_recv_lock create failed!");
result = -RT_ENOMEM;
goto __exit;
}
rt_snprintf(name, RT_NAME_MAX, "%s%d", AT_CLIENT_SEM_NAME, at_client_num);
client->rx_notice = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO);
if (client->rx_notice == RT_NULL)
{
LOG_E("AT client initialize failed! at_client_notice semaphore create failed!");
result = -RT_ENOMEM;
goto __exit;
}
rt_snprintf(name, RT_NAME_MAX, "%s%d", AT_CLIENT_RESP_NAME, at_client_num);
client->resp_notice = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO);
if (client->resp_notice == RT_NULL)
{
LOG_E("AT client initialize failed! at_client_resp semaphore create failed!");
result = -RT_ENOMEM;
goto __exit;
}
client->urc_table = RT_NULL;
client->urc_table_size = 0;
rt_snprintf(name, RT_NAME_MAX, "%s%d", AT_CLIENT_THREAD_NAME, at_client_num);
client->parser = rt_thread_create(name,
(void (*)(void *parameter))client_parser,
client,
1024 + 512,
RT_THREAD_PRIORITY_MAX / 3 - 1,
5);
if (client->parser == RT_NULL)
{
result = -RT_ENOMEM;
goto __exit;
}
__exit:
if (result != RT_EOK)
{
if (client->lock)
{
rt_mutex_delete(client->lock);
}
if (client->rx_notice)
{
rt_sem_delete(client->rx_notice);
}
if (client->resp_notice)
{
rt_sem_delete(client->resp_notice);
}
if (client->device)
{
rt_device_close(client->device);
}
if (client->recv_buffer)
{
rt_free(client->recv_buffer);
}
rt_memset(client, 0x00, sizeof(struct at_client));
}
else
{
at_client_num++;
}
return result;
}
/**
* AT client initialize.
*
* @param dev_name AT client device name
* @param recv_bufsz the maximum number of receive buffer length
*
* @return 0 : initialize success
* -1 : initialize failed
* -5 : no memory
*/
int at_client_init(const char *dev_name, rt_size_t recv_bufsz)
{
int idx = 0;
int result = RT_EOK;
rt_err_t open_result = RT_EOK;
at_client_t client = RT_NULL;
RT_ASSERT(dev_name);
RT_ASSERT(recv_bufsz > 0);
for (idx = 0; idx < AT_CLIENT_NUM_MAX && at_client_table[idx].device; idx++);
if (idx >= AT_CLIENT_NUM_MAX)
{
LOG_E("AT client initialize failed! Check the maximum number(%d) of AT client.", AT_CLIENT_NUM_MAX);
result = -RT_EFULL;
goto __exit;
}
client = &at_client_table[idx];
client->recv_bufsz = recv_bufsz;
/* find and open command device */
client->device = rt_device_find(dev_name);
if (client->device)
{
RT_ASSERT(client->device->type == RT_Device_Class_Char);
/* using DMA mode first */
open_result = rt_device_open(client->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX);
/* using interrupt mode when DMA mode not supported */
if (open_result == -RT_EIO)
{
open_result = rt_device_open(client->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
}
RT_ASSERT(open_result == RT_EOK);
rt_device_set_rx_indicate(client->device, at_client_rx_ind);
}
else
{
LOG_E("AT client initialize failed! Not find the device(%s).", dev_name);
result = -RT_ERROR;
goto __exit;
}
result = at_client_para_init(client);
if (result != RT_EOK)
{
goto __exit;
}
__exit:
if (result == RT_EOK)
{
client->status = AT_STATUS_INITIALIZED;
rt_thread_startup(client->parser);
LOG_I("AT client(V%s) on device %s initialize success.", AT_SW_VERSION, dev_name);
}
else
{
LOG_E("AT client(V%s) on device %s initialize failed(%d).", AT_SW_VERSION, dev_name, result);
}
return result;
}
#endif /* AT_USING_CLIENT */

View File

@@ -0,0 +1,560 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-03-30 chenyong first version
* 2018-04-14 chenyong modify parse arguments
*/
#include <at.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <rthw.h>
#define LOG_TAG "at.svr"
#include <at_log.h>
#ifdef AT_USING_SERVER
#define AT_CMD_CHAR_0 '0'
#define AT_CMD_CHAR_9 '9'
#define AT_CMD_QUESTION_MARK '?'
#define AT_CMD_EQUAL_MARK '='
#define AT_CMD_L_SQ_BRACKET '['
#define AT_CMD_R_SQ_BRACKET ']'
#define AT_CMD_L_ANGLE_BRACKET '<'
#define AT_CMD_R_ANGLE_BRACKET '>'
#define AT_CMD_COMMA_MARK ','
#define AT_CMD_SEMICOLON ';'
#define AT_CMD_CR '\r'
#define AT_CMD_LF '\n'
static at_server_t at_server_local = RT_NULL;
static at_cmd_t cmd_table = RT_NULL;
static rt_size_t cmd_num;
extern void at_vprintf(rt_device_t device, const char *format, va_list args);
extern void at_vprintfln(rt_device_t device, const char *format, va_list args);
/**
* AT server send data to AT device
*
* @param format the input format
*/
void at_server_printf(const char *format, ...)
{
va_list args;
va_start(args, format);
at_vprintf(at_server_local->device, format, args);
va_end(args);
}
/**
* AT server send data and newline to AT device
*
* @param format the input format
*/
void at_server_printfln(const char *format, ...)
{
va_list args;
va_start(args, format);
at_vprintfln(at_server_local->device, format, args);
va_end(args);
}
/**
* AT server request arguments parse arguments
*
* @param req_args request arguments
* @param req_expr request expression
*
* @return -1 : parse arguments failed
* 0 : parse without match
* >0 : The number of arguments successfully parsed
*/
int at_req_parse_args(const char *req_args, const char *req_expr, ...)
{
va_list args;
int req_args_num = 0;
RT_ASSERT(req_args);
RT_ASSERT(req_expr);
va_start(args, req_expr);
req_args_num = vsscanf(req_args, req_expr, args);
va_end(args);
return req_args_num;
}
/**
* AT server send command execute result to AT device
*
* @param result AT command execute result
*/
void at_server_print_result(at_result_t result)
{
switch (result)
{
case AT_RESULT_OK:
at_server_printfln("");
at_server_printfln("OK");
break;
case AT_RESULT_FAILE:
at_server_printfln("");
at_server_printfln("ERROR");
break;
case AT_RESULT_NULL:
break;
case AT_RESULT_CMD_ERR:
at_server_printfln("ERR CMD MATCH FAILED!");
at_server_print_result(AT_RESULT_FAILE);
break;
case AT_RESULT_CHECK_FAILE:
at_server_printfln("ERR CHECK ARGS FORMAT FAILED!");
at_server_print_result(AT_RESULT_FAILE);
break;
case AT_RESULT_PARSE_FAILE:
at_server_printfln("ERR PARSE ARGS FAILED!");
at_server_print_result(AT_RESULT_FAILE);
break;
default:
break;
}
}
/**
* AT server print all commands to AT device
*/
void rt_at_server_print_all_cmd(void)
{
rt_size_t i = 0;
at_server_printfln("Commands list : ");
for (i = 0; i < cmd_num; i++)
{
at_server_printf("%s", cmd_table[i].name);
if (cmd_table[i].args_expr)
{
at_server_printfln("%s", cmd_table[i].args_expr);
}
else
{
at_server_printf("%c%c", AT_CMD_CR, AT_CMD_LF);
}
}
}
at_server_t at_get_server(void)
{
RT_ASSERT(at_server_local);
RT_ASSERT(at_server_local->status != AT_STATUS_UNINITIALIZED);
return at_server_local;
}
static rt_err_t at_check_args(const char *args, const char *args_format)
{
rt_size_t left_sq_bracket_num = 0, right_sq_bracket_num = 0;
rt_size_t left_angle_bracket_num = 0, right_angle_bracket_num = 0;
rt_size_t comma_mark_num = 0;
rt_size_t i = 0;
RT_ASSERT(args);
RT_ASSERT(args_format);
for (i = 0; i < strlen(args_format); i++)
{
switch (args_format[i])
{
case AT_CMD_L_SQ_BRACKET:
left_sq_bracket_num++;
break;
case AT_CMD_R_SQ_BRACKET:
right_sq_bracket_num++;
break;
case AT_CMD_L_ANGLE_BRACKET:
left_angle_bracket_num++;
break;
case AT_CMD_R_ANGLE_BRACKET:
right_angle_bracket_num++;
break;
default:
break;
}
}
if (left_sq_bracket_num != right_sq_bracket_num || left_angle_bracket_num != right_angle_bracket_num
|| left_sq_bracket_num > left_angle_bracket_num)
{
return -RT_ERROR;
}
for (i = 0; i < strlen(args); i++)
{
if (args[i] == AT_CMD_COMMA_MARK)
{
comma_mark_num++;
}
}
if ((comma_mark_num + 1 < left_angle_bracket_num - left_sq_bracket_num)
|| comma_mark_num + 1 > left_angle_bracket_num)
{
return -RT_ERROR;
}
return RT_EOK;
}
static rt_err_t at_cmd_process(at_cmd_t cmd, const char *cmd_args)
{
at_result_t result = AT_RESULT_OK;
RT_ASSERT(cmd);
RT_ASSERT(cmd_args);
if (cmd_args[0] == AT_CMD_EQUAL_MARK && cmd_args[1] == AT_CMD_QUESTION_MARK && cmd_args[2] == AT_CMD_CR)
{
if (cmd->test == RT_NULL)
{
at_server_print_result(AT_RESULT_CMD_ERR);
return -RT_ERROR;
}
result = cmd->test();
at_server_print_result(result);
}
else if (cmd_args[0] == AT_CMD_QUESTION_MARK && cmd_args[1] == AT_CMD_CR)
{
if (cmd->query == RT_NULL)
{
at_server_print_result(AT_RESULT_CMD_ERR);
return -RT_ERROR;
}
result = cmd->query();
at_server_print_result(result);
}
else if (cmd_args[0] == AT_CMD_EQUAL_MARK
|| (cmd_args[0] >= AT_CMD_CHAR_0 && cmd_args[0] <= AT_CMD_CHAR_9 && cmd_args[1] == AT_CMD_CR))
{
if (cmd->setup == RT_NULL)
{
at_server_print_result(AT_RESULT_CMD_ERR);
return -RT_ERROR;
}
if(at_check_args(cmd_args, cmd->args_expr) < 0)
{
at_server_print_result(AT_RESULT_CHECK_FAILE);
return -RT_ERROR;
}
result = cmd->setup(cmd_args);
at_server_print_result(result);
}
else if (cmd_args[0] == AT_CMD_CR)
{
if (cmd->exec == RT_NULL)
{
at_server_print_result(AT_RESULT_CMD_ERR);
return -RT_ERROR;
}
result = cmd->exec();
at_server_print_result(result);
}
else
{
return -RT_ERROR;
}
return RT_EOK;
}
static at_cmd_t at_find_cmd(const char *cmd)
{
rt_size_t i = 0;
RT_ASSERT(cmd_table);
for (i = 0; i < cmd_num; i++)
{
if (!strcasecmp(cmd, cmd_table[i].name))
{
return &cmd_table[i];
}
}
return RT_NULL;
}
static rt_err_t at_cmd_get_name(const char *cmd_buffer, char *cmd_name)
{
rt_size_t cmd_name_len = 0, i = 0;
RT_ASSERT(cmd_name);
RT_ASSERT(cmd_buffer);
for (i = 0; i < strlen(cmd_buffer) + 1; i++)
{
if (*(cmd_buffer + i) == AT_CMD_QUESTION_MARK || *(cmd_buffer + i) == AT_CMD_EQUAL_MARK
|| *(cmd_buffer + i) == AT_CMD_CR
|| (*(cmd_buffer + i) >= AT_CMD_CHAR_0 && *(cmd_buffer + i) <= AT_CMD_CHAR_9))
{
cmd_name_len = i;
memcpy(cmd_name, cmd_buffer, cmd_name_len);
*(cmd_name + cmd_name_len) = '\0';
return RT_EOK;
}
}
return -RT_ERROR;
}
static char at_server_gerchar(void)
{
char ch;
while (rt_device_read(at_server_local->device, 0, &ch, 1) == 0)
{
rt_sem_control(at_server_local->rx_notice, RT_IPC_CMD_RESET, RT_NULL);
rt_sem_take(at_server_local->rx_notice, RT_WAITING_FOREVER);
}
return ch;
}
static void server_parser(at_server_t server)
{
#define ESC_KEY 0x1B
#define BACKSPACE_KEY 0x08
#define DELECT_KEY 0x7F
char cur_cmd_name[AT_CMD_NAME_LEN] = { 0 };
at_cmd_t cur_cmd = RT_NULL;
char *cur_cmd_args = RT_NULL, ch, last_ch;
RT_ASSERT(server);
RT_ASSERT(server->status != AT_STATUS_UNINITIALIZED);
while (ESC_KEY != (ch = server->get_char()))
{
if (server->echo_mode)
{
if (ch == AT_CMD_CR || (ch == AT_CMD_LF && last_ch != AT_CMD_CR))
{
at_server_printf("%c%c", AT_CMD_CR, AT_CMD_LF);
}
else if (ch == BACKSPACE_KEY || ch == DELECT_KEY)
{
if (server->cur_recv_len)
{
server->recv_buffer[--server->cur_recv_len] = 0;
at_server_printf("\b \b");
}
continue;
}
else
{
at_server_printf("%c", ch);
}
}
server->recv_buffer[server->cur_recv_len++] = ch;
last_ch = ch;
if(!strstr(server->recv_buffer, server->end_mark))
{
continue;
}
if (at_cmd_get_name(server->recv_buffer, cur_cmd_name) < 0)
{
at_server_print_result(AT_RESULT_CMD_ERR);
goto __retry;
}
cur_cmd = at_find_cmd(cur_cmd_name);
if (!cur_cmd)
{
at_server_print_result(AT_RESULT_CMD_ERR);
goto __retry;
}
cur_cmd_args = server->recv_buffer + strlen(cur_cmd_name);
if (at_cmd_process(cur_cmd, cur_cmd_args) < 0)
{
goto __retry;
}
__retry:
memset(server->recv_buffer, 0x00, AT_SERVER_RECV_BUFF_LEN);
server->cur_recv_len = 0;
}
}
static rt_err_t at_rx_ind(rt_device_t dev, rt_size_t size)
{
if (size > 0)
{
rt_sem_release(at_server_local->rx_notice);
}
return RT_EOK;
}
#if defined(__ICCARM__) || defined(__ICCRX__) /* for IAR compiler */
#pragma section="RtAtCmdTab"
#endif
int at_server_init(void)
{
rt_err_t result = RT_EOK;
rt_err_t open_result = RT_EOK;
if (at_server_local)
{
return result;
}
/* initialize the AT commands table.*/
#if defined(__CC_ARM) /* ARM C Compiler */
extern const int RtAtCmdTab$$Base;
extern const int RtAtCmdTab$$Limit;
cmd_table = (at_cmd_t)&RtAtCmdTab$$Base;
cmd_num = (at_cmd_t)&RtAtCmdTab$$Limit - cmd_table;
#elif defined (__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */
cmd_table = (at_cmd_t)__section_begin("RtAtCmdTab");
cmd_num = (at_cmd_t)__section_end("RtAtCmdTab") - cmd_table;
#elif defined (__GNUC__) /* for GCC Compiler */
extern const int __rtatcmdtab_start;
extern const int __rtatcmdtab_end;
cmd_table = (at_cmd_t)&__rtatcmdtab_start;
cmd_num = (at_cmd_t) &__rtatcmdtab_end - cmd_table;
#endif /* defined(__CC_ARM) */
at_server_local = (at_server_t) rt_calloc(1, sizeof(struct at_server));
if (!at_server_local)
{
result = -RT_ENOMEM;
LOG_E("AT server session initialize failed! No memory for at_server structure !");
goto __exit;
}
at_server_local->echo_mode = 1;
at_server_local->status = AT_STATUS_UNINITIALIZED;
memset(at_server_local->recv_buffer, 0x00, AT_SERVER_RECV_BUFF_LEN);
at_server_local->cur_recv_len = 0;
at_server_local->rx_notice = rt_sem_create("at_svr", 0, RT_IPC_FLAG_FIFO);
if (!at_server_local->rx_notice)
{
LOG_E("AT server session initialize failed! at_rx_notice semaphore create failed!");
result = -RT_ENOMEM;
goto __exit;
}
/* Find and open command device */
at_server_local->device = rt_device_find(AT_SERVER_DEVICE);
if (at_server_local->device)
{
RT_ASSERT(at_server_local->device->type == RT_Device_Class_Char);
/* using DMA mode first */
open_result = rt_device_open(at_server_local->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX);
/* using interrupt mode when DMA mode not supported */
if (open_result == -RT_EIO)
{
open_result = rt_device_open(at_server_local->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
}
RT_ASSERT(open_result == RT_EOK);
rt_device_set_rx_indicate(at_server_local->device, at_rx_ind);
}
else
{
LOG_E("AT device initialize failed! Not find the device : %s.", AT_SERVER_DEVICE);
result = -RT_ERROR;
goto __exit;
}
at_server_local->get_char = at_server_gerchar;
memcpy(at_server_local->end_mark, AT_CMD_END_MARK, sizeof(AT_CMD_END_MARK));
at_server_local->parser_entry = server_parser;
at_server_local->parser = rt_thread_create("at_svr",
(void (*)(void *parameter))server_parser,
at_server_local,
2 * 1024,
RT_THREAD_PRIORITY_MAX / 3 - 1,
5);
if (at_server_local->parser == RT_NULL)
{
result = -RT_ENOMEM;
goto __exit;
}
__exit:
if (!result)
{
at_server_local->status = AT_STATUS_INITIALIZED;
rt_thread_startup(at_server_local->parser);
LOG_I("RT-Thread AT server (V%s) initialize success.", AT_SW_VERSION);
}
else
{
if (at_server_local)
{
rt_free(at_server_local);
}
LOG_E("RT-Thread AT server (V%s) initialize failed(%d).", AT_SW_VERSION, result);
}
return result;
}
INIT_COMPONENT_EXPORT(at_server_init);
RT_WEAK void at_port_reset(void)
{
LOG_E("The reset for AT server is not implement.");
}
RT_WEAK void at_port_factory_reset(void)
{
LOG_E("The factory reset for AT server is not implement.");
}
#endif /* AT_USING_SERVER */

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-04-14 chenyong first version
*/
#include <at.h>
#include <stdlib.h>
#include <stdio.h>
static char send_buf[AT_CMD_MAX_LEN];
static rt_size_t last_cmd_len = 0;
/**
* dump hex format data to console device
*
* @param name name for hex object, it will show on log header
* @param buf hex buffer
* @param size buffer size
*/
void at_print_raw_cmd(const char *name, const char *buf, rt_size_t size)
{
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
#define WIDTH_SIZE 32
rt_size_t i, j;
for (i = 0; i < size; i += WIDTH_SIZE)
{
rt_kprintf("[D/AT] %s: %04X-%04X: ", name, i, i + WIDTH_SIZE);
for (j = 0; j < WIDTH_SIZE; j++)
{
if (i + j < size)
{
rt_kprintf("%02X ", buf[i + j]);
}
else
{
rt_kprintf(" ");
}
if ((j + 1) % 8 == 0)
{
rt_kprintf(" ");
}
}
rt_kprintf(" ");
for (j = 0; j < WIDTH_SIZE; j++)
{
if (i + j < size)
{
rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
}
}
rt_kprintf("\n");
}
}
const char *at_get_last_cmd(rt_size_t *cmd_size)
{
*cmd_size = last_cmd_len;
return send_buf;
}
rt_size_t at_vprintf(rt_device_t device, const char *format, va_list args)
{
last_cmd_len = vsnprintf(send_buf, sizeof(send_buf), format, args);
#ifdef AT_PRINT_RAW_CMD
at_print_raw_cmd("send", send_buf, last_cmd_len);
#endif
return rt_device_write(device, 0, send_buf, last_cmd_len);
}
rt_size_t at_vprintfln(rt_device_t device, const char *format, va_list args)
{
rt_size_t len;
len = at_vprintf(device, format, args);
rt_device_write(device, 0, "\r\n", 2);
return len + 2;
}