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 - 设备IDok: 是否成功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 请求超时
- 检查 WiFi 连接稳定性
- 增加 HTTP 超时时间到 5-10 秒
- 添加重试机制
Q: JSON 解析失败
- 使用
StaticJsonDocument而非DynamicJsonDocument - 确保文档大小足够(建议 512 字节)
- 检查内存是否充足
Q: 内存不足
- 使用简化的请求/响应字段
- 减少 JSON 文档大小
- 及时释放 HTTP 连接
Q: 回答被截断
HTTP API 会自动截断超过 200 字符的回答,以适应 ESP32 内存限制。