STM32與物聯網02-網路數據收發

来源:https://www.cnblogs.com/frozencandles/archive/2022/07/15/16480363.html
-Advertisement-
Play Games

基礎知識 進程 內核的功能和作用:文件系統管理、網路管理、進程管理、記憶體管理等,屬於linux最基礎的功能 進程:process,正在運行中的程式的一個副本。允許有多個進程同時執行。 #操作系統負責分配cpu運行進程的順序和時間 #副本:把磁碟上的指定文件載入到記憶體進行運行 運行多次就會有多個副本 ...


上一節中,介紹了 ESP8266 的使用方法。不過上一節中都是通過串口調試工具手動發送信息的方式來操作 ESP8266 ,這肯定不能用於實際開發。因此,本節介紹如何編寫合適的程式來和 ESP8266 交互,從而收發並解析網路數據。

TCP伺服器

在 TCP 伺服器下,可以使用移動設備主動連接 ESP8266 提供的 WiFi 。如果編寫正確的程式,那麼可以使用移動設備控制 ESP8266 。

建立TCP伺服器

從上一節的介紹可以瞭解到,程式和 ESP8266 的交互主要是通過發送 AT 指令完成的,因此程式中首要的任務就是編寫合適的程式向 ESP8266 發送指令。

不過在發送指令後,可能還需要判斷指令是否被成功接收。一般來說,ESP8266 執行失敗時可能返回各種信息,但在成功執行指令後都會返回 OK 。發送指令可以通過以下函數完成:

uint8_t ESP8266_SendCmd(char* cmd, uint8_t timeout) {
    ESP8266_Buffer.Length = 0;
    memset(ESP8266_Buffer.Body, 0, USART_RX_BUF_SIZE);
    USART_printf(USART3, "%s\r\n", cmd);
    while (timeout--) {
        delay_ms(100);
        if (strstr(ESP8266_Buffer.Body, "OK"))
            return 0;
    }
    return 1;
}

由於不同指令處理的時間也不一致,因此在程式中引入了一個倒計時器,在倒計時結束前不斷檢查接收到的信息中是否包含 "OK" ,如果是則結束當前倒計時,這樣可以確保在指令執行完後就可以立即退出延時,提高程式執行效率。

程式中與 ESP8266 交互基本是都採用這種方式。例如,在程式下載後若希望使 ESP8266 也重啟,則可以通過拉低 RST 引腳複位 ESP8266 ,複位後會接到 "ready" 信息,則可以編寫以下函數:

uint8_t ESP8266_Reset(uint16_t timeout) {
    ESP8266_RST(RESET);
    delay_ms(500);
    ESP8266_RST(SET);
    while (timeout--) {
        delay_ms(100);
        if (strstr(ESP8266_Buffer.Body, "ready"))
            return 0;
    }
    return 1;
}

如果某條指令有其餘回覆的情況,只需要參照以上略做修改即可。

有了以上函數後,就可以編寫代碼,逐條發送指令了。這裡將 ESP8266 設置為 AP 模式,使其變成一個 WiFi 熱點,使電腦可以直接連接上 ESP8266 並收發信息,因此首先需要發送 AT+CWMODE=2 指令:

if (ESP8266_Reset(50))
    return 1;
if (ESP8266_SendCmd("AT+CWMODE=2", 50))
    return 2;

然後可以使用 AT+CWSAP="<ssid>","<password>",<chl>,<enc> 設置 WiFi 參數,一般來說通道號和加密類型都設置為 4 即可:

char cmd[64];
sprintf(cmd, "AT+CWSAP=\"%s\",\"%s\",%d,%d\r\n", SSID, PASSWORD, 4, WPA_WPA2_PSK);
if (ESP8266_SendCmd(cmd, 50))
    return 3;

可以將這些參數設置為巨集定義以方便修改。接下來的許多設置都和以上代碼類似,可以以此為模板替換為其它命令,因此不再展示代碼,僅介紹主要命令。

如果要設置固定的區域網 IP ,可以通過 AT+CIPAP="<ip>" 完成。

接下來可以通過指令 AT+CIPMODE=<mode> 設置 ESP8266 的傳輸模式。該命令可以設置 ESP8266 的兩種傳輸模式:

  1. 普通傳輸模式(Normal Transmission Mode),該模式下,用戶可以通過 AT 指令發送 TCP 數據,同時 ESP8266 也會將接收到的數據以 +IPD 等指令的形式返回
  2. 透傳接收模式(Passthrough Receiving Mode):該模式下,ESP8266 無法發送 TCP 數據,同時 ESP8266 會將接收到的數據以原始的形式返回給 STM32

透傳接收模式一般用於開啟透傳模式。關於透傳模式會在後續介紹。

ESP8266 支持多路連接,即一個 TCP 埠可以建立多個連接。通過 AT+CIPMUX=1 可以啟用多連接,每個連接到埠上的客戶端通過 <id> 標識,連接的數量最後為 5 個,因此 <id> 的取值範圍為 0~4 。

多連接必須在所有連接都斷開且伺服器也關閉時才可以設置,並且只有普通傳輸模式下才能設置為多連接。

接下來,可以通過 AT+CIPSERVER=1,8266 開啟一個位於埠 8266 上的 TCP 伺服器。根據以上步驟,TCP 伺服器便建立完成,可以準備接收客戶端發來的數據了。

數據獲取與解析

在建立了 TCP 伺服器後,ESP8266 便會等待客戶端的連接。

TCP 客戶端在接到客戶端的數據時,會以 +IPD,<id>,<len>:<data> 的指令形式轉交數據給 STM32 。由於以上開啟了多路連接,因此接收的數據中多了一個欄位 <id>

因此判斷是否有數據收到也很簡單,只需要判斷接收緩衝區內是否有子串 "+IPD" 即可:

bool ESP8266_HasData(void) {
    return strstr(ESP8266_Buffer.Body,"+IPD") 
        && strstr(ESP8266_Buffer.Body,":");
}

以上同時查找子串 ":" 確保數據有效性。根據以上格式,拆解該字元串並截取有效數據如下:

int8_t ESP8266_MuxGetData(char* data, uint16_t* len) {
    uint8_t id;
    char* data_ptr = strstr(ESP8266_Buffer.Body, "+IPD");
    if (sscanf(data_ptr,"+IPD,%d,%d", &id, len) == 2) {
        memcpy(data, strstr(data_ptr, ":") + 1, *len);
        data[*len] = '\0';
        ESP8266_Buffer.Length = 0;
        memset(ESP8266_Buffer.Body, 0, USART_RX_BUF_SIZE);
        return id;
    }
    return -1;
}

以上函數略顯複雜。之所以要這麼複雜,主要有以下兩個方面的原因:scanf() 類函數使用字元串轉換說明時,它在讀入數據時如果遇到一個空格或回車符,會丟棄後面的所有數據,這顯然不能用於截取用戶數據。

另外上一節說過工程中接收串口傳來的不定長數據的方式是使用串口空閑中斷,然而空閑中斷接收的一包數據並不都是符合期望的一包數據:在接收到 TCP 連接時,ESP8266 會發送 <id>,CONNECT 表示連接已建立,如果此時建立的連接接收到任何數據,ESP8266 也會立即轉發該數據。因此如果連接建立後馬上收到數據,那麼兩次發送的數據時間相隔過短,可能會沒有引起空閑中斷而被 STM32 認為是同一包數據。在連接取消時,也有同樣的問題。

不過發送數據的函數可能更加複雜:

uint8_t ESP8266_MuxSendData(uint8_t* data, uint16_t length, uint8_t id, uint8_t timeout) {
    ESP8266_ClearBuffer();
    USART_printf(USART3, "AT+CIPSEND=%d,%d\r\n", id, length);
    while (timeout--) {
        delay_ms(10);
        if (strstr(ESP8266_Buffer.Body, ">"))
            break;
    }
    if (timeout > 0) {
        ESP8266_ClearBuffer();
        USART_SendBytes(USART3, data, length);
        while (timeout--) {
            delay_ms(10);
            if (strstr(ESP8266_Buffer.Body, "SEND OK")) {
                ESP8266_ClearBuffer();
                return 0;
            }
            if (strstr(ESP8266_Buffer.Body, "link is not valid")) {
                ESP8266_ClearBuffer();
                return 2;
            }
        }
        return 3;
    }
    else
        return 1;
}

上一節介紹了發送數據主要使用 AT+CIPSEND 指令完成(多連接下需要一個額外的欄位指示發送給的 <id> ),如果可以發送 ESP8266 會返回 "> " 作提示。如果發送成功,ESP8266 會返回 "SEND OK" ,通過返回提示就可以知道發送狀態。

有了以上函數以後,就可以著手編寫主程式了。主程式的處理邏輯非常簡單,在建立 TCP 伺服器後,便不斷判斷是否有數據到達,如果有那麼便讀取數據並回覆信息:

char ipd_data[512];
int8_t ipd_id;
uint16_t ipd_len;
while (ESP8266_CreateTcpServer())
    delay_ms(200);
while (1) {
    if (ESP8266_HasData()) {
        ipd_id = ESP8266_MuxGetData(ipd_data, &ipd_len);
        ESP8266_MuxSendData("Acknowledge", 12, ipd_id, 30);
    }
    delay_ms(500);
}

可以將得到的數據顯示在串口中。在電腦客戶端,編寫如下套接字程式:

import socket, time
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('192.168.10.1', 8266))
client.send(time.ctime().encode())
message = client.recv(1024)
print(message.decode())
client.close()

將電腦連接到 ESP8266 創建的 WiFi 上並運行該套接字程式,即可觀察到實驗現象。如果為程式編寫合適的用戶界面併在 STM32 上進一步解析數據,那麼便可以實現手機端控制 STM32 了。

TCP客戶端與HTTP請求

TCP 客戶端的建立與 TCP 伺服器類似,這裡先使 ESP8266 連接到路由器,藉助路由器訪問公網上的伺服器。

前幾步操作與 TCP 客戶端類似:在複位 ESP8266 後,首先通過指令 AT+CWMODE=1 設置 Sta 模式,然後通過 AT+CWJAP="<ssid>","<password>" 連接到路由器中。由於客戶端無需多個連接,可以使用 AT+CIPMUX=0 關閉多連接。

本次採用透傳模式(Passthrough Mode)來收發數據。透傳模式是一種特殊的收發數據模式,在透傳模式下,用戶不能發送 AT 指令,發送的任何數據都會作為原始的數據發送到傳輸對端;從傳輸對端收到的數據也會不經由任何 +IPD 封裝而原封不動地返回給 STM32 。

使用 AT+CIPMODE=1 可以設置傳輸模式為透傳模式。通過 AT+CIPSTART 連接上伺服器後,直接執行 AT+CIPSEND ,待 ESP8266 返回 "> " 後就進入了透傳模式。透傳模式下,每包數據以 20ms 間隔區分,每包最大 2048 位元組,發送和接收數據都不需要封裝成指令,方便處理。

正常退出透傳模式的唯一方式就是單獨發送一包發送指令 +++

根據以上原理,可以使用 STM32 發送相應指令,連接到伺服器後進入透傳模式,並準備發送相應的數據。其代碼和上文服務端類似,例如:

if (ESP8266_Reset(50))
    while (1);
if (ESP8266_SendCmd("AT+CWMODE=1", 20))
    while (1);
if (ESP8266_SendCmd("AT+CWJAP=\"TP_LINK\",\"abc123456\"", 100))
    while (1);
// ... and so on

當然,考慮到一些指令執行成功時不總是返回 OK ,並且為了使程式邏輯更清晰,可以將一些指令封裝成函數。例如,以下函數根據地址(可以是 IP 地址或功能變數名稱,DNS 解析將自動完成)和埠號,連接到特定的 TCP 伺服器中併進入透傳模式:

uint8_t ESP8266_ConnectServer(char* address, uint16_t port, uint8_t timeout) {
    ESP8266_ClearBuffer();
    USART_printf(USART3, "AT+CIPSTART=\"TCP\",\"%s\",%d\r\n", address, port);
    while (timeout--) {
        delay_ms(100);
        if (strstr(ESP8266_Buffer.Body, "CONNECT")) {
            ESP8266_ClearBuffer();
            USART_printf(USART3, "AT+CIPSEND\r\n");
            while (timeout--) {
                delay_ms(100);
                if (strstr(ESP8266_Buffer.Body, "\r\nOK\r\n\r\n>"))
                    return 0;
            }
            return 4;
        }
        if (strstr(ESP8266_Buffer.Body, "CLOSED"))
            return 1;
        if (strstr(ESP8266_Buffer.Body, "ALREADY CONNECTED"))
            return 2;
    }
    return 3;                   //超時錯誤,返回3
}

可以仿照該函數將其它指令封裝成具有抽象功能的對應函數。

在本示例中,在使用 TCP 連接到遠程伺服器的 80 埠的基礎上,手動構造合適的 HTTP 請求併發送:

while(1) {
    USART_SendString(USART3, "GET /api/temperature?time=now HTTP/1.1\r\n"
                            "Connection: keep-alive\r\n"
                            "Host: 192.168.1.105:80\r\n\r\n");
    delay_s(5);
    printf("%s", ESP8266_Buffer.Body);
}

這裡 5 秒鐘便查詢一次數據。如果間隔過長,連接可能斷開,那麼可以先主動斷開連接,等需要查詢時再發起 TCP 連接。

通過 HTTP 伺服器提供的合適介面,ESP8266 便可以從互聯網中獲取到非常廣泛的數據。在測試用的伺服器中,該介面返回一個 json 響應並被轉發到 STM32 中,串口調試工具中顯示的原始數據如下:

HTTP/1.1 200 OK
Date: Wed, 13 Jul 2022 10:57:48 GMT
Server: WSGIServer/0.2 CPython/3.9.1
Content-Type: application/json
X-Frame-Options: DENY
Content-Length: 93
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin

{"temperature": {"high": 37.6, "low": 28.1, "now": 36.7}, "humidity": "50%", "wind": "11mph"}

通過解析請求頭 Content-Length 就可以獲取數據的長度,然後查找子串 "{" 的位置便可以提取出介面返回的 json 數據,並可以使用 cJSON 等第三方庫解析其中的數據。互聯網中存在許多類似的介面,只需要構造合適的請求頭,便可以抓取很多有用的信息,不過這需要有一定的抓包或爬蟲的基礎。

通過路由器可以接入互聯網,在 TCP 服務的基礎上,構造出合適的 HTTP 等應用層協議的封裝,便可以採集互聯網中的各種數據,或者向伺服器報告自身感測器的數據,由此真正實現物聯網的基礎。

例如,可以向 HTTP 伺服器提供的介面發送 POST 請求,將感測器數據作為參數發送給伺服器,伺服器解析 POST 請求並更新資料庫,然後便可以顯示在前端上,這樣便可以在任何地點查看 STM32 的狀態了。不過由於其實現涉及到的知識點過於廣泛,無論是環境的配置還是程式的編寫都不是一篇文章能完整介紹的,這裡便不再涉及。

一個比較有趣的實現是利用 SMTP 發送電子郵件,可以閱讀這篇文章瞭解 SMTP 應用層協議的原理與基本報文格式,文章中附帶了 Python 套接字程式實現,它和 AT 指令的思路具有一定相似性,移植到 STM32 的主要難點是使用 base64 編碼完成身份驗證,有興趣的讀者可以嘗試自行實現。

參考資料/延伸閱讀

https://docs.espressif.com/projects/esp-at/en/release-v2.2.0.0_esp8266/AT_Command_Set/TCP-IP_AT_Commands.html

TCP/IP 相關 AT 指令集的官方文檔。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在某個時間點執行一次任務 at工具 作用:用於執行一次性任務,需要指定執行的時間。 at工具來源於at軟體包。 依賴與atd服務,需要啟動才能實現at任務。#通過這個守護進程見監控at的相關內容 #選項: -l:查看當前定義好的計劃任務 -d num:根據作業編號來刪除計劃任務 -c num: 查看 ...
  • 一、 Linux用戶和組及其許可權管理 請根據以下項目要求,寫出操作過程和命令併進行相應的驗證測試操作。 項目要求: 某軟體開發公司即將開始在Linux系統上進行項目的開發。要實現的環境是:公司有軟體開發,網路和技術支持3個部門,對應建立3個用戶組為project,technology,market。 ...
  • 覓長生 for Mac是一款以傳統道家文化與修真為題材的修仙角色扮演游戲,玩家在覓長生mac版游戲中可以體驗從零開始,一步步積攢修為突破境界的逆天修仙之旅。覓長生mac版玩法非常簡單,戰鬥目前採用了卡牌對戰方式,讓玩家能夠輕鬆駕馭游戲! 詳情:覓長生 for Mac(修仙角色扮演游戲) 修仙小說是我 ...
  • FontLab VI Mac版是Mac平臺上的一款字體編輯器應用。FontLab VI Mac版是專為字體鑄造廠、專業設計師、印刷和平面設計工作室設計的軟體。 詳情:FontLab 8 for mac(字體編輯器應用) 功能介紹 1、下一代繪圖 創造Bézier曲線不再是“精通或神秘”。 2、刷子和 ...
  • AIrBuddy Mac版是Mac平臺上的一款可以幫助用戶更好的體驗和使用AIrPods及Beats無線耳機的應用。只需點擊一下,即可連接並將Mac的音頻播放到AIrPods。它還可以確保Mac的音頻輸入不會切換到AIrPods,這樣您就可以獲得最佳質量。 詳情:AirBuddy for Mac(A ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 一、paramiko模塊簡介 paramiko是一個用於做遠程式控制制的模塊,使用該模塊可以對遠程伺服器進行命令或文件操作,基於linux中的ssh服務 。paramiko是第三方模塊,需要我們單獨安裝。通過paramiko模塊,我們可以利用pyth ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 前言 出現這種bug Unit mysql.service could not be found 因為bug被我修複好了,在這裡引用一下網友的bug截圖 1. 原理 之所以使用命令行service mysqld status出現Unit mysq ...
  • SecureFX for Mac是一款跨平臺文件傳輸客戶端軟體,有著易用的、類似於資源管理器的用戶界面。securefx mac可以更加有效的實現文件的安全傳輸,您可以使用其新的拖放功能直接將文件拖至Windows Explorer和其他程式中,用戶也可以充分利用SecureFX for mac的自 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...