ESP8266 RTOS SDK 自带样例解析(二)

在本文中,我们通过样例来分析 ESP8266 连接 Wifi 的过程及需要用都的 API。

该程序在 SDK 中 examples/wifi/getting_started/station 目录中。项目包含一个 C 语言的源文件 - hello_world_main.c(位于 main 目录中), 其它的文件是 Make 构建系统需要的配置文件。

主程序解析

打开 station_example_main.c 文件,入口函数 app_main 位于文件末尾,代码如下:

1
2
3
4
5
6
7
void app_main()
{
ESP_ERROR_CHECK(nvs_flash_init());

ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
wifi_init_sta();
}

首先,调用了系统 API: nvs_flash_init , 用来初始化默认的 “nvs” 分区。

然后就调用自定义的 wifi_init_sta 来启动 Wifi 连接。

启动 Wifi

首先看函数 wifi_init_sta 的实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();

tcpip_adapter_init();

ESP_ERROR_CHECK(esp_event_loop_create_default());

wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));

ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));

wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );

ESP_LOGI(TAG, "wifi_init_sta finished.");

/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);

/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}

ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));
vEventGroupDelete(s_wifi_event_group);
}

可以看到,程序首先通过 xEventGroupCreate 构建了一个事件组,该 API 是 RTOS 系统 API 的一个。

接下来,程序调用了 tcpip_adapter_init,构建 TCPIP 栈。

然后调用 esp_event_loop_create_default 构建默认的消息(事件)队列。

接着,初始化 Wifi 设备驱动,其实是为运行 Wifi 准备相关的数据结构,存储空间,该调用所涉及到的参数比较多,因此建议使用预定义的宏,代码:

1
2
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));

接下来的两行代码注册事件(消息)处理行数,

1
2
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));

这里我们注册了连个类型的事件: WIFI_EVENT - Wifi 相关事件 和 IP_EVENT - ip 相关事件,这两类事件又包含多个具体的事件,这就由第二个参数来标识: ESP_EVENT_ANY_ID - 表示接收类别下所有的事件,对于 WIFI_EVENT 来说就可以是: WIFI_EVENT_STA_START, WIFI_EVENT_STA_DISCONNECTED 等, IP_EVENT_STA_GOT_IP - 表示接收获得IP的事件。

再下面的代码是设定所要连接的AP的SSID和密码(该值可以通过 make menuconfig 进行设置),并设置 Wifi 工作模式,然后启动。

1
2
3
4
5
6
7
8
9
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );

ESP_ERROR_CHECK(esp_wifi_start() ); 这行代码启动 Wifi, 事件处理程序中的代码是异步执行的

因为这是一个演示程序,所以执行完成以后需要停止Wifi的连接,并打印是否连接上网络。

下面,执行一个租塞语句,要等待异步执行的事件处理结果,代码:

1
2
3
4
5
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);

该预计在 s_wifi_event_group 中等待 WIFI_CONNECTED_BIT 或 WIFI_FAIL_BIT 标致。

后续的代码就是打印联网结果及清理现场了。

事件处理

在函数 wifi_init_sta 中,注册了两个事件处理,都使用同样的事件处理函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG,"connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:%s",
ip4addr_ntoa(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}

可以看到,当 WIFI_EVENT_STA_START 事件触发时,调用API: esp_wifi_connect 建立连接

当 WIFI_EVENT_STA_DISCONNECTED 事件触发时,如果重连次数小于设定的最多重连次数,则调用 esp_wifi_connect 重建连接,否则设置调用 xEventGroupSetBits,触发主线程中调用 xEventGroupWaitBits 的返回。

同样,如果发生 IP_EVENT_STA_GOT_IP 事件,则获取 IP 地址。

本文标题:ESP8266 RTOS SDK 自带样例解析(二)

文章作者:晨星

发布时间:2020年04月20日 - 10:04

最后更新:2020年09月16日 - 08:09

原始链接:https://www.mls-tech.info/iot/esp8266-examples-02/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。