跳转到内容

常见陷阱与解决方案

常见陷阱与解决方案

本节汇总 IoT 方案开发和部署中的常见陷阱及解决方案。这些问题来源于课程中所有项目的真实经验,售前工程师可以通过这些案例展示方案的专业性,帮助规避常见问题。

学习完成后您将能够:

  • 识别 IoT 项目中的常见陷阱
  • 在方案设计阶段预防潜在问题
  • 快速排查和解决常见故障
  • 向客户解释如何避免这些问题

问题描述: ESP32 部分 GPIO 被内部 Flash/PSRAM 占用,使同时使用导致系统崩溃。

受影响的 GPIO:

GPIO冲突资源说明
GPIO 6-11Flash/PSRAM不可用于外部电路
GPIO 1UART0 TX调试串口占用
GPIO 3UART0 RX调试串口占用
GPIO 12启动电压检测上拉影响启动模式
GPIO 16-17PSRAM使用 PSRAM 时占用

解决方案:

  • 查看 ESP32 引脚功能表,避免使用冲突引脚
  • 使用 GPIO 21/22 作为 I2C 标准引脚
  • 调试完成后可复用 UART0 引脚(需谨慎)

问题描述: ESP32 峰值电流可达 500mA,使用 USB 线缆过长或劣质电源导致电压跌落、WiFi 断连。

诊断方法:

// 读取 ESP32 内部电压(ADC 通道 0)
uint32_t voltage = analogRead(36);
Serial.printf("Supply voltage: %d mV\n", voltage);

解决方案:

  • 使用 2A 以上电源适配器
  • USB 线缆长度不超过 1 米
  • 电源引脚增加 470uF 电解电容
  • 大功率外设单独供电

问题描述: ESP32 进入 Deep Sleep 后 WiFi 指示灯熄灭,但无法被定时器或外部信号唤醒。

常见原因:

  • GPIO 唤醒电平不匹配(高电平/低电平)
  • Timer 唤醒时间设置错误
  • 未调用 esp_deep_sleep_start()

解决方案:

// 正确的 Deep Sleep 配置
esp_sleep_enable_timer_wakeup(30 * 1000000); // 30 秒
esp_sleep_enable_ext0_wakeup(GPIO_NUM_0, LOW); // 按钮接 GND
Serial.flush(); // 确保串口数据发送完毕
esp_deep_sleep_start(); // 进入 Deep Sleep

问题描述: 多个设备使用相同 Client ID 连接 Broker,导致设备互踢。

错误示例:

// Client ID 写死,所有设备相同
client.setClientId("ESP32_Client");

解决方案:

// 使用芯片 MAC 地址生成唯一 Client ID
String clientId = "ESP32_Client_" + String((uint32_t)ESP.getEfuseMac(), HEX);
client.setClientId(clientId.c_str());

问题描述: MQTT payload 超过默认缓冲区大小(256 字节),导致消息截断或连接断开。

解决方案:

// 增大 MQTT 缓冲区(在 setup 中)
client.setBufferSize(2048); // 增大到 2KB
// 或减少发送数据量
// 错误的 payload(过大)
// {"timestamp":"2024-01-01T00:00:00Z","device_id":"ESP32_Sensor_001","sensors":[{"type":"temperature","value":25.5,"unit":"C"},{"type":"humidity","value":65.2,"unit":"%"}]}
// 正确的 payload(精简)
// {"t":25.5,"h":65.2}

问题描述: 传感器数据使用 QoS 2 导致 Broker 和网络负载过大;控制命令使用 QoS 0 导致命令丢失。

错误做法后果正确做法
传感器数据用 QoS 2网络拥堵 + 存储膨胀使用 QoS 0
控制命令用 QoS 0命令可能丢失使用 QoS 1
状态消息 QoS 0新订阅者得不到状态QoS 1 + Retain

问题描述: Node-RED Function 节点中不当的变量管理导致内存持续增长,最终 OOM。

错误示例:

// 全局变量不断增长
var list = context.global.get("dataList") || [];
list.push(msg.payload); // 无限增长
context.global.set("dataList", list);

解决方案:

// 限制数据大小
var list = context.global.get("dataList") || [];
list.push(msg.payload);
if (list.length > 1000) {
list.shift(); // 移除最早的数据
}
context.global.set("dataList", list);
// 或使用 InfluxDB 存储,不在 Node-RED 内存保存

问题描述: 时序数据写入时精度不足,或字段类型不一致导致查询错误。

解决方案:

// Node-RED InfluxDB Out 节点配置
// 建议设置
{
"precision": "ms", // 毫秒精度
"fieldType": { // 显式指定字段类型
"temperature": "float",
"humidity": "float",
"rssi": "integer"
}
}

问题描述: WiFi 密码、MQTT 密码等敏感信息直接写在代码中,通过固件反编译可提取。

错误示例:

const char* ssid = "MyWiFi";
const char* password = "MyPassword123";

解决方案:

// 方案 1:使用编译时加密
#include <esp_wifi.h>
const char* ssid = "MyWiFi"; // 使用编译符号
const char* password = "MyPassword123";
// 方案 2:从 EEPROM 读取(生产推荐)
String ssid = readStringFromEEPROM(0);
String password = readStringFromEEPROM(32);
// 方案 3:使用 ESP32 NVS(推荐生产环境)
#include <Preferences.h>
Preferences prefs;
prefs.begin("wifi", false);
String ssid = prefs.getString("ssid", "");
String password = prefs.getString("pass", "");

问题描述: 使用 setInsecure() 跳过证书验证,使 TLS 形同虚设。

错误示例:

// 跳过证书验证(不安全)
client.setInsecure();

解决方案:

// 使用 CA 证书验证(推荐)
#include "ca_cert.h" // Let's Encrypt 或其他 CA 证书
client.setCACert(ca_cert);
// 双向验证(最高安全级)
client.setCertificate(client_cert); // 设备证书
client.setPrivateKey(priv_key); // 私钥

问题描述: Docker 容器文件权限配置的卷映射路径错误导致数据持久化失败。

解决方案:

# docker-compose.yml 正确配置
volumes:
- ./mosquitto/data:/mosquitto/data:rw
- ./mosquitto/log:/mosquitto/log:rw
- ./mosquitto/config:/mosquitto/config:ro # 配置文件只读
# 注意:映射后宿主目录需要 1883 权限(Mosquitto UID)

问题描述: 多个服务使用相同端口导致冲突,Broker 无法启动。

排查方法:

Terminal window
# 检查端口占用
sudo lsof -i :1883 # MQTT 端口
sudo lsof -i :8883 # MQTT TLS
sudo lsof -i :9001 # MQTT WebSocket

解决方案:

# 修改 docker-compose.yml 中冲突端口
services:
mosquitto:
ports:
- "18830:1883" # 修改宿主端口为 18830
- "88830:8883" # 修改宿主端口为 88830
症状可能原因排查命令解决方案
ESP32 无法连接 WiFiSSID/密码错误串口日志 [WIFI] 标签检查凭证、信号强度
MQTT 连接失败Broker 地址/端口错误mosquitto_sub -v -t "#"检查 Broker 配置
Broker 拒绝连接匿名访问关闭添加 allow_anonymous true或配置用户名密码
传感器读数异常电源噪声analogRead() 多次采样取平均增加滤波电容
OTA 更新失败分区表不匹配检查 Partitions CSV重新分区
Node-RED 无输出节点未部署检查右上角 “Deploy” 按钮重新部署

本节要点总结:

  1. 硬件故障多由 GPIO 冲突和供电不足引起,设计阶段即可规避
  2. MQTT 故障主要是 Client ID、缓冲区、QoS 配置不当
  3. 软件问题重点关注内存管理和数据精度
  4. 安全漏洞以硬编码凭证和证书验证跳过最为常见
  5. 部署问题集中在 Docker 配置和端口管理