001-【RTThread】-AT组件

==================================================================================

1.初始化步骤

ESP8266

 \ | /
- RT -     Thread Operating System
 / | \     4.0.3 
 \ | /
- RT -     Thread Operating System
 / | \     4.0.3 build Jun 15 2021
 2006 - 2020 Copyright by rt-thread team
lwIP-2.1.2 initialized!
[I/sal.skt] Socket Abstraction Layer initialize success.
[I/at.clnt] AT client(V1.3.1) on device uart3 initialize success.
[D/at.dev.esp] network interface device(esp0) set up status
[D/at.dev.esp] esp0 device initialize start.
msh >[I/at.dev.esp] esp0 device wifi is disconnect.
[D/at.dev.esp] AT version:1.2.0.0(Jul  1 2016 20:04:45)
[D/at.dev.esp] SDK version:1.5.4.1(39cb9a32)
[D/at.dev.esp] Ai-Thinker Technology Co. Ltd.
[D/at.dev.esp] Dec  2 2016 14:21:16
[I/at.dev.esp] esp0 device wifi is connected.
[I/at.dev.esp] esp0 device network initialize successfully.
[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed!
[W/at.dev.esp] please check and update esp0 device firmware to support the "AT+CIPDNS_CUR?" cmd.
[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed!
[W/at.dev.esp] please check and update esp0 device firmware to support the "AT+CIPDNS_CUR?" cmd.
  • 1.代码没有上传,一直上传失败,还是切换到使用gitee。

切换到gitee了,网速快点。 和SVN上传代码一样,不允许有空的文件夹,否则上传失败。

  • 2.分析下这个AT组件的流程

要配合at_device 软件包的使用,选择at_client

在这里插入图片描述

  • 3.怎么调用的urc里的函数
static const struct at_urc urc_table[] =
{
    {"busy p",           "\r\n",           urc_busy_p_func},
    {"busy s",           "\r\n",           urc_busy_s_func},
    {"WIFI CONNECTED",   "\r\n",           urc_func},
    {"WIFI DISCONNECT",  "\r\n",           urc_func},
};

当AT Server 遇到上面这些定义的情况,将会调用我们定义的urc_table中的执行函数(见函数client_parser代码)。

//rt-thread/components/net/at/src/at_client.c
static void client_parser(at_client_t client)
  • 4.接收时 用到的正则表达式
at_response_t resp = RT_NULL;
const char *resp_expr = "%*[^\"]\"%[^\"]\"";//正则表达式,可以查询sscanf
if (at_resp_parse_line_args_by_kw(resp, "STAMAC", resp_expr, mac) <= 0)
{
    LOG_E("%s device parse \"AT+CIFSR\" cmd error.", device->name);
    goto __exit;
}
if (at_resp_parse_line_args_by_kw(resp, "ip", resp_expr, ip) <= 0 ||
        at_resp_parse_line_args_by_kw(resp, "gateway", resp_expr, gateway) <= 0 ||
        at_resp_parse_line_args_by_kw(resp, "netmask", resp_expr, netmask) <= 0)
{
    LOG_E("%s device prase \"AT+CIPSTA?\" cmd error.", device->name);
    goto __exit;
}

sscanf_百度百科 (baidu.com)
在这里插入图片描述
在这里插入图片描述

正则表达式_百度百科 (baidu.com)

2. 初始化成功

msh >ifconfig
network interface device: esp0 (Default)
MTU: 1500
MAC: fc f5 c4 83 04 30 
FLAGS: UP LINK_UP INTERNET_UP DHCP_DISABLE
ip address: 192.168.2.110
gw address: 192.168.2.1
net mask  : 255.255.255.0
dns server #0: 0.0.0.0
dns server #1: 0.0.0.0
msh >
msh >
msh >ping www.baidu.com
32 bytes from 36.152.44.95 icmp_seq=0 time=12 ms
32 bytes from 36.152.44.96 icmp_seq=1 time=12 ms
32 bytes from 36.152.44.95 icmp_seq=2 time=10 ms
32 bytes from 36.152.44.95 icmp_seq=3 time=11 ms

从上面ifconfig与ping命令的执行结果看,ESP8266已经成功连接上WiFi,并能访问外部网络(比如www.baidu.com),说明我们的移植暂未出现明显问题,可以在此基础上使用BSD Socket API 编写网络应用程序了。

3.AT CLI 命令行交互模式

AT Client CLI 功能可以转发本地 shell 输入的数据到设备连接的 AT Server 串口设备上,并在本地 shell 上实时显示 AT Client 串口接收到的数据。在本地 shell 中执行 at client 命令进入 AT Client CLI 模式即可进行数据的收发。

msh >at client
======== Welcome to using RT-Thread AT command client cli ========
Cli will forward your command to server port(uart3). Press 'ESC' to exit.
AT

OK
AT+CIFSR
+CIFSR:STAIP,"192.168.2.110"
+CIFSR:STAMAC,"fc:f5:c4:83:04:30"

OK
AT+GMR
AT version:1.2.0.0(Jul  1 2016 20:04:45)
SDK version:1.5.4.1(39cb9a32)
Ai-Thinker Technology Co. Ltd.
Dec  2 2016 14:21:16
OK
  • 怎么实现 AT cli client的?

rt-thread/components/net/at/src/at_cli.c

串口收发透传

MSH_CMD_EXPORT(at, RT-Thread AT component cli: at <server|client [dev_name]>);

/*1.从控制台读取字符*/
while (ESC_KEY != (ch = console_getchar()))
{
     /*按下ENTER发送AT*/
     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;
       }
}
/*2.从串口读取字符 使用是ringbuffer*/
at_client = rt_thread_create("at_cli", at_client_entry, RT_NULL, 512, 8, 8);
static void at_client_entry(void *param)
{
    char ch;

    while(1)
    {
        ch = client_getchar();
        rt_kprintf("%c", ch);/*读取出来 再回显到控制台*/
    }
}

4.建立tcp_client通讯

问题:
电脑(192.168.2.101)能ping通wifi板子(192.168.2.110),但是wifi板子却ping不通电脑;但是板子却又能ping通外网 百度?
答:电脑的防火墙要全部关闭

在这里插入图片描述

4.1 分析ping命令

在这里插入图片描述


//components/net/netdev/src//netdev.c

#ifdef NETDEV_USING_PING
int netdev_cmd_ping(char* target_name, rt_uint32_t times, rt_size_t size)
{
#define NETDEV_PING_DATA_SIZE       32
/** ping receive timeout - in milliseconds */
#define NETDEV_PING_RECV_TIMEO      (2 * RT_TICK_PER_SECOND)
/** ping delay - in milliseconds */
#define NETDEV_PING_DELAY           (1 * RT_TICK_PER_SECOND)
/* check netdev ping options */
#define NETDEV_PING_IS_COMMONICABLE(netdev) \
    ((netdev) && (netdev)->ops && (netdev)->ops->ping && \
        netdev_is_up(netdev) && netdev_is_link_up(netdev)) \

    struct netdev *netdev = RT_NULL;
    struct netdev_ping_resp ping_resp;
    int index, ret = 0;

    if (size == 0)
    {
        size = NETDEV_PING_DATA_SIZE;
    }

    if (NETDEV_PING_IS_COMMONICABLE(netdev_default))
    {
        /* using default network interface device for ping */
        netdev = netdev_default;
    }
    else
    {
        /* using first internet up status network interface device */
        netdev = netdev_get_first_by_flags(NETDEV_FLAG_LINK_UP);
        if (netdev == RT_NULL)
        {
            rt_kprintf("ping: not found available network interface device.\n");
            return -RT_ERROR;
        }
        else if (netdev->ops == RT_NULL || netdev->ops->ping == RT_NULL)
        {
            rt_kprintf("ping: network interface device(%s) not support ping feature.\n", netdev->name);
            return -RT_ERROR;
        }
        else if (!netdev_is_up(netdev) || !netdev_is_link_up(netdev))
        {
            rt_kprintf("ping: network interface device(%s) status error.\n", netdev->name);
            return -RT_ERROR;
        }
    }

    for (index = 0; index < times; index++)
    {
        int delay_tick = 0;
        rt_tick_t start_tick = 0;

        rt_memset(&ping_resp, 0x00, sizeof(struct netdev_ping_resp));
        start_tick = rt_tick_get();
        ret = netdev->ops->ping(netdev, (const char *)target_name, size, NETDEV_PING_RECV_TIMEO, &ping_resp);
        if (ret == -RT_ETIMEOUT)
        {
            rt_kprintf("ping: from %s icmp_seq=%d timeout\n",
                (ip_addr_isany(&(ping_resp.ip_addr))) ? target_name : inet_ntoa(ping_resp.ip_addr), index);
        }
        else if (ret == -RT_ERROR)
        {
            rt_kprintf("ping: unknown %s %s\n",
                (ip_addr_isany(&(ping_resp.ip_addr))) ? "host" : "address",
                    (ip_addr_isany(&(ping_resp.ip_addr))) ? target_name : inet_ntoa(ping_resp.ip_addr));
        }
        else
        {
            if (ping_resp.ttl == 0)
            {
                rt_kprintf("%d bytes from %s icmp_seq=%d time=%d ms\n",
                            ping_resp.data_len, inet_ntoa(ping_resp.ip_addr), index, ping_resp.ticks);
            }
            else
            {
                rt_kprintf("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n",
                            ping_resp.data_len, inet_ntoa(ping_resp.ip_addr), index, ping_resp.ttl, ping_resp.ticks);
            }
        }

        /* if the response time is more than NETDEV_PING_DELAY, no need to delay */
        delay_tick = ((rt_tick_get() - start_tick) > NETDEV_PING_DELAY) || (index == times) ? 0 : NETDEV_PING_DELAY;
        rt_thread_delay(delay_tick);
    }

    return RT_EOK;
}

int netdev_ping(int argc, char **argv)
{
    if (argc == 1)
    {
        rt_kprintf("Please input: ping <host address>\n");
    }
    else
    {
        netdev_cmd_ping(argv[1], 4, 0);
    }

    return 0;
}
FINSH_FUNCTION_EXPORT_ALIAS(netdev_ping, __cmd_ping, ping network host);

在这里插入图片描述
static int esp8266_init(struct at_device *device)
在esp8266初始化时已经绑定好了。

components/net/netdev/src//netdev.c
netdev->ops->ping(netdev, (const char *)target_name, size, NETDEV_PING_RECV_TIMEO, &ping_resp);
调用 esp8266_netdev_ping

packages/at-device-latest/class/esp8266/at_device_esp8266.c

static int esp8266_netdev_ping(struct netdev *netdev, const char *host,
size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp)

给esp8266单独发ping AT 命令式时的接收返回:

AT+PING=“192.168.2.101”
+5

发送AT指令 :
at_obj_exec_cmd(device->client, resp, “AT+PING=”%s"", host)

解析返回值时根据关键字“+
at_resp_parse_line_args_by_kw(resp, “+”, “+%d”, &req_time)

//packages/at-device-latest/class/esp8266/at_device_esp8266.c

static int esp8266_netdev_ping(struct netdev *netdev, const char *host,
                size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp)
{
#define ESP8266_PING_IP_SIZE         16

    rt_err_t result = RT_EOK;
    at_response_t resp = RT_NULL;
    struct at_device *device = RT_NULL;
    char ip_addr[ESP8266_PING_IP_SIZE] = {0};
    int req_time;

    RT_ASSERT(netdev);
    RT_ASSERT(host);
    RT_ASSERT(ping_resp);

    device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL)
    {
        LOG_E("get device(%s) failed.", netdev->name);
        return -RT_ERROR;
    }

    resp = at_create_resp(64, 0, timeout);
    if (resp == RT_NULL)
    {
        LOG_E("no memory for resp create.");
        return -RT_ENOMEM;
    }

    /* send domain commond "AT+CIPDOMAIN=<domain name>" and wait response */
    if (at_obj_exec_cmd(device->client, resp, "AT+CIPDOMAIN=\"%s\"", host) < 0)
    {
        result = -RT_ERROR;
        goto __exit;
    }

    /* parse the third line of response data, get the IP address */
    if (at_resp_parse_line_args_by_kw(resp, "+CIPDOMAIN:", "+CIPDOMAIN:%s", ip_addr) < 0)
    {
        result = -RT_ERROR;
        goto __exit;
    }

    /* send ping commond "AT+PING=<IP>" and wait response */
    if (at_obj_exec_cmd(device->client, resp, "AT+PING=\"%s\"", host) < 0)
    {
        result = -RT_ERROR;
        goto __exit;
    }

    if (at_resp_parse_line_args_by_kw(resp, "+", "+%d", &req_time) < 0)
    {
        result = -RT_ERROR;
        goto __exit;
    }

    if (req_time)
    {
        inet_aton(ip_addr, &(ping_resp->ip_addr));
        ping_resp->data_len = data_len;
        ping_resp->ttl = 0;
        ping_resp->ticks = req_time;
    }

__exit:
    if (resp)
    {
        at_delete_resp(resp);
    }

    return result;
}

4.2 分析 socket 函数

   /*创建一个socket,类型是SOCK_STREAM,TCP类型 */
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if(sock == -1)
    {
      LOG_E("Socket error\n");
      rt_free(buffer);
      return;
    }

    /* 初始化服务端地址 */
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr = *((struct in_addr *)host->h_addr);
    rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));

sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
----int at_socket(int domain, int type, int protocol)
在这里插入图片描述
在这里插入图片描述

    /* set AT socket receive data callback function */
    sock->ops->at_set_event_cb(AT_SOCKET_EVT_RECV, at_recv_notice_cb); //线程接收串口数据时调用
    sock->ops->at_set_event_cb(AT_SOCKET_EVT_CLOSED, at_closed_notice_cb);

4.3 分析 connect函数

    /* 绑定socket到服务端地址 */
    ret = connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
   if(ret == -1)
    {
      LOG_E("Connect fail\n");
      closesocket(sock);
      rt_free(buffer);
      return;
    }

    LOG_I("Successfully connected to (%s , %d)\n",
            inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));

在这里插入图片描述

connect(sock, (struct sockaddr )&server_addr, sizeof(struct sockaddr));
—esp8266_socket_connect()

在这里插入图片描述
在这里插入图片描述

4.4 分析recv 函数

 bytes_recv = recv(sock,buffer,BUFSZ-1,0);
#define recv(s, mem, len, flags)                           sal_recvfrom(s, mem, len, flags, NULL, NULL)

sal_recvfrom
---pf->skt_ops->recvfrom((int) sock->user_data, mem, len, flags, from, fromlen);
	---at_recvfrom(int socket, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
		---recv_len = at_recvpkt_get(&(sock->recvpkt_list), (char *) mem, len);

在创建at_client时注册了接收解析函数static void client_parser(at_client_t client),这个里面会有urc->func(client, client->recv_line_buf, client->recv_line_len);

我们在创建at_socket时 创建了 urc_table,其中一项是{"+IPD",             ":",              urc_recv_func},+IPD 就是串口接收的wifi数据。接收数据用
 at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz);
放入到接收链表中。at_recvpkt_put(&(sock->recvpkt_list), buff, bfsz);

这样就形成了 client_parser 线程放数据到链表中 at_recvpkt_put,而recvfrom 从链表中拿取数据at_recvpkt_get 

在这里插入图片描述
高清原图

4.5 分析 send 函数

	  static char Sensor_Data[] ="The current temperature is: %3d.%d C, humidity is: %3d.%d %.";
       if(rt_strncmp(buffer, "get", 3) == 0)
       {
           rt_sprintf(buffer, Sensor_Data, 11, 22,33, 44);
          /* 发送数据到sock连接 */
          send(sock, buffer, (size_t)rt_strlen(buffer), 0);
          LOG_D("%s\n", buffer);
       }

在这里插入图片描述

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值