對於ESP32,其作為一款集成了2.4GHz WiFi和藍牙雙模塊的單芯片,所有基於wifi和藍牙開發是學習esp32的重要一環,今天WiFi原理和網絡結構 可以點擊鏈接進行詳細的了解,這里就不做詳細的敘述了,本文重點講解省電模式下的WiFi是如何連接上路由器的,重點是相關API接口和編程方法的介紹。本文源碼地址在:esp-idf/examples/wifi/power_save里。源碼完成了對ESP32的低功耗模式的設置,並通過menuconfig將接入點AP的名稱和密碼賦值給ESP32,使ESP32作為一個站點STA接入到接入點AP(即路由器)中。
PART1:
定義基本參數
/*set the ssid and password via "make menuconfig"*/ #define DEFAULT_SSID CONFIG_WIFI_SSID #define DEFAULT_PWD CONFIG_WIFI_PASSWORD #if CONFIG_POWER_SAVE_MODEM #define DEFAULT_PS_MODE WIFI_PS_MODEM #elif CONFIG_POWER_SAVE_NONE #define DEFAULT_PS_MODE WIFI_PS_NONE #else #define DEFAULT_PS_MODE WIFI_PS_NONE #endif /*CONFIG_POWER_SAVE_MODEM*/
這里首先將AP端的名稱和密碼賦值給ESP32,使ESP32可以連接上接入點AP,這里的CONFIG_WIFI_SSID和CONFIG_WIFI_PASSWORD即為路由器端的名稱和密碼,他們在源碼中是看不到的,它們的定義是在Kconfig.projbuild中定義的,我們可以通過make menconfig對其進行賦值。具體操作如下:
選擇Example Configuration后
在WIFI SSID和WIFI Password中分別將路由器的名稱和密碼賦值給ESP32。
(當然,你也可以不通過menucofig而對ESP32直接進行賦值)
緊隨其后的便是對ESP32工作模式的設置,同樣,其也可以通過menuconfig進行設置。
PART2:
進程打印函數
static const char *TAG = "power_save"; static esp_err_t event_handler(void *ctx, system_event_t *event) { switch(event->event_id) { case SYSTEM_EVENT_STA_START: ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START"); ESP_ERROR_CHECK(esp_wifi_connect()); break; case SYSTEM_EVENT_STA_GOT_IP: ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP"); ESP_LOGI(TAG, "got ip:%s\n", ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); break; case SYSTEM_EVENT_STA_DISCONNECTED: ESP_LOGI(TAG, "SYSTEM_EVENT_STA_DISCONNECTED"); ESP_ERROR_CHECK(esp_wifi_connect()); break; default: break; } return ESP_OK; }
本部分主要是將ESP32的工作信息,打印出來,對返回的任務通知進行switch分析,如果連接上了,就打印sta_start消息,並再次執行esp_err_t esp_wifi_connect
( void )將ESP32 WiFi站連接到AP,第二次得到返回任務通知SYSTEM_EVENT_STA_GOT_IP,並調用 ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));將數字IP地址轉換為十進制點ASCII表示法。,此時顯示連接到的AP的IP和MAC地址。
如果沒有連接上AP,同樣會一直執行esp_err_t esp_wifi_connect
( void ),直到將ESP32 WiFi站連接到AP為止。
PART3:
WIFI設置和耗電設置
/*init wifi as sta and set power save mode*/ static void wifi_power_save(void) { tcpip_adapter_init(); ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); wifi_config_t wifi_config = { .sta = { .ssid = DEFAULT_SSID, .password = DEFAULT_PWD }, }; 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, "esp_wifi_set_ps()."); esp_wifi_set_ps(DEFAULT_PS_MODE); }
wifi_power_save首先調用 tcpip_adapter_init();函數對底層庫的TCP/IP協議進行調用,然后檢測esp_event_loop_init是否初始化完成。
之后便是進行wifi的設置,首先用esp_wifi_init(&cfg)對WIFI的內存空間進行設置,初始化WiFi Alloc資源為WiFi驅動,如WiFi控制結構,RX / TX緩沖區,WiFi NVS結構等,此WiFi也啟動WiFi任務。(注意;在調用所有其他WiFi API之前,必須先調用此API)
然后設置ESP32 STA或AP的配置。
wifi_config_t wifi_config = { .sta = { .ssid = DEFAULT_SSID, //設置要連接的AP的接入點名稱和密碼 .password = DEFAULT_PWD }, };
- (注意
- 1.只有當指定的接口被啟用時,才能調用這個API,否則API會失敗
- 2.對於站配置,bssid_set需要為0; 只有當用戶需要檢查AP的MAC地址時,才需要1。
- 3. ESP32僅限一個通道,因此在軟AP +站模式下,軟AP將自動調整其通道與ESP32站的通道相同。)
通過esp_wifi_set_mode(WIFI_MODE_STA)將WiFi操作模式設置為站,軟AP或站+軟AP,默認模式為軟AP模式。
esp_wifi_set_config設置ESP32 STA或AP的配置。
最后通過esp_wifi_start()根據當前配置啟動WiFi,
並通過 esp_wifi_set_ps(DEFAULT_PS_MODE);設置當前節電類型。
PART4:
APP_main函數
void app_main() { // Initialize NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK( ret ); wifi_power_save(); }
app_main函數主要是對NVS完成基本的初始化操作(關於NVS,可以在ESP32匯總中進行詳細了解),保證數據的緩存空間,然后調用 wifi_power_save();函數完成WIFI設置。
PART5:
實驗現象
程序燒寫完成后,打開minicom,可以看到如下的打印信息
打開windows的cmd(這里筆記本和ESP32接入的是同一個AP),對AP分配的ESP32的IP進行ping操作,觀察能否PING通。操作結果如下:
至此,基於省電模式的WIFI鏈接就設計完成了。
PART6:編程詳情
一旦ESP32已經設置了站點配置細節,其中包括SSID和password,我們准備好連接到目標訪問點后。 功能 esp_wifi_connect() 將形成的連接。你連接了后ESP32中的任何內容都不會阻塞,同樣也不會影響到這個功能。
在一段時間以后,當其實際的連接起來后,我們會看到兩個回調事件發生, 首先是 SYSTEM_EVENT_STA_CONNECTED 表明我們有連接到接入點。 第二個事件是 SYSTEM_EVENT_STA_GOT_IP 其表示我們已經被DHCP服務器分配了一個IP地址。只有這樣我們才能真正參與通訊。如果我們正在使用靜態IP地址,那么我們只會看到連接的事件。
我們從接入點斷開連接時,我們將看到一個SYSTEM_EVENT_STA_DISCONNECTED 事件。從先前連接的 斷開接入點我們調用esp_wifi_disconnect()完成,
關於與接入點連接的進一步考慮是自動連接的想法。 有一個布爾標志存儲在閃存中指示ESP32是否應嘗試自動連接到最后一個使用的接入點。 如果設置為true,那么之后在設備啟動后,你無需調用任何API函數,它將嘗試連接到最后使用的接入點。 這是一個
方便選項,但是我更喜歡關閉。 通常我想在我的設備中進行控制來確定是否自動連接,是否自動連接,我們可以通過調用esp_wifi_set_auto_connect()。
另外,當我們連接到接入點時,我們的設備正在成為一個station。 連接到接入點AP不是自動的,意味着我們現在有一個IP地址。 我們堅持必須從DHCP服務器請求已建立的IP地址。 這可能需要幾秒。在某些情況下,我們可以讓設備請求特定的IP。 這可以更快的連接時間。 如果我們指定數據,我們也需要提供DNS信息,如果我們需要連接到DNS服務器的名字解析度。
這是分配給我們一個特定IP地址的邏輯片段: #include <lwip / sockets.h> //我們希望我們的設備擁有的IP地址。 #define DEVICE_IP“192.168.1.99” //我們希望發送數據包的網關地址 //這通常是我們的接入點。 的#define DEVICE_GW “192.168.1 1” //網絡掩碼規范。 #define DEVICE_NETMASK“255.255.255.0” //我們希望連接的接入點的身份。 #define AP_TARGET_SSID“RASPI3” //我們需要提供給接入點進行授權的密碼。 #define AP_TARGET_PASSWORD“password” esp_err_t wifiEventHandler(void * ctx,system_event_t * event) { 返回ESP_OK; } //代碼片段在這里... nvs_flash_init(); tcpip_adapter_init(); tcpip_adap ter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); tcpip_adapter_ip_info_t ipInfo; inet_pton(AF_INET,DEVICE_IP,&ipInfo.ip); inet_pton(AF_INET,DEVICE_GW,&ipInfo.gw); inet_pton(AF_INET,DEVICE_NETMASK,&ipInfo.netmask); tcpip_ada pter_set_ip_info(TCPIP_ADAPTER_IF_STA,&ipInfo); ESP_ERROR_CHECK(esp_event_loop_init(wifiEventHandler,NULL)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_ST ORAGE_RAM)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); wifi_config_t sta_config = { .sta = { .ssid = AP_TARGET_SSID, .password = AP_TARGET_PASSWORD, .bssid_set = 0 } }; ESP_ERROR_CHECK(esp_wifi_set_config(WI FI_IF_STA,&sta_config)); ESP_ERROR_CHECK(esp_wifi_start()); ESP_ERROR_CHECK(esp_wifi_connect());
作為接入點AP
到目前為止,我們只將ESP32作為了接入接入點的WiFi站
但它也具有作為一個接入點使其他WiFi設備(站)連接的能力 。為了成為一個接入點,我們需要定義允許其他的SSID設備來區分我們的網絡。 這個SSID可以被標記為為hidden, 如果我們不希望它在掃描中找到。 另外我們還要提供認證方式當台站希望與我們聯系時,將使用該功能。 這是用來允許
的,實際的將ES32實例作為接入點AP請看下一篇文章
ESP32作為接入點AP
相關知識:wifi相關的API接口