当前位置:网站首页>潘多拉 IOT 开发板学习(RT-Thread)—— 实验17 ESP8266 实验(学习笔记)
潘多拉 IOT 开发板学习(RT-Thread)—— 实验17 ESP8266 实验(学习笔记)
2022-07-26 15:34:00 【小辉_Super】
本文代码参考 RT-Thread 官方 BSP
实验功能
例程源码:(main.c)
该实验实现的功能:Ping 一个指定的 IP 地址。
/* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018-08-16 ZYLX first implementation */
#include <rtthread.h>
int main(void)
{
extern int netdev_ping(int argc, char **argv);
char *cmd[] = {
"esp8266_ping", "www.rt-thread.org"};
while (1)
{
if (netdev_ping(2, cmd) == RT_EOK)
{
break;
}
else
{
rt_thread_mdelay(5000);
}
}
return 0;
}
代码剖析
netdev_ping()
ping 命令,该函数主要进行参数判断,然后执行底层的 ping 命令函数。
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;
}
netdev_cmd_ping()
ping 命令函数,实际调用的是 (netdev)->ops->ping,这里的 netdev 是一个全局变量,本实验对应的网络设备肯定是 esp8266。
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 || NETDEV_PING_IS_COMMONICABLE(netdev) == 0)
{
rt_kprintf("ping: network interface device get error.\n");
return -RT_ERROR;
}
}
for (index = 0; index < times; index++)
{
rt_memset(&ping_resp, 0x00, sizeof(struct netdev_ping_resp));
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);
}
}
rt_thread_mdelay(NETDEV_PING_DELAY);
}
return RT_EOK;
}
esp8266_netdev_ping()
这便是 esp8266 底层驱动的 ping 函数,它会发送 “AT+PING=<IP>” 指令进行 ping 网测试。
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
at_response_t resp = RT_NULL;
rt_err_t result = RT_EOK;
int req_time;
char ip_addr[ESP8266_PING_IP_SIZE] = {
0};
RT_ASSERT(netdev);
RT_ASSERT(host);
RT_ASSERT(ping_resp);
resp = at_create_resp(64, 0, timeout);
if (!resp)
{
LOG_E("No memory for response structure!");
return -RT_ENOMEM;
}
rt_mutex_take(at_event_lock, RT_WAITING_FOREVER);
/* send domain commond "AT+CIPDOMAIN=<domain name>" and wait response */
if (at_exec_cmd(resp, "AT+CIPDOMAIN=\"%s\"", host) < 0)
{
LOG_D("ping: send commond AT+CIPDOMAIN=<domain name> failed");
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)
{
LOG_E("ping: get the IP address failed");
result = -RT_ERROR;
goto __exit;
}
/* send ping commond "AT+PING=<IP>" and wait response */
if (at_exec_cmd(resp, "AT+PING=\"%s\"", host) < 0)
{
LOG_D("ping: unknown remote server host");
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);
}
rt_mutex_release(at_event_lock);
return result;
}
rt_thread_mdelay()
这是 RT-Thread 的毫秒级延时函数,定义如下:
rt_err_t rt_thread_mdelay(rt_int32_t ms)
{
rt_tick_t tick;
// 获取需要的时钟节拍
tick = rt_tick_from_millisecond(ms);
// 阻塞相应的节拍时间
return rt_thread_sleep(tick);
}
rt_tick_from_millisecond()
/** * 算出 ms 对应的时钟节拍数 * * * @param ms the specified millisecond * - Negative Number wait forever * - Zero not wait * - Max 0x7fffffff * * @return the calculated tick */
rt_tick_t rt_tick_from_millisecond(rt_int32_t ms)
{
rt_tick_t tick;
if (ms < 0)
{
tick = (rt_tick_t)RT_WAITING_FOREVER; // -1
}
else
{
// 将“每秒节拍数” / 1000 * ms,算出对应的秒节拍数
tick = RT_TICK_PER_SECOND * (ms / 1000);
// 加上小于 1000ms 部分的节拍数
tick += (RT_TICK_PER_SECOND * (ms % 1000) + 999) / 1000;
}
/* return the calculated tick */
return tick;
}
rt_thread_sleep()
线程睡眠(挂起)函数,参数是系统节拍数:
/** * 该函数能让当前线程挂起一段时间(由 tick 决定) * * @param tick the sleep ticks * * @return RT_EOK */
rt_err_t rt_thread_sleep(rt_tick_t tick)
{
register rt_base_t temp;
struct rt_thread *thread;
/* set to current thread */
thread = rt_thread_self();
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* suspend thread */
rt_thread_suspend(thread);
/* reset the timeout of thread timer and start it */
rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick);
rt_timer_start(&(thread->thread_timer));
/* enable interrupt */
rt_hw_interrupt_enable(temp);
rt_schedule();
/* clear error number of this thread to RT_EOK */
if (thread->error == -RT_ETIMEOUT)
thread->error = RT_EOK;
return RT_EOK;
}
LOG_D()
本实验中,我们可以将 LOG_D() 视为 rt_kprintf(),
#define dbg_log_line(lvl, color_n, fmt, ...) \ do \ {
\ _DBG_LOG_HDR(lvl, color_n); \ rt_kprintf(fmt, ##__VA_ARGS__); \ _DBG_LOG_X_END; \ } \ while (0)
LOG_D 是 RT-Thread 内核里的一个日志打印函数,详情可见:《RT-Thread 文档中心——ulog 日志》
RT-Thread 的日志 API 包括:

at_socket_device_init
main.c 中并没有 WiFi 模块初始化代码,这些功能都在驱动底层完成了,这类代码文件全部在 at_socket_esp8266.c 中,下面这个函数是总的初始化函数,主要完成的是 AT socket 客户端的创建和 ESP8266 的初始化。
static int at_socket_device_init(void)
{
/* create current AT socket event */
at_socket_event = rt_event_create("at_se", RT_IPC_FLAG_FIFO);
if (at_socket_event == RT_NULL)
{
LOG_E("RT AT client port initialize failed! at_sock_event create failed!");
return -RT_ENOMEM;
}
/* create current AT socket event lock */
at_event_lock = rt_mutex_create("at_se", RT_IPC_FLAG_FIFO);
if (at_event_lock == RT_NULL)
{
LOG_E("RT AT client port initialize failed! at_sock_lock create failed!");
rt_event_delete(at_socket_event);
return -RT_ENOMEM;
}
/* initialize AT client */
at_client_init(AT_DEVICE_NAME, AT_DEVICE_RECV_BUFF_LEN);
/* register URC data execution function */
at_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
/* Add esp8266 to the netdev list */
esp8266_netdev_add(ESP8266_NETDEV_NAME);
/* initialize esp8266 net workqueue */
rt_delayed_work_init(&esp8266_net_work, exp8266_get_netdev_info, (void *)netdev_get_by_name(ESP8266_NETDEV_NAME));
/* initialize esp8266 network */
esp8266_net_init();
/* set esp8266 AT Socket options */
at_socket_device_register(&esp8266_socket_ops);
return RT_EOK;
}
INIT_APP_EXPORT(at_socket_device_init);
esp8266_net_init()
esp8266 初始化函数,其实就是调用了一个线程回调函数 esp8266_init_thread_entry()。
int esp8266_net_init(void)
{
#ifdef PKG_AT_INIT_BY_THREAD
rt_thread_t tid;
tid = rt_thread_create("esp8266_net_init", esp8266_init_thread_entry, RT_NULL, ESP8266_THREAD_STACK_SIZE, ESP8266_THREAD_PRIORITY, 20);
if (tid)
{
rt_thread_startup(tid);
}
else
{
LOG_E("Create AT initialization thread fail!");
}
#else
esp8266_init_thread_entry(RT_NULL);
#endif
return RT_EOK;
}
esp8266_init_thread_entry()
该函数内部主要执行了一些 AT 指令的发送,比如复位模块(AT+RST),设置 STA 模式(AT+CWMODE=1)。
static void esp8266_init_thread_entry(void *parameter)
{
at_response_t resp = RT_NULL;
rt_err_t result = RT_EOK;
rt_size_t i;
resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000));
if (!resp)
{
LOG_E("No memory for response structure!");
result = -RT_ENOMEM;
goto __exit;
}
rt_thread_delay(rt_tick_from_millisecond(5000));
/* reset module */
AT_SEND_CMD(resp, "AT+RST");
/* reset waiting delay */
rt_thread_delay(rt_tick_from_millisecond(1000));
/* disable echo */
AT_SEND_CMD(resp, "ATE0");
/* set current mode to Wi-Fi station */
AT_SEND_CMD(resp, "AT+CWMODE=1");
/* get module version */
AT_SEND_CMD(resp, "AT+GMR");
/* show module version */
for (i = 0; i < resp->line_counts - 1; i++)
{
LOG_D("%s", at_resp_get_line(resp, i + 1));
}
/* connect to WiFi AP */
if (at_exec_cmd(at_resp_set_info(resp, 128, 0, 20 * RT_TICK_PER_SECOND), "AT+CWJAP=\"%s\",\"%s\"",
AT_DEVICE_WIFI_SSID, AT_DEVICE_WIFI_PASSWORD) != RT_EOK)
{
LOG_E("AT network initialize failed, check ssid(%s) and password(%s).", AT_DEVICE_WIFI_SSID, AT_DEVICE_WIFI_PASSWORD);
result = -RT_ERROR;
goto __exit;
}
AT_SEND_CMD(resp, "AT+CIPMUX=1");
__exit:
if (resp)
{
at_delete_resp(resp);
}
if (!result)
{
netdev_low_level_set_status(netdev_get_by_name(ESP8266_NETDEV_NAME), RT_TRUE);
LOG_I("AT network initialize success!");
}
else
{
netdev_low_level_set_status(netdev_get_by_name(ESP8266_NETDEV_NAME), RT_FALSE);
LOG_E("AT network initialize failed (%d)!", result);
}
}
边栏推荐
- How to convert planning map into vector data with longitude and latitude geojson
- 我们被一个 kong 的性能 bug 折腾了一个通宵
- 【静态代码质量分析工具】上海道宁为您带来SonarSource/SonarQube下载、试用、教程
- Refuse noise, the entry journey of earphone Xiaobai
- Enterprise digital transformation needs in-depth research, and it cannot be transformed for the sake of transformation
- [5 minutes paper] Pointer network
- 线程和进程
- R language Visual scatter diagram, geom using ggrep package_ text_ The rep function avoids overlapping labels between data points (set the min.segment.length parameter to 0 to add line segments to the
- In the changing era of equipment manufacturing industry, how can SCM supply chain management system enable equipment manufacturing enterprises to transform and upgrade
- [five minute paper] reinforcement learning based on parameterized action space
猜你喜欢

一文搞懂│XSS攻击、SQL注入、CSRF攻击、DDOS攻击、DNS劫持

Quanzhi a40i industrial core board, 100% domestic 4-core arm cortex-a7, supports "dual screen abnormal display" [display interface capability, preferred scheme for industrial HMI]

ES6高级-查询商品案例

数仓:爱奇艺数仓平台建设实践
![[five minute paper] reinforcement learning based on parameterized action space](/img/86/9deb43958b6bf7401f41f31f737cc9.png)
[five minute paper] reinforcement learning based on parameterized action space

关于我写的IDEA插件能一键生成service,mapper....这件事(附源码)
![[leetcode daily question] - 121. The best time to buy and sell stocks](/img/51/ae7c4d903a51d97b70d5e69c6fffaa.png)
[leetcode daily question] - 121. The best time to buy and sell stocks

二叉树的创建以及遍历

2022你的安全感是什么?沃尔沃年中问道

Zynq PS + PL heterogeneous multicore Case Development Manual of Ti C6000 tms320c6678 DSP + zynq-7045 (1)
随机推荐
线程和进程
TI C6000 TMS320C6678 DSP+ Zynq-7045的PS + PL异构多核案例开发手册(2)
TI C6000 TMS320C6678 DSP+ Zynq-7045的ZYNQ PS + PL异构多核案例开发手册(1)
Tool skill learning (I): pre skills -makfile, make,.Mk
中金财富炒股安全吗 手续费最便宜的证券公司
一文搞懂│XSS攻击、SQL注入、CSRF攻击、DDOS攻击、DNS劫持
【5分钟Paper】Pointer Network指针网络
企业数字化转型需要深入研究,不能为了转型而转型
工具技能学习(二):前置技能-shell
八叉树建立地图并实现路径规划导航
2023 catering industry exhibition, China catering supply chain exhibition and Jiangxi catering Ingredients Exhibition were held in February
Sword finger offer II 009. subarray with product less than k
反射、枚举以及lambda表达式
03 common set security classes under JUC
LeetCode_ Prefix and_ Hash table_ Medium_ 525. Continuous array
数仓:数仓建设中的数据建模和日志体系
拒绝噪声,耳机小白的入门之旅
Detailed explanation of nat/napt address translation (internal and external network communication) technology [Huawei ENSP]
关于我写的IDEA插件能一键生成service,mapper....这件事(附源码)
使用两个栈实现一个队列