通过 MQTT 拍照
通过 MQTT 拍照
本节介绍如何通过 MQTT 消息远程触发 ESP32-CAM 拍照。学习完成后,您将能够:
- 在 ESP32 上实现 MQTT 拍照控制
- 通过 Node-RED 发送拍照命令
- 控制闪光灯和拍照参数
- 构建远程巡检的拍照触发流程
┌────────────────────────────────────────────────────────────┐│ 远程拍照控制流程 │├────────────────────────────────────────────────────────────┤│ ││ [Node-RED] ││ │ ││ │ MQTT: esp32cam/command (拍照/闪光灯控制) ││ ▼ ││ [Mosquitto Broker] ││ │ ││ ▼ ││ [ESP32-CAM] ││ │ ││ ├──→ 拍照 → MQTT: esp32cam/photo (图片数据) ││ │ ││ └──→ 控制闪光灯 ││ ││ Node-RED 接收图片 → 保存/显示/推送 ││ │└────────────────────────────────────────────────────────────┘ESP32 MQTT Photo Control Sketch
Section titled “ESP32 MQTT Photo Control Sketch”#include <WiFi.h>#include <PubSubClient.h>#include "esp_camera.h"
// Wi-Fi 配置const char* ssid = "YOUR_SSID";const char* password = "YOUR_PASSWORD";
// MQTT 配置const char* mqtt_server = "192.168.1.100";const int mqtt_port = 1883;const char* mqtt_user = "iot_user";const char* mqtt_pass = "iot_password";const char* client_id = "esp32cam-01";
// MQTT Topicconst char* topic_command = "esp32cam/command";const char* topic_photo = "esp32cam/photo";const char* topic_status = "esp32cam/status";
// MQTT 客户端WiFiClient espClient;PubSubClient client(espClient);
// 闪光灯控制变量bool flashEnabled = false;int flashPin = 4; // ESP32-CAM 内置 LED
// MQTT 回调函数void callback(char* topic, byte* payload, unsigned int length) { String message; for (int i = 0; i < length; i++) { message += (char)payload[i]; }
Serial.println("MQTT command received: " + message);
if (message == "take_photo") { takePhoto(); } else if (message == "flash_on") { flashEnabled = true; digitalWrite(flashPin, HIGH); client.publish(topic_status, "flash_on"); } else if (message == "flash_off") { flashEnabled = false; digitalWrite(flashPin, LOW); client.publish(topic_status, "flash_off"); } else if (message == "flash_toggle") { flashEnabled = !flashEnabled; digitalWrite(flashPin, flashEnabled ? HIGH : LOW); client.publish(topic_status, flashEnabled ? "flash_on" : "flash_off"); }}
// 拍照函数void takePhoto() { Serial.println("Taking photo...");
// 如果闪光灯开启,先点亮 if (flashEnabled) { digitalWrite(flashPin, HIGH); delay(200); // 等待闪光灯稳定 }
// 拍照 camera_fb_t* fb = esp_camera_fb_get(); if (!fb) { Serial.println("Camera capture failed!"); client.publish(topic_status, "error:capture_failed"); if (flashEnabled) digitalWrite(flashPin, LOW); return; }
Serial.printf("Photo taken: %zu bytes\n", fb->len);
// 通过 MQTT 发送图片 if (client.connected()) { // 发送图片数据 (二进制) bool sent = client.publish(topic_photo, fb->buf, fb->len, false); if (sent) { Serial.println("Photo sent via MQTT"); client.publish(topic_status, "photo_sent"); } else { Serial.println("MQTT publish failed!"); client.publish(topic_status, "error:publish_failed"); } }
// 关闭闪光灯 if (flashEnabled) { digitalWrite(flashPin, LOW); }
// 释放帧缓冲 esp_camera_fb_return(fb);}
void setup() { Serial.begin(115200);
// 初始化 Flash LED pinMode(flashPin, OUTPUT); digitalWrite(flashPin, LOW);
// 初始化摄像头 (见 11-02) camera_config_t config; // ... (完整的摄像头初始化代码) esp_camera_init(&config);
// 连接 Wi-Fi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); } Serial.println("WiFi connected");
// 连接 MQTT client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); connectMQTT();
Serial.println("ESP32-CAM ready for MQTT commands");}
void connectMQTT() { while (!client.connected()) { if (client.connect(client_id, mqtt_user, mqtt_pass)) { Serial.println("MQTT connected"); client.subscribe(topic_command); client.publish(topic_status, "online"); } else { delay(5000); } }}
void loop() { if (!client.connected()) { connectMQTT(); } client.loop();}Node-RED Control Flow
Section titled “Node-RED Control Flow”发送拍照命令
Section titled “发送拍照命令”[Inject: 手动触发] ──→ [MQTT Out: esp32cam/command][Inject: 定时拍照] ──→ [MQTT Out: esp32cam/command]// Inject 节点 - 手动拍照{ "name": "拍照", "payload": "take_photo", "payloadType": "str", "repeat": ""}
// Inject 节点 - 定时拍照 (每 30 分钟){ "name": "定时巡检", "payload": "take_photo", "payloadType": "str", "repeat": "cron", "cron": "*/30 * * * *"}
// Inject 节点 - 开启闪光灯{ "name": "开闪光灯", "payload": "flash_on", "payloadType": "str"}
// MQTT Out 节点{ "name": "ESP32-CAM命令", "topic": "esp32cam/command", "qos": 1, "broker": "local-broker"}完整控制 Dashboard
Section titled “完整控制 Dashboard”┌──────────────────────────────────────────────┐│ ESP32-CAM 远程拍照控制面板 │├──────────────────────────────────────────────┤│ ││ [📷 拍照] [💡 闪光灯] [⏰ 定时(30分)] ││ ││ 状态: 🟢 在线 ││ 最后拍照: 2026-05-18 14:30:25 ││ 照片大小: 32.5 KB ││ │└──────────────────────────────────────────────┘MQTT Buffer Configuration
Section titled “MQTT Buffer Configuration”由于图片数据较大,需要增加 MQTT 缓冲大小:
// 在 setup() 中设置最大包大小#define PHOTO_BUFFER_SIZE 60000 // 60KB
// 在 MQTT 连接后设置void setup() { // ... client.setBufferSize(PHOTO_BUFFER_SIZE); // ...}
// PubSubClient 默认 256 字节,需要增大// 在 platformio.ini 中:// build_flags = -DMQTT_MAX_PACKET_SIZE=60000# 测试拍照命令mosquitto_pub -t "esp32cam/command" -m "take_photo"
# 检查照片是否发布mosquitto_sub -t "esp32cam/photo" -C 1 > photo.jpg
# 检查图片大小和完整性ls -la photo.jpgfile photo.jpgCommon Customer Questions
Section titled “Common Customer Questions”Q1: MQTT 传输图片速度如何?
Section titled “Q1: MQTT 传输图片速度如何?”VGA (640×480) JPEG 约 30-50KB,通过 MQTT 传输约 1-3 秒(取决于 Wi-Fi 质量)。如果需要更快传输,可降低分辨率或 JPEG 质量。
Q2: 可以连续拍照吗?
Section titled “Q2: 可以连续拍照吗?”可以,但每次拍照后需要等待前一张图片发送完成。建议拍照间隔至少 5-10 秒,避免 MQTT 缓冲溢出。
Q3: 图片传输失败怎么办?
Section titled “Q3: 图片传输失败怎么办?”建议实现确认机制:ESP32 发送图片后等待 Node-RED 确认。如果超时未确认,重试发送。
✅ 推荐做法:
- MQTT 缓冲大小设置为 60000 (60KB)
- 拍照命令使用 QoS 1 确保可靠投递
- 拍照后释放帧缓冲避免内存泄漏
- 使用状态 Topic 反馈拍照结果
❌ 避免做法:
- 默认 MQTT 缓冲 (256 字节) 传输图片
- 在中断中调用 MQTT 发布函数
- 拍照频率过高导致 MQTT 队列溢出
- 忽略图片传输确认机制
Summary
Section titled “Summary”- MQTT 拍照控制通过
esp32cam/commandTopic 远程触发拍照 - 图片数据通过
esp32cam/photoTopic 以二进制形式传输 - MQTT 缓冲需要设置为至少 60KB 以传输图片
- 闪光灯控制和拍照可独立控制
- Node-RED 可通过 Inject 节点实现手动或定时拍照