嵌入式网络协议实战:MQTT、HTTP、CoAP

IoT 项目离不开网络通信。本文介绍嵌入式开发中最常用的网络协议,以及 GitHub 上的优质实现代码,帮助你快速实现设备联网。

一、协议对比与选型

协议 传输层 特点 适用场景
MQTT TCP 发布/订阅、轻量 设备上报、远程控制
HTTP TCP 通用、RESTful Web API、配置管理
CoAP UDP 极低功耗、类 REST 电池供电设备
WebSocket TCP 全双工、实时 实时数据推送

二、MQTT 实战

1. MQTT 简介

MQTT(Message Queuing Telemetry Transport)是 IoT 最流行的协议,基于发布/订阅模式:

  • Broker:消息服务器(如 Mosquitto、EMQ X)
  • Topic:消息主题(如 `home/temperature`)
  • QoS:服务质量等级(0/1/2)
  • Retain:保留消息,新订阅者立即收到

2. ESP32 MQTT 客户端

// ESP32 MQTT 完整示例
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>

#define DHTPIN 4
#define DHTTYPE DHT22
#define LED_PIN 2

const char* ssid = "WiFi-SSID";
const char* password = "WiFi-Password";
const char* mqtt_server = "broker.emqx.io";
const int mqtt_port = 1883;

WiFiClient espClient;
PubSubClient client(espClient);
DHT dht(DHTPIN, DHTTYPE);

// MQTT 消息回调
void callback(char* topic, byte* payload, unsigned int length) {
    String message;
    for (int i = 0; i < length; i++) {
        message += (char)payload[i];
    }
    
    Serial.printf("Topic: %s\n", topic);
    Serial.printf("Message: %s\n", message.c_str());
    
    // 处理 LED 控制
    if (String(topic) == "home/led/command") {
        if (message == "ON") {
            digitalWrite(LED_PIN, HIGH);
            client.publish("home/led/status", "ON", true);
        } else if (message == "OFF") {
            digitalWrite(LED_PIN, LOW);
            client.publish("home/led/status", "OFF", true);
        }
    }
}

// 重连 MQTT
void reconnect() {
    while (!client.connected()) {
        String clientId = "ESP32-" + String(random(10000));
        
        if (client.connect(clientId.c_str())) {
            Serial.println("MQTT connected");
            
            // 订阅主题
            client.subscribe("home/led/command");
            client.subscribe("home/config/#");
            
            // 发布上线消息
            client.publish("home/status", "online", true);
        } else {
            Serial.printf("MQTT failed, rc=%d\n", client.state());
            delay(5000);
        }
    }
}

void setup() {
    Serial.begin(115200);
    pinMode(LED_PIN, OUTPUT);
    dht.begin();
    
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("\nWiFi connected");
    
    client.setServer(mqtt_server, mqtt_port);
    client.setCallback(callback);
    client.setBufferSize(512);
}

void loop() {
    if (!client.connected()) {
        reconnect();
    }
    client.loop();
    
    // 每 10 秒发布传感器数据
    static unsigned long lastPublish = 0;
    if (millis() - lastPublish > 10000) {
        float humidity = dht.readHumidity();
        float temperature = dht.readTemperature();
        
        if (!isnan(humidity) && !isnan(temperature)) {
            // JSON 格式发布
            char payload[100];
            snprintf(payload, sizeof(payload),
                "{\"temperature\":%.1f,\"humidity\":%.1f}",
                temperature, humidity);
            
            client.publish("home/sensors", payload);
            Serial.printf("Published: %s\n", payload);
        }
        lastPublish = millis();
    }
}

三、HTTP 客户端

1. ESP32 HTTP GET/POST

// ESP32 HTTP 客户端示例
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

const char* ssid = "WiFi-SSID";
const char* password = "WiFi-Password";

// HTTP GET 请求
void httpGet() {
    HTTPClient http;
    http.begin("http://api.openweathermap.org/data/2.5/weather?q=Beijing&appid=YOUR_API_KEY");
    
    int httpResponseCode = http.GET();
    
    if (httpResponseCode > 0) {
        String response = http.getString();
        
        // 解析 JSON
        StaticJsonDocument<1024> doc;
        deserializeJson(doc, response);
        
        float temp = doc["main"]["temp"];
        float humidity = doc["main"]["humidity"];
        
        Serial.printf("Temperature: %.1f°C\n", temp - 273.15);
        Serial.printf("Humidity: %.1f%%\n", humidity);
    }
    
    http.end();
}

// HTTP POST 请求
void httpPost(float temperature, float humidity) {
    HTTPClient http;
    http.begin("http://your-server.com/api/sensors");
    http.addHeader("Content-Type", "application/json");
    
    // 构建 JSON
    StaticJsonDocument<256> doc;
    doc["device_id"] = "esp32-001";
    doc["temperature"] = temperature;
    doc["humidity"] = humidity;
    doc["timestamp"] = millis();
    
    String jsonString;
    serializeJson(doc, jsonString);
    
    int httpResponseCode = http.POST(jsonString);
    
    if (httpResponseCode > 0) {
        String response = http.getString();
        Serial.printf("Response: %s\n", response.c_str());
    }
    
    http.end();
}

四、CoAP 协议

1. CoAP 简介

CoAP(Constrained Application Protocol)是专为受限设备设计的协议:

  • 基于 UDP,开销极小
  • 类 RESTful API(GET/POST/PUT/DELETE)
  • 支持确认消息(CON)和非确认消息(NON)
  • 支持 Observe 模式(类似 MQTT 订阅)

2. CoAP 客户端示例

// ESP32 CoAP 客户端
#include <coap_simple.h>

WiFiUDP udp;
CoapSimple coap(udp);

// CoAP 资源回调
void coap_callback(CoapPacket &packet, IPAddress ip, int port) {
    char payload[100];
    packet.payloadcopy(payload);
    
    Serial.printf("CoAP request from %s:%d\n", ip.toString().c_str(), port);
    
    // 返回传感器数据
    float temp = dht.readTemperature();
    char response[50];
    snprintf(response, sizeof(response), "{\"temp\":%.1f}", temp);
    
    coap.respond(packet, (uint8_t*)response, strlen(response));
}

void setup() {
    // 注册 CoAP 资源
    coap.server(coap_callback, "sensor/temperature");
    coap.server(coap_callback, "sensor/humidity");
    coap.server(coap_callback, "led/status");
    
    coap.start(5683);
}

void loop() {
    coap.loop();
    
    // CoAP 定时上报(Observe 模式)
    static unsigned long lastNotify = 0;
    if (millis() - lastNotify > 30000) {
        float temp = dht.readTemperature();
        char payload[50];
        snprintf(payload, sizeof(payload), "{\"temp\":%.1f}", temp);
        
        coap.notifyObservers("sensor/temperature", 
                            (uint8_t*)payload, strlen(payload));
        lastNotify = millis();
    }
}

五、WebSocket 实时通信

// ESP32 WebSocket 服务器
#include <WebServer.h>
#include <WebSocketsServer.h>

WebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
    switch(type) {
        case WStype_DISCONNECTED:
            Serial.printf("WebSocket [%u] disconnected\n", num);
            break;
            
        case WStype_CONNECTED:
            Serial.printf("WebSocket [%u] connected\n", num);
            webSocket.sendTXT(num, "Connected to ESP32");
            break;
            
        case WStype_TEXT:
            String message = String((char*)payload);
            Serial.printf("WebSocket [%u]: %s\n", num, message.c_str());
            
            // 处理命令
            if (message == "get_temperature") {
                float temp = dht.readTemperature();
                char response[50];
                snprintf(response, sizeof(response), "{\"temp\":%.1f}", temp);
                webSocket.sendTXT(num, response);
            }
            break;
    }
}

void setup() {
    webSocket.begin();
    webSocket.onEvent(webSocketEvent);
    server.begin();
}

void loop() {
    webSocket.loop();
    server.handleClient();
    
    // 每 1 秒广播传感器数据
    static unsigned long lastBroadcast = 0;
    if (millis() - lastBroadcast > 1000) {
        float temp = dht.readTemperature();
        float humidity = dht.readHumidity();
        
        char json[100];
        snprintf(json, sizeof(json), 
            "{\"temp\":%.1f,\"humidity\":%.1f}", temp, humidity);
        
        webSocket.broadcastTXT(json);
        lastBroadcast = millis();
    }
}

六、协议选型指南

推荐

ESP32 开发板 - 网络协议开发首选

内置 WiFi 和蓝牙,支持 MQTT、HTTP、CoAP、WebSocket 等所有协议。

查看 Amazon 价格 →
// 协议选择决策树
需要实时双向通信?
├── 是 → WebSocket
└── 否 → 
    需要低功耗?
    ├── 是 → CoAP
    └── 否 → 
        设备数量多?
        ├── 是 → MQTT(发布/订阅模式)
        └── 否 → HTTP(简单直接)

// 典型应用场景
MQTT   → 传感器数据上报、智能家居控制
HTTP   → 固件升级、配置管理、Web API
CoAP   → 电池供电设备、LPWAN 网络
WebSocket → 实时监控面板、远程调试
选择合适的网络协议是 IoT 项目成功的关键,需要根据功耗、延迟、可靠性需求综合考虑。
← 返回首页