程序代碼篇---數據包解析

數據包解析是不同設備(如電腦、ESP32 等嵌入式設備)之間通信的核心環節。簡單說,就是把收到的 "一串數據" 翻譯成雙方都能理解的 "具體信息"(比如溫度、濕度、命令等)。下面介紹幾種常見的數據包格式,以及 Python 和 ESP32(基于 Arduino 框架)的解析代碼,盡量用通俗的語言解釋。

一、文本格式(最容易理解)

文本格式的數據包由字符串組成,人類可以直接看懂,適合簡單場景。常見的有CSV 格式JSON 格式

1. CSV 格式(逗號分隔值)

特點:用逗號(或其他符號)分隔不同字段,結構簡單,類似表格。
適用場景:傳感器批量數據(如溫度、濕度、時間)、簡單配置信息。
示例數據包"25.6,60.2,202310011200"(溫度 = 25.6℃,濕度 = 60.2%,時間 = 2023-10-01 12:00)

Python 解析代碼
用內置的split函數分割字符串即可。

# 假設收到的CSV數據包
recv_data = "25.6,60.2,202310011200"# 解析步驟
fields = recv_data.split(',')  # 用逗號分割
if len(fields) == 3:  # 檢查字段數量是否正確temperature = float(fields[0])humidity = float(fields[1])time = fields[2]print(f"溫度:{temperature}℃,濕度:{humidity}%,時間:{time}")
else:print("數據格式錯誤")

ESP32 解析代碼
ESP32 通過串口 / 網絡收到字符串后,用strtok函數分割(類似 Python 的 split)。

#include <Arduino.h>void setup() {Serial.begin(115200);  // 初始化串口
}void loop() {if (Serial.available()) {  // 檢查是否有數據String recv_str = Serial.readStringUntil('\n');  // 讀取一行數據(假設以換行結束)recv_str.trim();  // 去掉首尾空格和換行// 用逗號分割字符串char* data_ptr = strtok((char*)recv_str.c_str(), ",");  // 第一個字段float temperature, humidity;String time_str;int count = 0;while (data_ptr != NULL) {if (count == 0) {temperature = atof(data_ptr);  // 轉成浮點數(溫度)} else if (count == 1) {humidity = atof(data_ptr);  // 轉成浮點數(濕度)} else if (count == 2) {time_str = String(data_ptr);  // 時間字符串}data_ptr = strtok(NULL, ",");  // 下一個字段count++;}if (count == 3) {  // 確認解析到3個字段Serial.printf("溫度:%.1f℃,濕度:%.1f%,時間:%s\n", temperature, humidity, time_str.c_str());} else {Serial.println("CSV格式錯誤");}}
}
2. JSON 格式(鍵值對結構)

特點:用{鍵:值}的結構表示數據,支持嵌套(比如數組、對象),適合復雜數據。
適用場景:API 接口、帶層級的配置信息(如 "設備信息 + 傳感器數據")。
示例數據包{"device_id":"esp32_01","data":{"temp":25.6,"hum":60.2},"time":"202310011200"}

Python 解析代碼
用內置的json模塊直接轉成字典,方便取值。

import json# 假設收到的JSON數據包
recv_data = '{"device_id":"esp32_01","data":{"temp":25.6,"hum":60.2},"time":"202310011200"}'try:# 解析JSON字符串為字典data_dict = json.loads(recv_data)# 提取數據device_id = data_dict["device_id"]temp = data_dict["data"]["temp"]hum = data_dict["data"]["hum"]time = data_dict["time"]print(f"設備:{device_id},溫度:{temp}℃,濕度:{hum}%,時間:{time}")
except json.JSONDecodeError:print("JSON格式錯誤")
except KeyError as e:print(f"缺少字段:{e}")

ESP32 解析代碼
需要用ArduinoJson庫(需在 Arduino 庫管理器中安裝),處理 JSON 結構。

#include <Arduino.h>
#include <ArduinoJson.h>  // 引入JSON庫void setup() {Serial.begin(115200);
}void loop() {if (Serial.available()) {String recv_str = Serial.readStringUntil('\n');recv_str.trim();// 分配JSON緩沖區(根據數據大小調整,這里用1024字節)StaticJsonDocument<1024> doc;// 解析JSONDeserializationError error = deserializeJson(doc, recv_str);if (error) {  // 解析失敗Serial.printf("JSON解析錯誤:%s\n", error.c_str());return;}// 提取數據(注意判斷字段是否存在)if (doc.containsKey("device_id") && doc.containsKey("data") && doc.containsKey("time")) {const char* device_id = doc["device_id"];float temp = doc["data"]["temp"];float hum = doc["data"]["hum"];const char* time_str = doc["time"];Serial.printf("設備:%s,溫度:%.1f℃,濕度:%.1f%,時間:%s\n", device_id, temp, hum, time_str);} else {Serial.println("JSON缺少必要字段");}}
}

二、二進制格式(更高效)

文本格式雖然易讀,但占空間大(比如數字 "25.6" 要占 4 個字符),傳輸效率低。二進制格式直接用字節存儲數據(比如 25.6℃可以用 2 個字節存儲),更適合嵌入式設備(如 ESP32)之間的高速通信。常見的有固定長度格式TLV 格式

1. 固定長度格式

特點:每個字段的長度固定(比如溫度占 2 字節,濕度占 2 字節),解析時按固定位置取數據,速度快。
適用場景:實時性要求高的場景(如傳感器每秒傳輸一次數據)。
示例數據包結構(共 5 字節):

字段長度(字節)說明
溫度2用 int16_t 存儲(實際值 = 存儲值 / 10,比如 0x00FA=250 → 25.0℃)
濕度2同上(0x0258=600 → 60.0%)
狀態10 = 正常,1 = 異常

對應的二進制數據:0x00 0xFA 0x02 0x58 0x00(即溫度 25.0℃,濕度 60.0%,狀態正常)

Python 解析代碼
struct模塊解析二進制數據(需要知道每個字段的類型和順序)。

import struct# 假設收到的二進制數據(5字節)
recv_bytes = b'\x00\xFA\x02\x58\x00'if len(recv_bytes) == 5:  # 檢查長度是否正確# 解析:2字節溫度(int16)、2字節濕度(int16)、1字節狀態(uint8)temp_raw, hum_raw, status = struct.unpack('>hhu', recv_bytes)# 轉換為實際值(除以10)temperature = temp_raw / 10.0humidity = hum_raw / 10.0print(f"溫度:{temperature}℃,濕度:{humidity}%,狀態:{'正常' if status == 0 else '異常'}")
else:print("二進制數據長度錯誤")

>hhu表示:>大端模式,hint16,hint16,uuint8)

ESP32 解析代碼
直接操作字節數組,按固定位置取數據。

#include <Arduino.h>// 定義數據包結構(5字節)
struct DataPacket {int16_t temp_raw;  // 溫度原始值(2字節)int16_t hum_raw;   // 濕度原始值(2字節)uint8_t status;    // 狀態(1字節)
};void setup() {Serial.begin(115200);
}void loop() {if (Serial.available() >= 5) {  // 至少收到5字節uint8_t recv_buf[5];Serial.readBytes(recv_buf, 5);  // 讀取5字節到緩沖區// 解析數據(大端模式:高位字節在前)DataPacket data;data.temp_raw = (recv_buf[0] << 8) | recv_buf[1];  // 第0-1字節 → 溫度data.hum_raw = (recv_buf[2] << 8) | recv_buf[3];   // 第2-3字節 → 濕度data.status = recv_buf[4];                         // 第4字節 → 狀態// 轉換為實際值float temperature = data.temp_raw / 10.0;float humidity = data.hum_raw / 10.0;Serial.printf("溫度:%.1f℃,濕度:%.1f%,狀態:%s\n",temperature, humidity, (data.status == 0) ? "正常" : "異常");}
}
2. TLV 格式(Type-Length-Value)

特點:每個字段由 "類型(Type)+ 長度(Length)+ 值(Value)" 組成,字段數量和長度可以不固定,靈活性高。
適用場景:需要擴展的協議(比如有時傳溫度,有時傳溫度 + 濕度 + 電量)。
示例數據包

類型(1 字節)長度(1 字節)值(n 字節)說明
0x010x020x00 0xFA溫度(25.0℃)
0x020x020x02 0x58濕度(60.0%)

對應的二進制數據:0x01 0x02 0x00 0xFA 0x02 0x02 0x02 0x58

Python 解析代碼
循環解析每個 TLV 單元(先讀類型,再讀長度,再讀對應的值)。

def parse_tlv(data):parsed = {}index = 0while index < len(data):if index + 2 > len(data):  # 至少需要類型(1)+長度(1)breaktype_ = data[index]        # 類型(1字節)length = data[index + 1]   # 長度(1字節)index += 2if index + length > len(data):  # 檢查值是否完整breakvalue = data[index:index + length]  # 值(length字節)index += length# 解析具體值(根據類型轉換)if type_ == 0x01:  # 溫度temp_raw = (value[0] << 8) | value[1]parsed["temperature"] = temp_raw / 10.0elif type_ == 0x02:  # 濕度hum_raw = (value[0] << 8) | value[1]parsed["humidity"] = hum_raw / 10.0return parsed# 假設收到的TLV二進制數據
recv_bytes = bytes([0x01, 0x02, 0x00, 0xFA, 0x02, 0x02, 0x02, 0x58])
result = parse_tlv(recv_bytes)
print(f"解析結果:{result}")  # 輸出:{'temperature': 25.0, 'humidity': 60.0}

ESP32 解析代碼
同樣循環讀取每個 TLV 單元,按類型處理值。

#include <Arduino.h>void parseTLV(uint8_t* data, int len) {int index = 0;float temp = -1, hum = -1;while (index < len) {if (index + 2 > len) break;  // 不夠類型+長度的字節uint8_t type = data[index];uint8_t length = data[index + 1];index += 2;if (index + length > len) break;  // 不夠值的字節// 解析值(根據類型)if (type == 0x01 && length == 2) {  // 溫度(2字節)int16_t raw = (data[index] << 8) | data[index + 1];temp = raw / 10.0;} else if (type == 0x02 && length == 2) {  // 濕度(2字節)int16_t raw = (data[index] << 8) | data[index + 1];hum = raw / 10.0;}index += length;}// 打印結果if (temp != -1) Serial.printf("溫度:%.1f℃ ", temp);if (hum != -1) Serial.printf("濕度:%.1f% ", hum);Serial.println();
}void setup() {Serial.begin(115200);
}void loop() {// 假設收到8字節TLV數據if (Serial.available() >= 8) {uint8_t recv_buf[8];Serial.readBytes(recv_buf, 8);parseTLV(recv_buf, 8);}
}

三、自定義格式(最靈活,適合嵌入式)

實際項目中,常自定義格式(結合文本或二進制),并加入幀頭、幀尾、校驗位(防止數據錯亂)。比如:

格式定義幀頭(0xAA) + 數據長度(1字節) + 數據內容 + 校驗和(1字節) + 幀尾(0x55)

  • 幀頭 / 幀尾:標記數據包的開始和結束(比如 0xAA 開始,0x55 結束)。
  • 數據長度:告訴接收方 "數據內容" 有多少字節,方便解析。
  • 校驗和:所有數據字節的和(取低 8 位),用于檢查數據是否傳輸錯誤。

示例數據包(數據內容是溫度 25.0℃,即 0x00FA):
0xAA 0x02 0x00 0xFA 0xFA 0x55

  • 幀頭:0xAA
  • 數據長度:0x02(數據內容占 2 字節)
  • 數據內容:0x00 0xFA(溫度原始值)
  • 校驗和:0x00 + 0xFA = 0xFA
  • 幀尾:0x55

Python 解析代碼
先找幀頭,再驗證幀尾和校驗和,最后解析數據。

def parse_custom(data):index = 0while index < len(data):# 找幀頭(0xAA)if data[index] != 0xAA:index += 1continue# 檢查是否有足夠的字節(幀頭+長度+校驗+幀尾至少4字節)if index + 4 > len(data):breaklength = data[index + 1]  # 數據長度# 檢查總長度是否足夠(幀頭+長度+數據+校驗+幀尾)total_len = 2 + length + 1 + 1  # 2=幀頭+長度,1=校驗,1=幀尾if index + total_len > len(data):index += 1continue# 提取數據內容content = data[index + 2 : index + 2 + length]# 提取校驗和checksum = data[index + 2 + length]# 提取幀尾tail = data[index + 2 + length + 1]# 驗證幀尾和校驗和if tail != 0x55:index += 1continue# 計算校驗和(數據內容的和)calc_checksum = sum(content) & 0xFFif calc_checksum != checksum:index += 1continue# 解析數據內容(這里是溫度)if length == 2:temp_raw = (content[0] << 8) | content[1]temperature = temp_raw / 10.0print(f"解析成功:溫度={temperature}℃")# 移動到下一個數據包index += total_lenreturn# 假設收到的自定義格式數據
recv_bytes = bytes([0xAA, 0x02, 0x00, 0xFA, 0xFA, 0x55])
parse_custom(recv_bytes)  # 輸出:解析成功:溫度=25.0℃

ESP32 解析代碼
邏輯和 Python 類似,先找幀頭,再驗證校驗和和幀尾。

#include <Arduino.h>void parseCustom(uint8_t* data, int len) {int index = 0;while (index < len) {// 找幀頭0xAAif (data[index] != 0xAA) {index++;continue;}// 檢查最小長度(幀頭+長度+校驗+幀尾=4字節)if (index + 4 > len) break;uint8_t length = data[index + 1];  // 數據長度uint16_t total_len = 2 + length + 1 + 1;  // 總長度// 檢查總長度是否足夠if (index + total_len > len) {index++;continue;}// 提取各部分uint8_t* content = &data[index + 2];  // 數據內容uint8_t checksum = data[index + 2 + length];  // 校驗和uint8_t tail = data[index + 2 + length + 1];  // 幀尾// 驗證幀尾if (tail != 0x55) {index++;continue;}// 計算校驗和uint8_t calc_checksum = 0;for (int i = 0; i < length; i++) {calc_checksum += content[i];}if (calc_checksum != checksum) {index++;continue;}// 解析數據(溫度)if (length == 2) {int16_t temp_raw = (content[0] << 8) | content[1];float temperature = temp_raw / 10.0;Serial.printf("解析成功:溫度=%.1f℃\n", temperature);}// 移動到下一個數據包index += total_len;}
}void setup() {Serial.begin(115200);
}void loop() {// 假設收到6字節自定義數據if (Serial.available() >= 6) {uint8_t recv_buf[6];Serial.readBytes(recv_buf, 6);parseCustom(recv_buf, 6);}
}

總結:如何選擇格式?

  • 簡單場景(少量數據,需要人看懂):用 CSV 或 JSON。
  • 實時性高、數據量大(如傳感器每秒 10 次傳輸):用固定長度二進制。
  • 需要靈活擴展(字段不固定):用 TLV。
  • 嵌入式設備通信(怕數據錯亂):用帶幀頭、校驗的自定義格式。

核心原則:在 "易讀性" 和 "效率 / 可靠性" 之間找平衡

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916803.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916803.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916803.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【開發雜談】用AI玩AI聊天游戲:使用 Electron 和 Python 開發大模型語音聊天軟件

項目地址&#xff1a; GitHub | wfts-ai-chathttps://github.com/HiMeditator/wfts-ai-chat 前言 最近一個基于 AI 的聊天游戲 Whispers from the Stars&#xff08;群星低語&#xff09;的 Demo 版本發布了。《Whispers from the Star》是一款科幻主題互動游戲。背景設定在…

SQL優化系統解析

MySQL的安裝就不講述了, 本篇文章著重講解sql優化 本篇是對B站顏群老師視頻講解的筆記梳理, 感興趣的可以去看下老師的原視頻: SQL優化 MySQL原理 1. MySQL邏輯分層: 連接層->服務層->引擎層->存儲層(如圖) 連接層&#xff1a;提供與客戶端連接的服務服務層&#…

【機器學習案列-25】電信用戶流失預測:從數據處理到模型評估

&#x1f9d1; 博主簡介&#xff1a;曾任某智慧城市類企業算法總監&#xff0c;目前在美國市場的物流公司從事高級算法工程師一職&#xff0c;深耕人工智能領域&#xff0c;精通python數據挖掘、可視化、機器學習等&#xff0c;發表過AI相關的專利并多次在AI類比賽中獲獎。CSDN…

【MATLAB代碼】灰色預測與多項式預測、指數平滑預測的對比,包含預處理、模型構建和和可視化輸出。模擬預測若干年的GDP,訂閱后可查看完整代碼,有中文注釋

代碼實現了灰色預測模型GM(1,1)在GDP預測中的應用,并結合線性回歸、二次多項式回歸和指數平滑模型進行對比分析。代碼包含數據預處理、模型構建、可視化輸出和誤差驗證四個核心模塊,實現了從數據輸入到預測結果展示的全流程。 文章目錄 運行結果 MATLAB源代碼 GM(1,1)模型數學…

搜索二維矩陣Ⅱ C++

編寫一個高效的算法來搜索 m x n 矩陣 matrix 中的一個目標值 target 。該矩陣具有以下特性&#xff1a; 每行的元素從左到右升序排列。 每列的元素從上到下升序排列。class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {i…

如何在 Apache Ignite 中創建和使用自定義 SQL 函數(Custom SQL Functions)

這段內容講的是 如何在 Apache Ignite 中創建和使用自定義 SQL 函數&#xff08;Custom SQL Functions&#xff09;。我們可以分步驟來理解它的含義和用法。&#x1f4da; 一、什么是 Custom SQL Function&#xff1f; Apache Ignite 的 SQL 引擎支持 標準 SQL 函數&#xff08…

Oracle 11g RAC數據庫實例重啟的兩種方式

Oracle 11g RAC數據庫實例重啟的兩種方式 使用SQLPlus重啟數據庫實例 使用SRVCTL重啟數據庫實例 Administrator-Managed還是Policy-Managed ?? 關于關閉RAC的數據庫實例: 在Oracle RAC中,單獨關閉一個實例不會影響到其他正在運行的實例。 要完全關閉Oracle RAC數據庫,需要…

分別使用 Java 8 和 Python 調用 Elasticsearch 接口簡單獲取數據

使用 Java 8 首先,確保在您的 pom.xml 文件中添加了正確的 Maven 依賴: <dependency><groupId>co.elastic.clients</groupId><artifactId>elastic

【通識】數據結構

數據結構邏輯結構物理結構&#xff08;存儲結構&#xff09;&#xff0c;數據結構是計算機中存儲、組織數據的方式。 其中物理結構是數據的邏輯結構在計算機中的存儲形式。而存儲器針對內存而言&#xff0c;像硬盤、軟盤、光盤等外部存儲器的數據組織常用文件結構描述。1. 基礎…

Ubuntu22.04提示找不到python命令的解決方案

Ubuntu22.04提示找不到python命令的解決方案 問題背景 在Ubuntu22.04中按照獲取Openharmony源碼中的如下命令&#xff1a; // 方式一&#xff08;推薦&#xff09;&#xff1a;通過repo ssh下載&#xff08;需注冊公鑰&#xff0c;請參考碼云幫助中心&#xff09;。repo in…

RabbitMQ面試精講 Day 6:消息確認與事務機制

【RabbitMQ面試精講 Day 6】消息確認與事務機制 開篇 歡迎來到"RabbitMQ面試精講"系列的第6天&#xff01;今天我們將深入探討RabbitMQ中確保消息可靠性的兩大核心機制&#xff1a;消息確認與事務機制。這兩個特性是面試中高頻出現的熱點問題&#xff0c;也是生產環…

被困擾的elementplus樣式修改問題:select選擇器修改和el-input修改

一、Select選擇器的原生樣式的本來面貌這是原生的沒有經過任何加工的面貌&#xff1a;這是沒有經過任何加工的選中時出現下拉框的面貌&#xff1a;這是沒有經過加工的懸浮下拉菜單的面貌&#xff1a;這是沒有經過加工的選中時的面貌&#xff1a;二、如何修改Select選擇器&#…

GO 從入門到精通2

Go語言的反射&#xff08;Reflection&#xff09;機制通過 reflect 包實現&#xff0c;允許程序在運行時動態檢查、修改和操作變量的類型信息和值。以下是反射的核心概念、用法及注意事項的詳細解析&#xff1a;一、反射的基本概念reflect.Type 表示變量的類型信息&#xff0c;…

常用設計模式系列(十二)—享元模式

常用設計模式系列&#xff08;十二&#xff09;—享元模式 第一節 前言 昏昏沉沉的兩天過去了&#xff0c;也不知道為什么&#xff0c;突然總覺得很困&#xff0c;可能之前熬夜熬的多了&#xff0c;所以現在可能年紀大了&#xff0c;需要蹦一蹦才能把自己從頹廢的邊緣拉扯回來&…

基于spring boot的醫院掛號就診系統(源碼+論文)

一、開發環境 技術/工具描述MYSQL數據庫1. 體積小&#xff0c;安裝便捷&#xff1a;MySQL數據庫體積小&#xff0c;占用內存小&#xff0c;不影響電腦上其他軟件的運行&#xff0c;并且不需要因為安裝維護MySQL數據庫而重裝系統。2. 適合老舊電腦&#xff1a;作為學習開發的電…

spring-security

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency>spring: security: user: name: root password: 123456 這個配置在訪問接口時候根據您提供的Spring Secur…

搭建一個自定義的 React 圖標庫

搭建一個自定義的 React 圖標庫可以讓你在多個項目中復用統一的圖標資源&#xff0c;同時支持按需加載、主題化和靈活的配置。以下是詳細的步驟指南&#xff1a; 1. 設計圖標庫結構 首先規劃圖標庫的目錄結構和功能&#xff1a; my-react-icons/ ├── src/ │ ├── ico…

寶塔面板如何升級OpenSSL

寶塔面板如何升級OpenSSL&#xff08;親測可用&#xff09;目前一些服務器的OpenSSL還是1.0.1e版本&#xff0c;今天進行服務器漏洞檢測出現OpenSSL存在漏洞&#xff0c;那只能升級OpenSSL了。1、登錄SSH&#xff0c;查看OpenSSL版本openssl version2、下載源代碼wget https://…

深入理解 C++ 紅黑樹:從理論到實踐

引言 在計算機科學領域&#xff0c;數據結構是構建高效算法的基石。而在眾多的數據結構中&#xff0c;平衡二叉搜索樹因其優秀的查找、插入和刪除性能而備受關注。紅黑樹&#xff08;Red-Black Tree&#xff09;作為一種自平衡的二叉搜索樹&#xff0c;更是在 C 標準庫&#x…

外星人筆記本裝win11哪個版本好_外星人筆記本裝win11專業版教程

外星人筆記本安裝win11哪個版本好&#xff1f;答&#xff1a;外星人筆記本還是建議安裝win11專業版。Win分為多個版本&#xff0c;其中家庭版&#xff08;Home&#xff09;和專業版&#xff08;Pro&#xff09;是用戶選擇最多的兩個版本。win11專業版在功能以及安全性方面有著明…