当前位置:首页 > 公众号精选 > 嵌入式云IOT技术圈
[导读]本文基于TOS的AT框架,实现了一个基于MX+开发板的demo,用于控制之前搭的智能小车。

本节基于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获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

Linux系统平台上有许多开源的系统构建框架,这些框架方便了开发者进行嵌入式系统的构建和定制化开发,目前比较常见的有Buildroot, Yocto, OpenEmbedded等等。

关键字: 树莓派 瑞米派 开发板

NXP在处理器板块耕耘多年,从早期的i.MX 6 → i.MX 7 → i.MX 8,再到最新的i.MX 9都已经有一条完整的生态链以及很多客户基础。i.MX 93是NXP i.MX 9产品组合中最新的一个系列。i.MX...

关键字: 核心板 开发板

近日,米尔电子推出米尔基于NXP i.MX 93系列产品-MYC-LMX9X核心板及开发板。NXP i.MX 9系列在i.MX 6和i.MX 8系列产品市场验证的基础上,继承了前代产品的优点的同时,进一步提升了性能、资源...

关键字: 核心板 开发板 处理器

我们先实现 dmaion buffer 管理器,这里贴的代码省略了异常错误处理的逻辑,有个坑是 linux-4.9 和 linux-5.4 用法不一样,米尔电子的这个T113-i系统是linux-5.4,所以不兼容4.9...

关键字: 核心板 开发板

支持高达48V@5A的PD受电模式,达到目前USB PD最高标准。

关键字: 嵌入式 开发板

载波抑制这一术语在通信技术和电力电子学领域具有不同的含义,本篇文章将分别从这两个角度展开讨论,阐述载波抑制的概念、工作原理以及应用场景。

关键字: 载波抑制 通信技术

嵌入式开发平台是支撑嵌入式系统设计、开发和测试的重要基础环境,它集成了硬件设施、软件工具链、操作系统以及开发框架等一系列关键组件。本文将深入探讨嵌入式开发平台所具备的独特特点与优势,从硬件配置、软件环境、灵活性、可移植性...

关键字: 嵌入式开发平台 开发板

以前微处理器(MPU)与微控制器(MCU)是截然不同的两种设备,MPU支持丰富的软件系统,如Linux和相关的软件堆栈,而MCU通常将专注于裸机和RTOS。近年来,随着MCU的性能越来越高,MCU和MPU之间的区别变得越...

关键字: MCU MPU 开发板

一直以来,3G都是大家的关注焦点之一。因此针对大家的兴趣点所在,小编将为大家带来3G的相关介绍,详细内容请看下文。

关键字: 3G 通信技术
关闭
关闭