ESP32 IoT 设备接入指南

本文档介绍如何将 ESP32 设备接入 MBE 智能服务。

接入方式

ESP32 支持两种接入方式:

方式 协议 适用场景 延迟
HTTP REST HTTP/HTTPS 简单查询、低频交互 ~2秒
MQTT MQTT 3.1.1 实时通信、双向控制 ~100ms

HTTP REST API 接入

端点配置

配置项
查询接口 https://mbe.hi-maker.com/api/esp32/query
传感器上报 https://mbe.hi-maker.com/api/esp32/sensor
健康检查 https://mbe.hi-maker.com/api/esp32/ping
调试页面 https://mbe.hi-maker.com/api/esp32/debug

请求/响应格式

智能问答

请求 (POST /api/esp32/query):

{
  "q": "问题内容",
  "did": "设备ID"
}

响应:

{
  "ok": true,
  "a": "回答内容",
  "e": "专家名称"
}

字段说明(极简设计,节省带宽):

  • q: query - 问题
  • did: device_id - 设备ID
  • ok: 是否成功
  • a: answer - 回答
  • e: expert - 专家名称

传感器上报

请求 (POST /api/esp32/sensor):

{
  "did": "esp32_001",
  "ts": 1706188800000,
  "temp": 25.5,
  "humi": 60,
  "light": 500,
  "motion": false,
  "battery": 85
}

响应:

{
  "ok": true,
  "cmd": "",
  "params": {}
}

如果有待执行的命令,cmd 字段会返回命令名。

Arduino 示例代码

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

// WiFi 配置
const char* WIFI_SSID = "your_wifi";
const char* WIFI_PASS = "your_password";

// MBE 服务配置
const char* MBE_SERVER = "https://mbe.hi-maker.com";
const char* DEVICE_ID = "esp32_001";

void setup() {
  Serial.begin(115200);
  
  // 连接 WiFi
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected");
}

// 智能问答
String askQuestion(String question) {
  if (WiFi.status() != WL_CONNECTED) {
    return "WiFi disconnected";
  }
  
  HTTPClient http;
  http.begin(String(MBE_SERVER) + "/api/esp32/query");
  http.addHeader("Content-Type", "application/json");
  http.setTimeout(5000);  // 5秒超时
  
  // 构建请求
  StaticJsonDocument<256> doc;
  doc["q"] = question;
  doc["did"] = DEVICE_ID;
  
  String body;
  serializeJson(doc, body);
  
  // 发送请求
  int httpCode = http.POST(body);
  
  if (httpCode == 200) {
    String response = http.getString();
    
    StaticJsonDocument<512> respDoc;
    DeserializationError error = deserializeJson(respDoc, response);
    
    if (!error && respDoc["ok"].as<bool>()) {
      http.end();
      return respDoc["a"].as<String>();
    }
  }
  
  http.end();
  return "Error: " + String(httpCode);
}

// 上报传感器数据
bool reportSensor(float temp, float humi, int battery) {
  if (WiFi.status() != WL_CONNECTED) {
    return false;
  }
  
  HTTPClient http;
  http.begin(String(MBE_SERVER) + "/api/esp32/sensor");
  http.addHeader("Content-Type", "application/json");
  http.setTimeout(3000);
  
  StaticJsonDocument<256> doc;
  doc["did"] = DEVICE_ID;
  doc["ts"] = millis();  // 或使用 NTP 时间
  doc["temp"] = temp;
  doc["humi"] = humi;
  doc["battery"] = battery;
  
  String body;
  serializeJson(doc, body);
  
  int httpCode = http.POST(body);
  http.end();
  
  return httpCode == 200;
}

void loop() {
  // 示例:每10秒问一个问题
  static unsigned long lastAsk = 0;
  if (millis() - lastAsk > 10000) {
    lastAsk = millis();
    
    String answer = askQuestion("今天天气怎么样");
    Serial.println("Answer: " + answer);
  }
  
  // 示例:每60秒上报传感器数据
  static unsigned long lastReport = 0;
  if (millis() - lastReport > 60000) {
    lastReport = millis();
    
    // 读取传感器(示例值)
    float temp = 25.5;
    float humi = 60.0;
    int battery = 85;
    
    if (reportSensor(temp, humi, battery)) {
      Serial.println("Sensor reported");
    }
  }
  
  delay(100);
}

MicroPython 示例代码

import urequests
import ujson
import time

MBE_SERVER = "https://mbe.hi-maker.com"
DEVICE_ID = "esp32_001"

def ask_question(question):
    """智能问答"""
    url = f"{MBE_SERVER}/api/esp32/query"
    data = {"q": question, "did": DEVICE_ID}
    
    try:
        response = urequests.post(url, json=data, timeout=5)
        result = response.json()
        response.close()
        
        if result.get("ok"):
            return result.get("a", "")
        return f"Error: {result.get('err', 'unknown')}"
    except Exception as e:
        return f"Error: {e}"

def report_sensor(temp, humi, battery=None):
    """上报传感器数据"""
    url = f"{MBE_SERVER}/api/esp32/sensor"
    data = {
        "did": DEVICE_ID,
        "ts": time.time() * 1000,
        "temp": temp,
        "humi": humi
    }
    if battery is not None:
        data["battery"] = battery
    
    try:
        response = urequests.post(url, json=data, timeout=3)
        result = response.json()
        response.close()
        return result.get("ok", False)
    except:
        return False

# 使用示例
answer = ask_question("离婚需要什么手续")
print(f"Answer: {answer}")

report_sensor(25.5, 60, 85)

MQTT 接入

MQTT 配置

配置项
Broker mqtt.hi-maker.com
端口 1883 (TCP) / 8883 (TLS)
用户名 设备ID
密码 设备密钥

主题规范

主题 方向 说明
mbe/devices/{device_id}/request 设备→服务器 发送查询
mbe/devices/{device_id}/response 服务器→设备 接收回答
mbe/devices/{device_id}/command 服务器→设备 接收命令
mbe/devices/broadcast 服务器→所有设备 广播消息

MQTT 消息格式

请求消息:

{
  "id": "消息ID",
  "text": "问题内容",
  "user_id": "用户ID(可选)"
}

响应消息:

{
  "id": "消息ID",
  "success": true,
  "content": "回答内容",
  "expert_id": "专家ID",
  "expert_name": "专家名称",
  "timestamp": "2024-01-25T12:00:00Z"
}

Arduino MQTT 示例

#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

const char* MQTT_SERVER = "mqtt.hi-maker.com";
const int MQTT_PORT = 1883;
const char* DEVICE_ID = "esp32_001";

WiFiClient espClient;
PubSubClient mqtt(espClient);

String requestTopic = String("mbe/devices/") + DEVICE_ID + "/request";
String responseTopic = String("mbe/devices/") + DEVICE_ID + "/response";

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  StaticJsonDocument<512> doc;
  deserializeJson(doc, payload, length);
  
  if (doc["success"].as<bool>()) {
    String answer = doc["content"].as<String>();
    String expert = doc["expert_name"].as<String>();
    Serial.println("[" + expert + "] " + answer);
  }
}

void setup() {
  Serial.begin(115200);
  
  // 连接 WiFi...
  
  mqtt.setServer(MQTT_SERVER, MQTT_PORT);
  mqtt.setCallback(mqttCallback);
}

void reconnect() {
  while (!mqtt.connected()) {
    if (mqtt.connect(DEVICE_ID)) {
      mqtt.subscribe(responseTopic.c_str());
    } else {
      delay(5000);
    }
  }
}

void askQuestion(String question) {
  StaticJsonDocument<256> doc;
  doc["id"] = String(millis());
  doc["text"] = question;
  
  String payload;
  serializeJson(doc, payload);
  
  mqtt.publish(requestTopic.c_str(), payload.c_str());
}

void loop() {
  if (!mqtt.connected()) {
    reconnect();
  }
  mqtt.loop();
  
  // 定时发送问题
  static unsigned long lastAsk = 0;
  if (millis() - lastAsk > 30000) {
    lastAsk = millis();
    askQuestion("今天天气怎么样");
  }
}

性能指标

指标 HTTP MQTT
响应延迟 1.5-2.5秒 0.5-1.5秒
内存占用 ~20KB ~30KB
适合场景 低频查询 实时交互

常见问题

Q: HTTP 请求超时

  1. 检查 WiFi 连接稳定性
  2. 增加 HTTP 超时时间到 5-10 秒
  3. 添加重试机制

Q: JSON 解析失败

  1. 使用 StaticJsonDocument 而非 DynamicJsonDocument
  2. 确保文档大小足够(建议 512 字节)
  3. 检查内存是否充足

Q: 内存不足

  1. 使用简化的请求/响应字段
  2. 减少 JSON 文档大小
  3. 及时释放 HTTP 连接

Q: 回答被截断

HTTP API 会自动截断超过 200 字符的回答,以适应 ESP32 内存限制。

相关文档