日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【蒲公英技术征文】如何在 ESP-12F/ESP8266 上实现 webserver

發布時間:2024/4/13 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【蒲公英技术征文】如何在 ESP-12F/ESP8266 上实现 webserver 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文將演示如何在一個 ESP-12F 模塊上實現webserver,并且可以通過web請求對與模塊連接的繼電器進行控制。

0.寫在前面

首先,假設本文的讀者了解C語言、邏輯電路和HTTP協議。再次,本文適合物聯網開發者和有意向涉及物聯網項目的web開發者、移動開發者閱讀 。最后,如果你只需要了解實現過程,你可以繼續往下看,如果你想親自體驗這神奇的過程,除了常用的一些裝備和動手能力以外你還要需要準備以下材料。

ESP-12F 是基于 Espressif ESP8266芯片開發的WIFI控制模塊,支持802.11 b/g/n/e/i標準并集成了Tensilica L106 32位控制器、4 MB Flash 和 64 KB SRAM。

ESP-12F 模塊

Espressif 為 ESP8266 已經移植好了操作系統并且在github 上開放了sdk,這個SDK已經實現了TCP/IP,只需要實現http協議就可以完成webserver的功能。

本例涉及的所有資料和代碼在本文最后一節都提供了參考鏈接,由于筆者能力有限,本文內難免會有一些錯誤,也請各位讀者積極糾正。

1.開發環境

ESP-12F在Linux或Mac OS 下開發并在Windows下燒錄會更容易。 官網提供了安裝好開發環境的虛擬機鏡像。安裝和配置開發環境不在本文討論范圍內,本文最后一章提供的鏈接會有很大幫助。

本文使用的開發環境是 CentOS7 / crosstool-NG / ESP8266_RTOS_SDK

注意: 如果不擅長自己配置開發環境,esp-open-sdk項目中的Readme會指導如何配置開發環境并創建項目。

2.硬件的連接和燒錄

按照官方提供的描述連接線路即可,使用面包板和杜邦線連接可以有助于重復使用器件。本文尾提供的鏈接會很大有幫助。

注意:

燒錄時需要更改連接到下載模式,否則無法寫入程序。燒錄以后需要更改連接到flash boot模式,否則將無法boot。
燒錄過程中需要上電同步,可以給模塊掉電在加電也可以把模塊RST端接地超過一秒重啟模塊。
ESP-12F是3.3 V 電源供電,使用5V電源或USB供電的同學需要裝備5V-3.3V 電源轉換模塊。

使用杜邦線連接以便重復利用模塊

3.測試硬件狀態并了解開發流程

在正式開發之前,需要測試硬件是否工作正常。由于ESP-12F不具備任何顯示部件,因此調試需要借助串口打印信息。我們在 user/user_main.c 內寫入如下代碼初始化串口并向串口打印一條信息。同時你還需要鏈接wifi網絡。

代碼3-1: 初始化串口并打印調試信息

// 初始化UART 用戶需要按照相同的設置設置串口調試工具UART_WaitTxFifoEmpty(UART0); UART_WaitTxFifoEmpty(UART1);UART_ConfigTypeDef uart_config;uart_config.baud_rate = BIT_RATE_115200; //波特率uart_config.data_bits = UART_WordLength_8b; //字長度uart_config.parity = USART_Parity_None; //校驗位uart_config.stop_bits = USART_StopBits_1; //停止位uart_config.flow_ctrl = USART_HardwareFlowControl_None;uart_config.UART_RxFlowThresh = 120;uart_config.UART_InverseMask = UART_None_Inverse;UART_ParamConfig(UART0, &uart_config);UART_SetPrintPort(UART0);// 向串口輸出一條信息printf("Hello World");

代碼3-2:初始化wifi連接

// init wifi connectionwifi_set_opmode(STATION_MODE);struct station_config * wifi_config = (struct station_config *) zalloc(sizeof(struct station_config));sprintf(wifi_config->ssid, "your wifi ssid");sprintf(wifi_config->password, "your wifi password");wifi_station_set_config(wifi_config);free(wifi_config);wifi_station_connect();

注意:

需要先打開串口工具再boot模塊,否則會漏掉一些調試內容。
wifi鏈接創建好后在路由器管理界面就可以看到IP地址了。

4.創建Socket并等待連接

ESP8266_RTOS_SDK 提供了基于lwip 的Socket API,我們只需要簡單調用即可實現創建Socket并綁定端口的過程。

代碼4-1:創建socket并綁定端口

int32 listenfd; int32 ret; struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET; //IPV4server_addr.sin_addr.s_addr = INADDR_ANY; //任意訪問IPserver_addr.sin_len = sizeof(server_addr);server_addr.sin_port = htons(80); //綁定端口 do{listenfd = socket(AF_INET, SOCK_STREAM, 0);//創建socket } while (listenfd == -1); do{ret = bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); //綁定端口 } while (ret != 0); do{ret = listen(listenfd, SOT_SERVER_MAX_CONNECTIONS); //開始監聽端口 } while (ret != 0);

5.處理request

當綁定端口成功以后 accept() 方法就會阻塞程序運行,直到有訪問請求。當有連接進入的時候(假設是沒有request body的GET請求),就可以獲得request的ID,并且通過 read() 獲取request header。當判斷request header完成后,即可通過 write() 方法向socket輸出response header和 response body,當這一切都完成的時候,就可以使用close() 關閉連接。至此,一個request處理完成。

注意:

我們無法實現判斷request header的長度,而read()方法會阻塞程序運行,因此我們需要判斷request header 是否完成以確定是否開始向socket寫入response。
對與有 request body 的請求來說,需要解析request header 中的 content-length 字段以獲取request body的程度,從而判斷request body 是否結束以防止 read() 方法阻塞程序。
在獲取request header 的過程中必須要獲取第一行報頭的內容以確定請求類和需要訪問的資源位置
關于報頭標準請參照 http://www.ietf.org/rfc/rfc26...

處理 request 的過程

代碼5-1:處理request

while((client_sock = accept(listenfd, (struct sockaddr *)&remote_addr, (socklen_t *)&len)) >= 0) {// recieveStatus 的含義 0. watting, 1. method get, 2. request URI get 3. finish recive 4. start send 5.send finishedint recieveStatus = 0;bool cgiRequest = true;char recieveBuffer;char *httpMethod = (char *)zalloc(8);int httpMethodLength = 0;char *httpRequestUri = (char *)zalloc(64);int httpRequestUriLength = 0;char *httpStopFlag = (char *)zalloc(4);int httpStopFlagLength = 0;httpMethod[0] = 0;httpRequestUri[0] = 0;httpStopFlag[0] = 0;// loop for recieve datafor(;;) {read(clientSock, &recieveBuffer, 1);if(recieveStatus == 0) {// 獲取請求方式if(recieveBuffer != 32) {httpMethod[httpMethodLength] = recieveBuffer;httpMethodLength ++;} else {httpMethod[httpMethodLength] = 0;recieveStatus = 1;}continue;}if(recieveStatus == 1) {// 獲取URIif(recieveBuffer != 32) {httpRequestUri[httpRequestUriLength] = recieveBuffer;httpRequestUriLength ++;} else {httpRequestUri[httpRequestUriLength] = 0;recieveStatus = 2;}continue;}if(recieveStatus == 2) {//判斷header是否結束,header結束標記是一個空行 因此檢測header最后4個字符是否是連續的\r\n\r\n即可if(recieveBuffer == 10 || recieveBuffer == 13) {httpStopFlag[httpStopFlagLength] = recieveBuffer;httpStopFlagLength ++;httpStopFlag[httpStopFlagLength] = 0;if(httpStopFlag[0] == 13 && httpStopFlag[1] == 10 && httpStopFlag[2] == 13 && httpStopFlag[3] == 10) {recieveStatus == 3;break;}} else {httpStopFlagLength = 0;httpStopFlag[httpStopFlagLength] = 0;}continue;}}// 向串口打印獲取的信息 可以判斷訪問是否正確printf("Method=%s SOCK=%d\n", httpMethod, clientSock);printf("URI=%s SOCK=%d\n", httpRequestUri, clientSock);printf("CGIRequestFlag=%d SOCK=%d\n", cgiRequest, clientSock);//輸出response headerwrite(clientSock, "HTTP/1.1 200 OK\r\n", strlen("HTTP/1.1 200 OK\r\n"));write(clientSock, "Server: SOTServer\r\n", strlen("Server: SOTServer\r\n"));write(clientSock, "Content-Type: text/plain; charset=utf-8\r\n", strlen("Content-Type: text/plain; charset=utf-8\r\n"));write(clientSock, "\r\n", 2);//輸出 respose bodywrite(clientSock, "Hello World", strlen("Hello World"));//關閉鏈接close(clientSock); }

6.規劃ROM文件系統

webserver 肯定是要能服務靜態文件的,現在需要手動創建文件系統,考慮到存儲器特點、片上資源和計算能力,文件系統被設計成只讀ROM并且文件的MIME,大小,路徑等信息被提前存到文件系統里。

ROM文件系統被分為兩個區域,從ROM文件系統開始前64KB被劃分為FAT區域,余下的區域都是文件數據存儲區;FAT區域被分為512個128B大小的文件條目存儲區,每個條目保存一條文件信息,其中前0x40 字節用于保存文件名,0x40-0x77 用于保存文件的MIME數據,0x78-0x7B 保存文件大小,0x7C-0x7F保存文件開頭部分相對于ROM首字節的相對偏移量也可以稱作文件的位置。

文件系統分配

注意

由于SPI Flash 讀數據需要4B對齊,所以ROM 系統內所有文件開始位置必須是4B對齊的。

7.制作靜態文件ROM

按照上節說到的文件系統,需要把一個特定目錄下的所有文件轉為一個單獨的二進制文件才可以燒錄到模塊上。這個過程需要先掃描目錄內所有文件并獲取文件名,再根據名文件名獲取文件相關屬性將所有的文件信息寫入ROM文件的FAT區,最后將文件二進制流附加在后面,并在文件開始位置4B對齊。

ROM創建過程

注意:

創建ROM的shell腳本可以在最后一章的鏈接里獲得。
按照官方推薦的Flash布局,ROM建議燒錄在Flash的0 x 0010 0000位置

8.讀取ROM文件內容

我們需要根據文件名來讀取文件,并不是直接讀取文件,因此先要在ROM的FAT區里查找對應文件名的存在位置、MIME、大小和存放區域,再去讀取文件內容,當讀到文件尾的時候不在讀取。官方的spi_flash_read接口只能讀取指定位置的指定長度的數據,這對我們讀區文件很不方便。

代碼8-1:文件系統實現

// 所謂的文件句柄 保存已經打開文件的信息 struct SOTROM_filePointer {uint32 location;uint32 offset;uint32 fileSize;bool fileExsit;char *mime; }; typedef struct SOTROM_filePointer SOTROM_file; define SOT_ROM_ORG 0x00100000; define SOT_ROM_FAT_SIZE 0x00010000; //讀區文件FAT,匹配每一條文件條目是否于請求的文件名一致,一致則讀取信息并返回,否則返回空文件句柄。 SOTROM_file *SOTROM_fopen(char* fileName) {SOTROM_file *openedFile;openedFile = malloc(70);openedFile->location = 0;openedFile->offset = 0;openedFile->fileSize = 0;openedFile->fileExsit = false;// 查找FAT區域char *pointerFilename = (char *)zalloc(64);uint32 currentFATPointer = SOT_ROM_ORG;uint32 maxFATPointer = SOT_ROM_ORG + SOT_ROM_FAT_SIZE;SpiFlashOpResult res;while(currentFATPointer < maxFATPointer) {// 獲得文件名res = spi_flash_read(currentFATPointer, (uint32* )pointerFilename, 64);if(res == SPI_FLASH_RESULT_OK) {if(strlen(pointerFilename) > 0) {if(strcmp(fileName, pointerFilename) == 0) {char *pointerFilename = (char *)zalloc(56);uint32 fileSize;uint32 location;res |= spi_flash_read(currentFATPointer + 64, (uint32* )pointerFilename, 56);res |= spi_flash_read(currentFATPointer + 120, (uint32* )&fileSize, 4);res |= spi_flash_read(currentFATPointer + 124, (uint32* )&location, 4);if(res == SPI_FLASH_RESULT_OK) {openedFile->fileExsit = true;openedFile->mime = pointerFilename;openedFile->fileSize = fileSize;openedFile->location = location;openedFile->location += maxFATPointer;break;}}currentFATPointer += 128;} else {break;}} else {break;}}// 有助于調試的調試信息// printf("file found: %d\n", openedFile->fileExsit);// printf("file mime: %s\n", openedFile->mime);// printf("file length: %d\n", openedFile->fileSize);// printf("file location: %d\n", openedFile->location);// printf("file offset: %d\n", openedFile->offset);return openedFile; } // 從 SOTROM_fopen 打開的文件里 獲取在offset指針處讀取 datalength 長度的數據并輸出到 data 里,并設置 offset 到下一字節位置。若文件長度小于 offset + datalength 只讀區到文件末尾 bool SOTROM_fread(SOTROM_file *file, uint32 *data, int32 datalength) {// 檢查文件是否存在if(!file->fileExsit) {return false;}int32 fileLength = file->fileSize;int32 currentOffset = file->offset;int32 startReadLocation = file->location + currentOffset;// 若指針已經到達文件結尾不讀數據if(currentOffset >= fileLength) {return false;}// 若超過文件結尾則只讀取到文件結尾if(currentOffset + datalength > fileLength) {datalength = fileLength - currentOffset;}SpiFlashOpResult res;res = spi_flash_read(startReadLocation, data, datalength);if(res == SPI_FLASH_RESULT_OK) {file->offset = currentOffset + datalength;char *tmpDataPtr = (char *)data;tmpDataPtr[datalength] = 0;return true;} else {return false;} }

9.處理動態請求

動態請求的URI一般指向的不是一個真實存在的路徑,因此需要區分動態請求和靜態請求。本例會把URI由 /cgi/ 開頭的請求視為動態請求。并且講動態請求傳入一個Router,有Router把請求轉發給每個執行動態的請求的文件或函數,我們稱之為Controller。

router的工作過程

代碼9-1:router實現的代碼

void SOTCGI_PROG(char *para, int32 sock) // CGI入口文件,傳socket連接ID和URL即可 void SOTCGI_handler(char * cgiURI, int32 sock) {char *response = (char *)zalloc(64);SOTCGI_route("/cgi/demo0/", cgiURI, sock, SOTCGI_PROG);SOTCGI_route("/cgi/demo1/", cgiURI, sock, SOTCGI_PROG); } // CGI Router設置, 根據指定地址 route 綁定指定控制器 callback。 void SOTCGI_route(char *route, char *cgiURI, int32 sock, void (* callback)()) {if(strncmp(route, cgiURI, strlen(route)) == 0) {char *para = substr(cgiURI, strlen(route), strlen(cgiURI));(* callback)(para, sock);free(para);} }

代碼9-2:controller實現的代碼模版

void SOTCGI_PROG(char *para, int32 sock) {printf("GET CGI input: %s\n", para); }

10.GPIO的控制

由于GPIO與普通IO不一樣,因此在使用前必須設置GPIO的功能,SDK為每個GPIO都設定了五種功能,使用前需要使用 PIN_FUNC_SELECT 宏函數進行設置,具體每個GPIO口的功能,在最后一節給出的鏈接里會有很大幫助。本例只使用了GPIO最基本的邏輯輸出的功能。具體GPOI功能設置可以參照SDK的API參考文檔。

代碼10-1:邏輯輸出的實現

PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12);//將 PERIPHS_IO_MUX_MTDI_U 接口綁定為 FUNC_GPIO12 輸出功能 gpio_output_set(BIT12, 0, BIT12, 0); // GPIO12 輸出高電平 gpio_output_set(0, BIT12, BIT12, 0); // GPIO12 輸出低電平

11.任務控制

由于使用了SDK內集成了FreeROTS操作系統,因此我們可以把整個Server啟動等待鏈接和處理請求的過程分配成任務,這樣在server運行過程中,模塊的程序流不會被阻塞。關于FreeROTS的任務管理方面,在最后一節給出的鏈接里會有很大幫助。本例使用了創建任務 xTaskCreate,掛起任務 vTaskDelay和銷毀任務 vTaskDelete 這三個任務API。

系統啟動時先檢查網絡連接,當網絡連接建立好后創建初始化WebServer的任務,當初始化完成后初始化任務會被刪除并創建WebServer的主任務,當有請求進來時,主任務會創建worker任務去處理請求,當處理任務完成后,worker任務會自行刪除。

任務控制

12.實現webserver

結合任務控制和其他的功能我們不難規劃出一個webserver,具體項目代碼在最后一章里有下載鏈接。

13.驅動5V繼電器

由于GPIO輸出電平為3.3V,不足以驅動5V的繼電器模塊,因此需要使用5V的邏輯門電路輔助驅動,本例使用的是CD4001 四或非門電路。

14.制作靜態頁面

現在我們已經有了一個可以控制繼電器的Webserver ,再有一個前端也面就完美了。將制作好的靜態頁面寫入ROM后燒錄在Flash的0 x 0010 0000 位置上。完美收工。關于前端實現不在本文討論范疇,前端代碼隨項目代碼在最后一章的連接里一起給出。

15.接入調試

連接好線路,接通電源,進行最終調試。

最終調試

我的Webserver 工作正常,你的呢?

16.相關資源及項目代碼

關于交叉編譯器:
https://github.com/esp8266/es...
https://github.com/jcmvbkbc/c...
http://bbs.espressif.com/view...

關于燒寫工具:
https://github.com/esp8266/es...
http://bbs.espressif.com/view...

關于SDK:
https://github.com/espressif/...
https://github.com/pfalcon/es...

關于ESP8266的技術支持文檔:
http://espressif.com/en/suppo...

關于硬件的連接和燒錄
http://espressif.com/sites/de...

關于GPIO的功能的描述
http://espressif.com/sites/de...

關于FreeROTS的使用
http://www.freertos.org/FreeR...

本示例源代碼
https://github.com/cubicwork/...

SOTServer + SOTROM github項目( 代碼整理好以后會開放源代碼 )
https://github.com/cubicwork/...


作者:CarneyWu

本文來自【蒲公英技術征文】,詳情鏈接:https://jinshuju.net/f/dGmewL
本活動用戶內容均采用?署名-非商業性使用-相同方式共享 3.0 中國大陸 進行許可

總結

以上是生活随笔為你收集整理的【蒲公英技术征文】如何在 ESP-12F/ESP8266 上实现 webserver的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日韩欧美亚洲天堂 | 国产精品视频你懂的 | 国产伦精品一区二区三区视频免费 | 色婷婷国产精品综合在线观看 | 日本二三区| 中日韩精品视频 | 日韩综合网站 | 国产精品视频一区在线观看 | 日本少妇xxxx | 中文字幕高潮 | 亚洲精品乱码久久久久久蜜桃欧美 | 一级片aaaaa 国产又黄又粗又猛又爽 | 美女国产一区 | 四虎影 | 日本久色 | 欧美18—19性高清hd4k | 在线国产一区 | www.av.cn | av专区在线 | 中文字幕在线观看 | 中文字幕第15页 | 日本老妇性生活 | 中文字幕日韩专区 | 亚洲精品77777| 大吊一区二区三区 | 姝姝窝人体www聚色窝 | 色老头一区二区三区在线观看 | 精品一区二区三区无码按摩 | 日日爱夜夜操 | 亚洲第8页| 美女张开腿让男人桶爽 | 国产区一区二区三 | 绿帽单男| 国产女人爽到高潮a毛片 | 天天视频入口 | 五月天视频 | 亚洲综合社区 | 国产精品国产精品国产专区蜜臀ah | 奇米av在线 | 又污又黄的视频 | 日本吃奶摸下激烈网站动漫 | 亚洲第三区| 成人性做爰aaa片免费看不忠 | 永久免费视频网站直接看 | 国产一区精品久久 | 大学生高潮无套内谢视频 | 东北高大丰满bbbbzbbb | 三八激情网 | 成人毛片视频网站 | 99久 | 一级片一区二区三区 | 中文字字幕在线中文乱码电影 | 九九免费精品视频 | 日韩一区2区 | 日韩av一区在线观看 | 永久av | 曰本黄色大片 | 一区二区中文字幕 | 在线视频欧美亚洲 | 国产精品久久久久久久久久久久久久久久久久 | 国产91成人 | 久久先锋 | 精品在线一区二区 | 亚洲高清视频一区 | 牛牛在线 | 日本三级影院 | 中文字幕在线播放一区二区 | 男女考妣视频 | 欧美日韩成人 | 久久久国产打桩机 | 黄色小视频免费在线观看 | 欧美日韩一区在线观看 | 免费a网站| 欧美s码亚洲码精品m码 | 午夜影院毛片 | 久久99久久99精品蜜柚传媒 | 国内免费精品视频 | 久久精品牌麻豆国产大山 | 搡老熟女国产 | 97视频在线免费观看 | 德国艳星videos极品hd | 黄色理伦 | 日韩欧美一| 中文幕无线码中文字蜜桃 | 亚洲精品日韩精品 | 国产尤物视频在线观看 | 国产成人一区二区在线 | 国产成人精品免费网站 | 欧美午夜在线 | 亚洲国产日韩av | 一级看片免费视频 | 成人小视频在线看 | 哺乳期给上司喂奶hd | 亚洲精品国产精品乱码不66 | 欧美一线高本道 | 久久aaaa片一区二区 | 亚洲精品一区二区三 | 视频国产一区 | 狠狠操精品|