[網絡篇]ESP8266-SDK教程(六)之網頁配置Wi-Fi名稱和密碼


這個周末有點忙,明天就是新的一周了,今晚更新一下文章!在上篇文章中有一點小小的歷史遺留問題,不知道大家有沒有自己實現出來,今天就給大家說一說網頁配置Wi-Fi是怎么實現的,最近也是比較忙,手頭有點小項目,今天抽點時間給大家講一下~

開始之前,照例我們還是先了解一下基礎知識吧,既然這里我們是用網頁就配置,那么我們就有必要了解一下瀏覽器與Web Server之間是怎樣通信的,相信每個人都有過在地址欄輸入網址的時候,而且會經常看見"http"、"https"等字樣,那么你有沒有深入去了解一下這幾個字母是什么意思呢?為什么網址的前面會加入這幾個字母?可能很多人都是知道有,但是具體是什么作用沒好好去了解,那么這里就簡單給大家科普一下,畢竟我也不是專業的對這塊知識也是停留在知道了解懂點的水平上。

首先我們需要先知道"http"、"https"是什么意思,戳卡片了解一下~

http-百度百科

https-百度百科

看完后是不是有點恍然大悟了?是不是覺得也沒那么高大上了?其實,就是一種傳輸協議,不過傳輸的內容多以網頁居多,http是在TCP/IP傳輸層之上的應用層協議,所以最根本的還是回到了TCP/IP通信,只不過http做了很多規范,包括請求格式,響應格式,狀態碼等等,http協議有一套很完整的規范,大家可以在這個規范之下傳輸數據,我們本篇文章雖然寫着是網頁配置,但是在界面之下的數據都是在http規范之下進行傳輸的,然后我們在Web Server(ESP8266)端進行數據解析,最后得到我們想要的信息。

先來簡單看一下流程吧,做了一個草圖,PS水平不佳,夠用水平~

 

 

 

流程就是這個樣子,通過網頁發送SSID和PWD給ESP8266,ESP8266解析以后得到SSID和PWD去連接Wi-Fi,流程沒多少東西,下面我們來看每一步是如何實現的!

首先是我們需要打開這個網頁,但是我們該怎么樣打開呢?網址是什么呢?可能大家會有這樣的疑問,但是你有沒有配置過家里的路由器呢?如果有的話,其實跟那個是一樣的道理,沒有配置的過的,我就給大家說一下,要知道的是在我們的ESP8266連接上路由器之前,我們是不可能在路由器這個局域網之下找到這個設備的,所以我們去訪問路由器下的子設備是不可能實現的,所以ESP8266應該是在AP模式,我們需要用手機或者PC去連接ESP8266,然后我們去訪問ESP8266在AP模式下的IP地址才能打開這個可以配置的網頁,就像我們家里的路由器,有一個固定的IP是去設置一些路由器參數的,這里原理是相同的。

然后我們打開這個網頁,但是這個網頁是保存在哪呢?其實就像你家里的路由器一樣,這個配置網頁都是保存在設備當中的,可能家里的路由器可存儲空間比較大,還跑着OpenWRT系統,但是我們ESP8266是一樣可以去實現的,我們需要將這個寫好的網頁,保存到ESP8266的flash當中,當ESP8266收到http請求時,我們再將這個網頁從flash中讀出來,發送給瀏覽器,可能有些人覺得有點不可思議,其實網頁本身就是一個文件,flash就是存儲文件的,我們既然可以寫固件,那么同樣可以寫很多別的東西,此時ESP8266就是一個很小的Web Server,處理來自瀏覽器的http請求,然后按照一定格式返回具體的網頁,或者其他數據,這些都是通過TCP傳輸的。

那么我們了解了整個流程,也知道了網頁是在哪保存着,那我們現在就卻一個網頁了,網頁不要寫的很復雜,因為ESP8266的flash是有限的,我們不能占用太大的地方,某些地方還是要存儲我們編譯好的程序的,大家應該也會寫一點簡單的網頁吧?HTML是超文本標記語言,通過一些標簽來實現特定的顯示內容或者格式,不懂的可以稍微看一下,或者想實現什么功能就是百度一下,當然我這里嘛,就是比較熟悉了,因為從初中開始我就對做一個個人主頁很感興趣,所以沒事也會看看這方面的知識,大家先看看這個界面吧,我也是簡單一寫。

就一個很簡單的表格,填完以后,按下按鍵,表格中的內容就會以http協議的格式發送給ESP8266,這不是重要的,我們看一下ESP8266端是如何處理我們通過網頁發送的數據。

直接上代碼吧~

  1 /*********************
  2  *      DEFINES
  3  *********************/
  4 #define INDEX_SIZE     4410
  5 #define WEBCONFIG_SIZE     4526
  6 #define WIFIDONE_SIZE    4266
  7 
  8 /**********************
  9  *      TYPEDEFS
 10  **********************/
 11 static struct espconn webserver_espconn;
 12 static esp_tcp webserver_esptcp;
 13 /**********************
 14  *  STATIC PROTOTYPES
 15  **********************/
 16 static void webconfig_get_wifi_ssid_pwd(char* urlparam);
 17 static void softAP_init(void);
 18 static bool parse_url(char *precv, URL_Frame *purl_frame);
 19 static void webserver_recv(void *arg, char *pusrdata, unsigned short length);
 20 static void data_send(void *arg, bool responseOK, char *psend);
 21 static bool parse_url(char *precv, URL_Frame *purl_frame);
 22 static bool save_data(char *precv, uint16 length);
 23 static bool check_data(char *precv, uint16 length);
 24 static void webserver_recon(void *arg, sint8 err);
 25 static void webserver_discon(void *arg);
 26 static void webserver_listen(void *arg);
 27 /************************
 28  *   STATIC VARIABLES   *
 29  ************************/
 30 static char *precvbuffer;
 31 static uint32 dat_sumlength = 0;
 32 
 33 /**********************
 34  *      MACROS
 35  **********************/
 36 
 37 /**********************
 38  *   GLOBAL FUNCTIONS
 39  **********************/
 40 void ICACHE_FLASH_ATTR
 41 user_webserver_init(uint32 port){
 42 
 43     softAP_init();
 44 
 45     webserver_espconn.type = ESPCONN_TCP;
 46     webserver_espconn.state = ESPCONN_NONE;
 47     webserver_espconn.proto.tcp = &webserver_esptcp;
 48     webserver_espconn.proto.tcp->local_port = port;
 49     espconn_regist_connectcb(&webserver_espconn, webserver_listen);
 50     espconn_regist_reconcb(&webserver_espconn,webserver_recon);
 51 
 52     espconn_accept(&webserver_espconn);
 53 }
 54 
 55 /**********************
 56  *   STATIC FUNCTIONS
 57  **********************/
 58 /*
 59  * softAP模式初始化代碼
 60  */
 61 static void ICACHE_FLASH_ATTR
 62 softAP_init(void){
 63     struct softap_config soft_ap_Config;
 64 
 65     wifi_set_opmode_current(SOFTAP_MODE);//設置為AP模式,不保存到flash
 66 //    wifi_set_opmode(SOFTAP_MODE);//設置為AP模式,並保存到flash
 67 
 68     soft_ap_Config.ssid_len = 14;//熱點名稱長度,與你實際的名稱長度一致就好
 69     os_strcpy(soft_ap_Config.ssid,"zhihu-IAMLIUBO");//實際熱點名稱設置,可以根據你的需要來
 70     os_strcpy(soft_ap_Config.password,"imliubo123");//熱點密碼設置
 71     soft_ap_Config.authmode = AUTH_WPA2_PSK;//設置權限模式,AUTH_WPA2_PSK這是一種加密算法
 72     soft_ap_Config.beacon_interval = 100;//信標間隔,默認為100
 73     soft_ap_Config.channel = 1;//信道,共支持1~13個信道
 74     soft_ap_Config.max_connection = 2;//最大連接數量,最大支持四個,默認四個
 75     soft_ap_Config.ssid_hidden = 0;//隱藏SSID,0:不隱藏  1:隱藏
 76 
 77     wifi_softap_set_config_current(&soft_ap_Config);//設置 Wi-Fi SoftAP 接口配置,不保存到 Flash
 78 //    wifi_softap_set_config(&soft_ap_Config);//設置 Wi-Fi SoftAP 接口配置,保存到 Flash
 79 
 80     os_printf("\r\nSSID: zhihu-IAMLIUBO\r\nPWD: imliubo123\r\n");
 81 }
 82 
 83 static void ICACHE_FLASH_ATTR
 84 webserver_recv(void *arg, char *pusrdata, unsigned short length)
 85 {
 86     URL_Frame *pURL_Frame = NULL;
 87     char *pParseBuffer = NULL;
 88     char *html = NULL;
 89     SpiFlashOpResult ret = 0;
 90 
 91     os_printf("\r\n\r\nlength:%d\r\n", length);
 92     os_printf("recv:%s", pusrdata);
 93 
 94 
 95     pURL_Frame = (URL_Frame *)os_zalloc(sizeof(URL_Frame));
 96 
 97     parse_url(pusrdata, pURL_Frame);
 98     os_printf("\r\nType[%d]\r\n", pURL_Frame->Type);
 99     os_printf("pSelect[%s]\r\n", pURL_Frame->pSelect);
100     os_printf("pCommand[%s]\r\n", pURL_Frame->pCommand);
101     os_printf("pFilename[%s]\r\n", pURL_Frame->pFilename);
102 
103     switch (pURL_Frame->Type) {
104         case GET:
105             os_printf("We have a GET request.\n");
106                 if(pURL_Frame->pFilename[0] == 0){
107                     html = (char *)os_zalloc(INDEX_SIZE);
108                     if(html == NULL){
109                         os_printf("os_zalloc error!\r\n");
110                         goto _temp_exit;
111                     }
112                     // Flash read/write has to be aligned to the 4-bytes boundary
113                     ret = spi_flash_read(508*4096, (uint32 *)html, INDEX_SIZE);  // start address:0x10000 + 0xC0000
114                     if(ret != SPI_FLASH_RESULT_OK){
115                         os_printf("spi_flash_read err:%d\r\n", ret);
116                         os_free(html);
117                         html = NULL;
118                         goto _temp_exit;
119                     }
120                     html[INDEX_SIZE] = 0;   // put 0 to the end
121                     data_send(arg, true, html);
122                     os_free(html);
123                     html = NULL;
124                 }
125                 if(strncmp(pURL_Frame->pFilename, "WebConfig.html", strlen("WebConfig.html")) == 0){
126                     html = (char *)os_zalloc(WEBCONFIG_SIZE);
127                     if(html == NULL){
128                         os_printf("os_zalloc error!\r\n");
129                         goto _temp_exit;
130                     }
131                     // Flash read/write has to be aligned to the 4-bytes boundary
132                     ret = spi_flash_read(510*4096, (uint32 *)html, WEBCONFIG_SIZE);  // start address:0x10000 + 0xC0000
133                     if(ret != SPI_FLASH_RESULT_OK){
134                         os_printf("spi_flash_read err:%d\r\n", ret);
135                         os_free(html);
136                         html = NULL;
137                         goto _temp_exit;
138                     }
139                     html[WEBCONFIG_SIZE] = 0;   // put 0 to the end
140                     data_send(arg, true, html);
141                     os_free(html);
142                     html = NULL;
143                 }
144             break;
145 
146         case POST:
147             os_printf("We have a POST request.\r\n");
148             if(strncmp(pURL_Frame->pCommand, "connect-wifi", strlen("connect-wifi")) == 0){
149                 os_printf("connect wifi\r\n");
150                 webconfig_get_wifi_ssid_pwd(pusrdata);
151                 html = (char *)os_zalloc(WIFIDONE_SIZE);
152                 if(html == NULL){
153                     os_printf("os_zalloc error!\r\n");
154                     goto _temp_exit;
155                 }
156                 ret = spi_flash_read(512*4096, (uint32 *)html, WIFIDONE_SIZE);  // start address:0x10000 + 0xC0000
157                 if(ret != SPI_FLASH_RESULT_OK){
158                     os_printf("spi_flash_read err:%d\r\n", ret);
159                     os_free(html);
160                     html = NULL;
161                     goto _temp_exit;
162                 }
163                 html[WIFIDONE_SIZE] = 0;   // put 0 to the end
164                 data_send(arg, true, html);
165                 os_free(html);
166                 html = NULL;
167             }
168             break;
169     }
170     _temp_exit:
171         ;
172     if(pURL_Frame != NULL){
173         os_free(pURL_Frame);
174         pURL_Frame = NULL;
175     }
176 
177 }
178 
179 static void ICACHE_FLASH_ATTR
180 webconfig_get_wifi_ssid_pwd(char* urlparam)
181 {
182     char *p = NULL, *q = NULL;
183     char ssid[10], pass[15];
184 
185     os_memset(ssid, 0, sizeof(ssid));
186     os_memset(pass, 0, sizeof(pass));
187 
188     p = (char *)os_strstr(urlparam, "SSID=");
189     q = (char *)os_strstr(urlparam, "PASSWORD=");
190     if ( p == NULL || q == NULL ){
191         return;
192     }
193     os_memcpy(ssid, p + 5, q - p - 6);
194     os_memcpy(pass, q + 9, os_strlen(urlparam) - (q - urlparam) - 9);
195     os_printf("ssid[%s]pass[%s]\r\n", ssid, pass);
196 
197     wifi_set_opmode(STATION_MODE);
198     struct station_config stConf;
199     stConf.bssid_set = 0;
200     os_memset(&stConf.ssid, 0, sizeof(stConf.ssid));
201     os_memset(&stConf.password, 0, sizeof(stConf.password));
202 
203     os_memcpy(&stConf.ssid, ssid, os_strlen(ssid));
204     os_memcpy(&stConf.password, pass, os_strlen(pass));
205 
206     wifi_station_set_config(&stConf);
207     //重啟
208     system_restart();
209 }
210 /******************************************************************************
211  * FunctionName : parse_url
212  * Description  : parse the received data from the server
213  * Parameters   : precv -- the received data
214  *                purl_frame -- the result of parsing the url
215  * Returns      : none
216 *******************************************************************************/
217 static bool ICACHE_FLASH_ATTR
218 parse_url(char *precv, URL_Frame *purl_frame)
219 {
220     char *str = NULL;
221     uint8 length = 0;
222     char *pbuffer = NULL;
223     char *pbufer = NULL;
224 
225     if (purl_frame == NULL || precv == NULL) {
226         return false;
227     }
228 
229     pbuffer = (char *)os_strstr(precv, "Host:");
230 
231     if (pbuffer != NULL) {
232         length = pbuffer - precv;
233         pbufer = (char *)os_zalloc(length + 1);
234         pbuffer = pbufer;
235         os_memcpy(pbuffer, precv, length);
236         os_memset(purl_frame->pSelect, 0, URLSize);
237         os_memset(purl_frame->pCommand, 0, URLSize);
238         os_memset(purl_frame->pFilename, 0, URLSize);
239 
240         if (os_strncmp(pbuffer, "GET ", 4) == 0) {
241             purl_frame->Type = GET;
242             pbuffer += 4;
243         } else if (os_strncmp(pbuffer, "POST ", 5) == 0) {
244             purl_frame->Type = POST;
245             pbuffer += 5;
246         }else{
247             return false;
248         }
249 
250         pbuffer ++;
251         str = (char *)os_strstr(pbuffer, "HTTP");
252 
253         if (str != NULL) {
254             length = str - pbuffer - 1;
255             os_memcpy(purl_frame->pFilename, pbuffer, length);
256         }
257 
258         os_free(pbufer);
259     }
260 
261     pbuffer = (char *)os_strstr(precv, "SSID");
262     if (pbuffer != NULL) {
263         purl_frame->Type = POST;
264         os_memcpy(purl_frame->pCommand, "connect-wifi", strlen("connect-wifi"));
265         os_free(pbufer);
266     }
267 
268 }
269 /******************************************************************************
270  * FunctionName : data_send
271  * Description  : processing the data as http format and send to the client or server
272  * Parameters   : arg -- argument to set for client or server
273  *                responseOK -- true or false
274  *                psend -- The send data
275  * Returns      :
276 *******************************************************************************/
277 static void ICACHE_FLASH_ATTR
278 data_send(void *arg, bool responseOK, char *psend)
279 {
280     uint16 length = 0;
281     char *pbuf = NULL;
282     char httphead[256];
283     struct espconn *ptrespconn = arg;
284     os_memset(httphead, 0, 256);
285 
286     if (responseOK) {
287         os_sprintf(httphead,
288                    "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nServer: lwIP/1.4.0\r\n",
289                    psend ? os_strlen(psend) : 0);
290 
291         if (psend) {
292             os_sprintf(httphead + os_strlen(httphead),
293                        "Content-type: text/html; charset=utf-8\r\nPragma: no-cache\r\n\r\n");
294             length = os_strlen(httphead) + os_strlen(psend);
295             pbuf = (char *)os_zalloc(length + 1);
296             os_memcpy(pbuf, httphead, os_strlen(httphead));
297             os_memcpy(pbuf + os_strlen(httphead), psend, os_strlen(psend));
298         } else {
299             os_sprintf(httphead + os_strlen(httphead), "\n");
300             length = os_strlen(httphead);
301         }
302     } else {
303         os_sprintf(httphead, "HTTP/1.0 400 BadRequest\r\nContent-Length: 0\r\nServer: lwIP/1.4.0\r\n\n");
304         length = os_strlen(httphead);
305     }
306 
307     if (psend) {
308         espconn_sent(ptrespconn, pbuf, length);
309     } else {
310         espconn_sent(ptrespconn, httphead, length);
311     }
312 
313     if (pbuf) {
314         os_free(pbuf);
315         pbuf = NULL;
316     }
317 }

其中webserver_recv函數就是處理瀏覽器發來的各種http請求,我這里實際寫了三個頁面:首頁、配置頁面、配置完成頁面,所以處理的有點多,spi_flash_read函數是將保存在flash中的網頁讀出來,可以看出我是從三個不同的地址讀出來的網頁,這個地址是根據你將網頁寫在了什么位置而去計算的,可以看一下我將三個網頁燒錄在了什么位置:

 

其中makingfun.html就是首頁,我是燒錄在了0x1FC000地址,那在代碼中是從哪一個地方讀出的呢?

spi_flash_read(508*4096, (uint32 *)html, INDEX_SIZE); 

就是508x4096這個地址,0x1FC000是508x4096結果的十六進制,在ESP8266 flash操作中是4字節對齊的,一個扇區4KB,每次擦寫都需要整個扇區去操作。我們后面再詳細說一下關於flash操作的一些注意事項。

parse_url函數是解析http請求中攜帶的URL參數、請求方法、請求的文件名稱,主要使用的是C庫中的字符串處理函數,然后根據http請求格式中固定的字段去解析出我們實際傳進來的參數。

webconfig_get_wifi_ssid_pwd函數就是解析我們填在表格中的Wi-Fi名稱和密碼了,也是使用的C庫中的字符串處理函數,其中“SSID”和“PASSWORD”是表格屬性,等號后邊是我們填寫的內容,“&”不是我們填寫的,所以計算的時候需要將它剔除掉。

最后還需要注意一點的是,這個三個宏定義:

1 #define INDEX_SIZE     1437
2 #define WEBCONFIG_SIZE     1541
3 #define WIFIDONE_SIZE    1293

這是我們寫好的網頁的實際大小,也就是我們實際寫到flash當中的網頁大小,這里需要根據你實際的網頁大小去更改。

下面我們看一下三個頁面的HTML代碼:

首頁代碼:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
        <title>
            IAMLIUBO的神奇物聯網之旅
        </title>
    </head>
    <body>
        <div align="center">
            <font>
                IAMLIUBO
            </font>
            <br/>
            <font>
                E-mail: imliubo@makingfun.xyz
            </font>
            <br/>
            <a href="https://zhuanlan.zhihu.com/imliubo-magic-IoT-Tutorial">
                IAMLIUBO的神奇物聯網之旅
            </a>
            <br/>
            <p>
                歡迎關注我的知乎專欄,我會在專欄不定期分享一些文章,其中有包括物聯網方面的,也有會有一些自己開發經驗分享,關注專欄,與我一起進步!
                <br/>
                唯有愛與科技不可辜負。
                <br/>
                技術交流或者項目合作可以私信或者郵件聯系我。
                <br/>
                點擊按鈕開始配網
            </p>
            <a href="WebConfig.html" text-decoration="none">
                <button formtarget="_self" style="display:block;margin:0 auto">
                    開始配網
                </button>
            </a>
        </div>
    </body>
</html>

配置網頁代碼:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
        <title>
            IAMLIUBO的神奇物聯網之旅
        </title>
    </head>
    <body>
        <div align="center">
            <font>
                IAMLIUBO
            </font>
            <br/>
            <font>
                E-mail: imliubo@makingfun.xyz
            </font>
            <br/>
            <a href="https://zhuanlan.zhihu.com/imliubo-magic-IoT-Tutorial">
                IAMLIUBO的神奇物聯網之旅
            </a>
            <br/>
        </div>
        <form action="WiFiConfig.html" enctype="application/x-www-form-urlencoded" method="post">
            <table align="center" border="0" cellspacing="10">
                <tr>
                    <td>
                        Wi-Fi名稱:
                        <input name="SSID" placeholder="在這里輸入Wi-Fi名稱" type="text"/>
                    </td>
                </tr>
                <tr>
                    <td>
                        Wi-Fi密碼:
                        <input name="PASSWORD" placeholder="在這里輸入Wi-Fi密碼" type="password"/>
                    </td>
                </tr>
            </table>
            <button style="display:block;margin:0 auto" type="submit" value="Submit">
                確認提交
            </button>
        </form>
    </body>
</html>

配網完成頁面:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
        <title>
            IAMLIUBO的神奇物聯網之旅
        </title>
    </head>
    <body>
        <div align="center">
            <font>
                IAMLIUBO
            </font>
            <br/>
            <font>
                E-mail: imliubo@makingfun.xyz
            </font>
            <br/>
            <a href="https://zhuanlan.zhihu.com/imliubo-magic-IoT-Tutorial">
                IAMLIUBO的神奇物聯網之旅
            </a>
            <br/>
            <font>
                正在連接Wi-Fi,LED燈閃爍三次后連接完成!
                <br/>
                常亮表示連接失敗請重新輸入!
                <br/>
                <a href="WebConfig.html" text-decoration="none">
                    <button formtarget="_self" style="display:block;margin:0 auto">
                        重新配網
                    </button>
                </a>
                <br/>
                <a href="/">
                    返回首頁
                </a>
            </font>
        </div>
    </body>
</html>

上個演示視頻:

嗶哩嗶哩-IAMLIUBO

視頻可以去B站看了,后期比較長一點的視頻都會放在B站上,因為知乎最長可以上傳15分鍾的視頻,最大1G,所以有些比較長的視頻就沒法直接上傳了,請諒解,代碼稍后整理后我會上傳到我的GitHub倉。

最后歡迎大家去我的倉庫點個Star,您的鼓勵是我最大的動力~有問題可以私信我,或者提交issues。

Github-IAMLIUBO

QQ交流群:592587184


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM