还在用传统方式驱动一个通信模组?TOS的AT模组框架了解下吧!
扫描二维码
随时随地手机看文章
本节基于TOS的AT框架,我实现了一个基于MX+开发板的demo,用于控制之前搭的智能小车,效果如下,详细源码及实验例程请参考文末码云仓库链接:
动手智能小车记(5)-坦克底盘硬件模块大杂烩
1、什么是AT指令?
在嵌入式开发过程中,我们有时候要使用一些通信模组,比如蓝牙、WIFI、4G、NBIOT等等,这些模组内部固件已经将协议栈封装好了,然后模组硬件向外部提供了标准的串口,这样通过与模组的串口相连接,就可以与模组进行通信;一般情况下,厂商都会提供模组的使用手册,只要照着流程去操作模组就可以正常通信了,如下图所示,这是一个MCU、蓝牙模组、手机之间的通信案例:
2、为什么要有AT框架?
一般情况下,在一些物联网产品的项目上可能这样的需求,比如:
常规的一些传感器设备,需要监测环境温度、湿度等等这样的情况:
-
实现数据上传
共享单车、智能门锁
-
实现开锁的逻辑
等等。。。
这些需求看起来就非常简单,比如我就用ESP8266+一个后台服务器来实现这样的需求吧,只要后台提供好API接口,那么这类简单的需求分分钟搞定,完全没有任何难度,在应用程序上编写好模组的驱动接口和通信逻辑就可以了。
但是,如果换一个呢??再换一个呢?有可能实现同样的需求,我们还要去实现不一样的驱动流程,这是不是显得很麻烦?基于这样的问题诞生,于是各个厂商分别提出了对应的AT框架思维,那么这种AT框架思维具体是什么样的呢?以驱动ESP8266为例,一般有以下几种模式:
-
AP模式 -
STA模式 -
AP+STA模式
以STA模式为例,最后要和云端服务器进行对接,我们首先要完成初始化流程,一般要发以下几个指令:
-
AT+RESTORE\r\n 模组复位
-
ATE0\r\n 关闭回显
-
AT+CWMODE=1\r\n 设置多连接
-
AT+CIPMODE=0\r\n 关闭透传模式
-
AT+CIPMUX=1\r\n 开始多连接模式
-
AT+CWJAP="TOS","12345678"\r\n 连接热点
这样就基本完成了模组的初始化流程,初始化完毕以后,就可以进入数据传输的了,连接服务器,然后开启透传模式,进入透传模式,然后就可以把数据直接传送到后台了,此时还可以读取后台的消息,当我们不需要需要模组的时候,还可以将模组掉电;所以,我们可以在这个基础上把这个驱动流程框架化,即是拥有初始化、连接服务器、发送、接收、关闭等等这些接口。
3、TencetOS tiny AT框架
在TencentOS tiny中,内部就集成了一套简单易用的AT框架,哪怕是不一样的指令,我们也只需要填充对应的方法,然后注册到框架上,就可以顺利与模组进行通信了,以下是TencentOS tiny AT框架的基本组成图:
上图来源于汪兄讲解的PPT
对于应用开发者来说,我们最关注的是SAL interface、也就是网络适配框架,只要模组注网成功,那么在这一层,我们不需要具体去关注模组到底是怎么用AT指令去通信的,我们只需要调用SAL interface的socket、connect、send、recv、close等等接口完成我们与后台的通信或者与别的通信方式的逻辑即可,但是调用SAL接口口还需要去与各个模组进行适配。
typedef struct sal_module_st {
int (*init)(void);
int (*get_local_mac)(char *mac);
int (*get_local_ip)(char *ip, char *gw, char *mask);
int (*parse_domain)(const char *host_name, char *host_ip, size_t host_ip_len);
int (*connect)(const char *ip, const char *port, sal_proto_t proto);
int (*send)(int sock, const void *buf, size_t len);
int (*recv_timeout)(int sock, void *buf, size_t len, uint32_t timeout);
int (*recv)(int sock, void *buf, size_t len);
int (*sendto)(int sock, char *ip, char *port, const void *buf, size_t len);
int (*recvfrom)(int sock, void *buf, size_t len);
int (*recvfrom_timeout)(int sock, void *buf, size_t len, uint32_t timeout);
int (*close)(int sock);
} sal_module_t;
对于怎么去绑定(适配)模组和SAL interface,在此之前那我们还需要完成AT framework与HAL(uart)的适配,然后提供SAL interface需要的接口,注册上去,这样我们就可以在SAL上愉快的进行操作了,接下来AT框架具体是怎么解析每个AT指令我们就不需要特别去关心了,感兴趣的可以去研究一下tos_at.c、tos_at.h这两个文件。
在TencentOS tiny SDK中,腾讯官方已经提供了一些热门模组的操作例程,比如esp8266,它是怎么与SAL interface完成适配的呢?如下:
上图来源于戴兄讲解的PPT
这样的话,我们就可以调用TOS提供的SAL接口进行通信了,如下,在sal_module_wrapper.h中查看,详细实现在sal_module_wrapper.c:
/**
* @brief Convert domain to ip address.
*
* @attention None
*
* @param[in] host_name domain name of the host
* @param[out] host_ip ip address of the host
* @param[out] host_ip_len ip address buffer length
*
* @return errcode
*/
int tos_sal_module_parse_domain(const char *host_name, char *host_ip, size_t host_ip_len);
/**
* @brief Connect to remote host.
*
* @attention None
*
* @param[in] ip ip address of the remote host
* @param[in] port port number of the remote host
* @param[in] proto protocol of the connection(TCP/UDP)
*
* @return socket id if succuss, -1 if failed.
*/
int tos_sal_module_connect(const char *ip, const char *port, sal_proto_t proto);
/**
* @brief Send data to the remote host(TCP).
*
* @attention None
*
* @param[in] sock socket id
* @param[in] buf data to send
* @param[in] len data length
*
* @return data length sent
*/
int tos_sal_module_send(int sock, const void *buf, size_t len);
/**
* @brief Receive data from the remote host(TCP).
*
* @attention None
*
* @param[in] sock socket id
* @param[in] buf data buffer to hold the data received
* @param[in] len data buffer length
*
* @return data length received
*/
int tos_sal_module_recv(int sock, void *buf, size_t len);
/**
* @brief Receive data from the remote host(TCP).
*
* @attention None
*
* @param[in] sock socket id
* @param[in] buf data buffer to hold the data received
* @param[in] len data buffer length
* @param[in] timeout timeout
*
* @return data length received
*/
int tos_sal_module_recv_timeout(int sock, void *buf, size_t len, uint32_t timeout);
/**
* @brief Send data to the remote host(UDP).
*
* @attention None
*
* @param[in] sock socket id
* @param[in] ip ip address of the remote host
* @param[in] port port number of the remote host
* @param[in] buf data to send
* @param[in] len data length
*
* @return data length sent
*/
int tos_sal_module_sendto(int sock, char *ip, char *port, const void *buf, size_t len);
/**
* @brief Receive data from the remote host(UDP).
*
* @attention None
*
* @param[in] sock socket id
* @param[in] buf data buffer to hold the data received
* @param[in] len data buffer length
*
* @return data length received
*/
int tos_sal_module_recvfrom(int sock, void *buf, size_t len);
/**
* @brief Receive data from the remote host(UDP).
*
* @attention None
*
* @param[in] sock socket id
* @param[in] buf data buffer to hold the data received
* @param[in] len data buffer length
* @param[in] timeout timeout
*
* @return data length received
*/
int tos_sal_module_recvfrom_timeout(int sock, void *buf, size_t len, uint32_t timeout);
/**
* @brief Close the connection.
*
* @attention None
*
* @param[in] sock socket id
*
* @return errcode
*/
int tos_sal_module_close(int sock);
但是你以为这就完了吗?为了和Posix API无差异化调用,TencentOS tiny官方开发人员开发了一套近似于通用网络操作接口,就类似操作一个文件一样open、read、write、close,这不就更简单了嘛?我们来一睹为快:
tos_at_socket.h
#ifndef _TOS_AT_SOCKET_H_
#define _TOS_AT_SOCKET_H_
#include "tos_at_socket_lib.h"
#include "tos_at_socket_types.h"
#define AF_INET 0
#define AF_INET6 1
#define AF_UNIX 2
/* Provides sequenced, reliable, bidirectional, connection-mode byte streams, and may provide a transmission mechanism for out-of-band data. */
#define SOCK_STREAM 0
/* Provides datagrams, which are connectionless-mode, unreliable messages of fixed maximum length. */
#define SOCK_DGRAM 1
/* Peeks at an incoming message. The data is treated as unread and the next recv() or similar function shall still return this data. */
#define MSG_PEEK 0x01
/* Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific. */
#define MSG_OOB 0x02
/* On SOCK_STREAM sockets this requests that the function block until the full amount of data can be returned. The function may return the smaller amount of data if the socket is a message-based socket, if a signal is caught, if the connection is terminated, if MSG_PEEK was specified, or if an error is pending for the socket. */
#define MSG_WAITALL 0x04
int socket(int domain, int type, int protocol);
int connect(int socket, const struct sockaddr *address, socklen_t address_len);
int recv(int socket, void *buffer, size_t length, int flags);
int recvfrom(int socket, void * buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len);
int send(int socket, const void *buffer, size_t length, int flags);
int sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
int shutdown(int socket, int how);
int read(int socket, void *buffer, size_t length);
int close(int socket);
int write(int socket, const void *buffer, size_t length);
#endif /* _TOS_AT_SOCKET_H_ */
只要注册框架等流程完成以后,模组注网成功,后面就可以直接这样常规操作了:
void network_demo(void)
{
int recv_len = -1;
int fd, rc, cnt = 0;
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(SERVER_IP);
addr.sin_port = htons(SERVER_PORT);
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
printf("socket failed\n");
return;
}
rc = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
if (rc < 0)
{
printf("connect failed\n");
close(fd);
return;
}
while(1)
{
//调用send发送数据
//调用recv接收并处理数据
}
close(fd);
}
基于TencentOS tiny AT框架的基础上,我编写了一个基于MX+开发板的控制小车例程,源码已更新到码云个人仓库。
个人码云仓库地址:
https://gitee.com/morixinguan
我还将之前做的一些项目以及练习例程在近期内全部上传完毕,与大家一起分享交流:
全文参考资料
腾讯物联网终端操作系统SDK文档.pdf
腾讯物联网终端操作系统开发指南.pdf
TencentOS tiny技术讲解与开发实践PPT.pdf
云加社区沙龙(腾讯物联网操作系统TencentOS tiny架构解析与实践).pdf
公众号粉丝福利时刻
这里我给大家申请到了福利,本公众号读者购买小熊派开发板可享受9折优惠,有需要购买小熊派以及腾讯物联网开发板的朋友,淘宝搜索即可,跟客服说你是公众号:嵌入式云IOT技术圈 的粉丝,立享9折优惠!
往期精彩
TencentOS tiny RTOS快速入门
网红腾讯物联网开发板终极开箱评测,让我们一睹为快!
Linux进程间通信(中)之信号、信号量实践
Linux进程间通信(下)之共享内存实践
STM32硬核DIY机械键盘|蓝牙USB双模|灯控
觉得本次分享的文章对您有帮助,随手点[在看]
并转发分享,也是对我的支持。
免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!