提交 8b887e76 编写于 作者: Lawlieta's avatar Lawlieta

[net][at] Add AT commands component

上级 2316dc3f
......@@ -19,9 +19,9 @@ config RT_USING_SAL
config SAL_USING_AT
bool "Support AT Commands stack"
default y if RT_USING_AT_SOCKET
default y if AT_USING_SOCKET
default n
depends on RT_USING_AT_SOCKET
depends on AT_USING_SOCKET
endmenu
config SAL_USING_POSIX
......@@ -369,6 +369,8 @@ endmenu
source "$RTT_DIR/components/net/freemodbus/Kconfig"
source "$RTT_DIR/components/net/at/Kconfig"
if RT_USING_LWIP
config LWIP_USING_DHCPD
......
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_DEVICE
string "Client device name"
default "uart2"
config AT_CLIENT_RECV_BUFF_LEN
int "The maximum length of client data accepted"
default 512
config AT_USING_SOCKET
bool "Provide similar BSD Socket API by AT"
default n
endif
config AT_USING_CLI
bool "Enable 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
endmenu
from building import *
cwd = GetCurrentDir()
path = [cwd + '/include']
src = Split('''
src/at_cli.c
src/at_utils.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 : at_socket.h
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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>
#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 (*connect)(int socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client);
int (*close)(int socket);
int (*send)(int socket, const char *buff, size_t bfsz, enum at_socket_type type);
int (*domain_resolve)(const char *name, char ip[16]);
void (*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_scoket_device_register(const struct at_device_ops *ops);
#ifndef RT_USING_SAL
#define socket(domain, type, protocol) at_socket(domain, type, protocol)
#define closescoket(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 */
#endif /* AT_SOCKET_H__ */
/*
* File : at.h
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-03-30 chenyong first version
*/
#ifndef __AT_H__
#define __AT_H__
#include <rtthread.h>
#define AT_SW_VERSION "0.2.1"
#define DBG_ENABLE
#define DBG_SECTION_NAME "AT"
#ifdef AT_DEBUG
#define DBG_LEVEL DBG_LOG
#else
#define DBG_LEVEL DBG_INFO
#endif /* AT_DEBUG */
#define DBG_COLOR
#include <rtdbg.h>
#define AT_CMD_NAME_LEN 16
#define AT_END_MARK_LEN 4
#ifndef AT_CMD_MAX_LEN
#define AT_CMD_MAX_LEN 128
#endif
/* client receive idle timeout, client will wait this timeout when send data, unit: ms */
#ifndef AT_CLIENT_RECV_IDEL
#define AT_CLIENT_RECV_IDEL 50
#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_CLIENT_RECV_BUFF_LEN
#define AT_CLIENT_RECV_BUFF_LEN 512
#endif
#ifndef AT_SERVER_DEVICE
#define AT_SERVER_DEVICE "uart2"
#endif
#ifndef AT_CLIENT_DEVICE
#define AT_CLIENT_DEVICE "uart2"
#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 recv_buffer[AT_CLIENT_RECV_BUFF_LEN];
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(void);
/* AT client send commands to AT server and waiter response */
int at_exec_cmd(at_response_t resp, const char *cmd_expr, ...);
/* AT Client send or receive data */
rt_size_t at_client_send(const char *buf, rt_size_t size);
rt_size_t at_client_recv(char *buf, rt_size_t size);
/* AT response structure 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, ...);
/* Set URC(Unsolicited Result Code) table */
void at_set_urc_table(const struct at_urc * table, rt_size_t size);
#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 AT_USING_CLIENT
/* AT client port initialization */
int at_client_port_init(void);
#endif
#endif /* __AT_H__ */
/*
* File : at_base_cmd.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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>
#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);
/*
* File : at_cli.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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, "at_cli_notice", 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;
rt_sem_detach(&console_rx_notice);
rt_ringbuffer_destroy(console_rx_fifo);
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);
}
#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(void)
{
#define ESC_KEY 0x1B
#define BACKSPACE_KEY 0x08
#define DELECT_KEY 0x7F
extern at_client_t rt_at_get_client(void);
at_client_t client = rt_at_get_client();
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, "at_cli_client_notice", 0, RT_IPC_FLAG_FIFO);
client_rx_fifo = rt_ringbuffer_create(AT_CLI_FIFO_SIZE);
at_client = rt_thread_create("at_cli_client", 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_exec_cmd(RT_NULL, "%.*s", cur_line_len, cur_line);
}
cur_line_len = 0;
}
else
{
rt_kprintf("%c", ch);
cur_line[cur_line_len++] = ch;
}
}
rt_thread_delete(at_client);
rt_sem_detach(&client_rx_notice);
rt_ringbuffer_destroy(client_rx_fifo);
/* 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);
}
}
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)
{
rt_kprintf("Please input 'at <server|client>' \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
}
else if (!strcmp(argv[1], "client"))
{
#ifdef AT_USING_CLIENT
client_cli_parser();
#else
rt_kprintf("Not support AT client, please check your configure!\n");
#endif
}
else
{
rt_kprintf("Please input 'at <server|client>' \n");
}
at_cli_deinit();
}
MSH_CMD_EXPORT(at, RT-Thread AT component cli: at <server|client>);
#endif /* AT_USING_CLI */
/*
* File : at_client.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-03-30 chenyong first version
* 2018-04-12 chenyong add client implement
*/
#include <at.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 at_client_t at_client_local = RT_NULL;
static char cust_end_sign = 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 structure.
*
* @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 structure
* = 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)
{
LOG_E("AT create response structure failed! No memory for response structure!");
return RT_NULL;
}
resp->buf = (char *) rt_calloc(1, buf_size);
if (!resp->buf)
{
LOG_E("AT create response structure failed! No memory for response buf structure!");
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 structure.
*
* @param resp response structure
*/
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 structure information
*
* @param resp response structure
* @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 structure
* = 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 = 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 structure
* @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 structure
* @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 structure
* @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 structure
* @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 resp AT response structure, 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_exec_cmd(at_response_t resp, const char *cmd_expr, ...)
{
at_client_t client = at_client_local;
va_list args;
rt_size_t cmd_size = 0;
rt_err_t result = RT_EOK;
const char *cmd = RT_NULL;
RT_ASSERT(cmd_expr);
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)
{
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;
}
/**
* Send data to AT server, send data don't have end sign(eg: \r\n).
*
* @param buf send data buffer
* @param size send fixed data size
*
* @return send data size
*/
rt_size_t at_client_send(const char *buf, rt_size_t size)
{
at_client_t client = at_client_local;
RT_ASSERT(buf);
#ifdef AT_PRINT_RAW_CMD
at_print_raw_cmd("send", buf, size);
#endif
return rt_device_write(client->device, 0, buf, size);
}
static char at_client_getchar(void)
{
char ch;
at_client_t client = at_client_local;
rt_sem_take(client->rx_notice, RT_WAITING_FOREVER);
rt_device_read(client->device, 0, &ch, 1);
return ch;
}
/**
* AT client receive fixed-length data.
*
* @param buf receive data buffer
* @param size receive fixed data size
*
* @note this function can only be used in execution function of URC data
*
* @return success receive data size
*/
rt_size_t at_client_recv(char *buf, rt_size_t size)
{
rt_size_t read_idx = 0;
char ch;
RT_ASSERT(buf);
while (1)
{
if (read_idx < size)
{
ch = at_client_getchar();
buf[read_idx++] = ch;
}
else
{
break;
}
}
#ifdef AT_PRINT_RAW_CMD
at_print_raw_cmd("urc_recv", buf, size);
#endif
return read_idx;
}
/**
* get AT client structure pointer.
*
* @return AT client structure pointer
*/
at_client_t rt_at_get_client(void)
{
RT_ASSERT(at_client_local);
RT_ASSERT(at_client_local->status != AT_STATUS_UNINITIALIZED);
return at_client_local;
}
/**
* AT client set end sign.
*
* @param ch the end sign, can not be used when it is '\0'
*
* @return 0: set success
*/
int at_set_end_sign(char ch)
{
cust_end_sign = ch;
return 0;
}
static const struct at_urc *get_urc_obj(char *data, rt_size_t size)
{
rt_size_t i, prefix_len, suffix_len;
at_client_t client = at_client_local;
if (client->urc_table == RT_NULL)
{
return RT_NULL;
}
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 (size < prefix_len + suffix_len)
{
continue;
}
if ((prefix_len ? !strncmp(data, client->urc_table[i].cmd_prefix, prefix_len) : 1)
&& (suffix_len ? !strncmp(data + size - suffix_len, client->urc_table[i].cmd_suffix, suffix_len) : 1))
{
return &client->urc_table[i];
}
}
return RT_NULL;
}
static int at_recv_readline(void)
{
rt_size_t read_len = 0;
char ch = 0, last_ch = 0;
rt_bool_t is_full = RT_FALSE;
at_client_t client = at_client_local;
memset(client->recv_buffer, 0x00, AT_CLIENT_RECV_BUFF_LEN);
client->cur_recv_len = 0;
while (1)
{
ch = at_client_getchar();
if (read_len < AT_CLIENT_RECV_BUFF_LEN)
{
client->recv_buffer[read_len++] = ch;
}
else
{
is_full = RT_TRUE;
}
/* is newline or URC data */
if ((ch == '\n' && last_ch == '\r') || (cust_end_sign != 0 && ch == cust_end_sign)
|| get_urc_obj(client->recv_buffer, read_len))
{
if (is_full)
{
LOG_E("read line failed. The line data length is out of buffer size(%d)!", AT_CLIENT_RECV_BUFF_LEN);
memset(client->recv_buffer, 0x00, AT_CLIENT_RECV_BUFF_LEN);
client->cur_recv_len = 0;
return -RT_EFULL;
}
client->cur_recv_len = read_len;
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() > 0)
{
if ((urc = get_urc_obj(client->recv_buffer, client->cur_recv_len)) != 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 ((memcmp(client->recv_buffer, AT_RESP_END_ERROR, strlen(AT_RESP_END_ERROR)) == 0)
|| (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)
{
rt_sem_release(at_client_local->rx_notice);
return RT_EOK;
}
/**
* Set URC(Unsolicited Result Code) table
*
* @param table URC table
* @param size table size
*/
void at_set_urc_table(const struct at_urc *table, rt_size_t size)
{
rt_size_t idx;
for(idx = 0; idx < size; idx++)
{
RT_ASSERT(table[idx].cmd_prefix);
RT_ASSERT(table[idx].cmd_suffix);
}
at_client_local->urc_table = table;
at_client_local->urc_table_size = size;
}
int at_client_init(void)
{
int result = RT_EOK;
if (at_client_local)
{
return result;
}
at_client_local = (at_client_t) rt_calloc(1, sizeof(struct at_client));
if (!at_client_local)
{
result = -RT_ERROR;
LOG_E("AT client session initialize failed! No memory for at_client structure !");
goto __exit;
}
at_client_local->status = AT_STATUS_UNINITIALIZED;
at_client_local->lock = rt_mutex_create("at_lock", RT_IPC_FLAG_FIFO);
if(!at_client_local->lock)
{
LOG_E("AT client session initialize failed! at_client_recv_lock create failed!");
result = -RT_ENOMEM;
goto __exit;
}
at_client_local->cur_recv_len = 0;
at_client_local->rx_notice = rt_sem_create("at_client_notice", 0, RT_IPC_FLAG_FIFO);
if (!at_client_local->rx_notice)
{
LOG_E("AT client session initialize failed! at_client_notice semaphore create failed!");
result = -RT_ENOMEM;
goto __exit;
}
at_client_local->resp_notice = rt_sem_create("at_client_resp", 0, RT_IPC_FLAG_FIFO);
if (!at_client_local->resp_notice)
{
LOG_E("AT client session initialize failed! at_client_resp semaphore create failed!");
result = -RT_ENOMEM;
goto __exit;
}
/* Find and open command device */
at_client_local->device = rt_device_find(AT_CLIENT_DEVICE);
if (at_client_local->device)
{
RT_ASSERT(at_client_local->device->type == RT_Device_Class_Char);
rt_device_open(at_client_local->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
rt_device_set_rx_indicate(at_client_local->device, at_client_rx_ind);
}
else
{
LOG_E("AT client device initialize failed! Not find the device : %s.", AT_CLIENT_DEVICE);
result = -RT_ERROR;
goto __exit;
}
at_client_local->urc_table = RT_NULL;
at_client_local->urc_table_size = 0;
at_client_local->parser = rt_thread_create("at_client",
(void (*)(void *parameter))client_parser,
at_client_local,
1024 + 512,
RT_THREAD_PRIORITY_MAX / 3 - 1,
5);
if (at_client_local->parser == RT_NULL)
{
result = -RT_ENOMEM;
goto __exit;
}
if ((result = at_client_port_init()) != RT_EOK)
{
LOG_E("AT client port initialize failed(%d).", result);
}
__exit:
if (!result)
{
at_client_local->status = AT_STATUS_INITIALIZED;
rt_thread_startup(at_client_local->parser);
LOG_I("RT-Thread AT client (V%s) initialize success.", AT_SW_VERSION);
}
else
{
if (at_client_local)
{
rt_free(at_client_local);
}
LOG_E("RT-Thread AT client (V%s) initialize failed(%d).", AT_SW_VERSION, result);
}
return result;
}
INIT_COMPONENT_EXPORT(at_client_init);
RT_WEAK int at_client_port_init(void)
{
at_client_local->urc_table = RT_NULL;
at_client_local->urc_table_size = 0;
LOG_E("The client porting initialize for AT client is not implement.");
return 0;
}
/*
* File : at_server.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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 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;
rt_sem_take(at_server_local->rx_notice, RT_WAITING_FOREVER);
rt_device_read(at_server_local->device, 0, &ch, 1);
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)
{
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;
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_server_notice", 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);
rt_device_open(at_server_local->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
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_server",
(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.");
}
/*
* File : at_utils.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册