门铃接入小智AI,实现智能看家
·
小智AI接入萤石云摄像头:MQTT 实时告警 + 图片预览
让小智AI/ESP32 设备秒变智能安防终端——门口摄像头检测到有人出现时,屏幕自动弹出告警消息、播放提示音,还能下载并显示现场抓拍图片。
整体架构
萤石摄像头(门铃)
│
├─→ 萤石开放平台 ←─ 注册设备、开启告警推送
│ │
│ ├─→ 告警事件推送 → 消息回调服务 → 发布到 MQTT Broker
│ │ │
│ │ ├─→ 飞书机器人 ←─ 手机端实时查看
│ │ │
│ │ └─→ 小智AI(ESP32) ←─ MQTT 订阅
│ │ │
│ │ ├─ 屏幕显示告警气泡
│ │ ├─ 播放提示音
│ │ └─ 下载并显示抓拍图片
步骤一:门铃接入萤石开放平台
1. 注册萤石开放平台账号
访问 萤石开放平台,注册并登录开发者账号。
2. 添加设备
- 在萤石 APP 中确保门铃已联网并正常工作
- 在开放平台「我的资源」中添加设备,或扫描设备二维码完成绑定
3. 创建应用获取 AppKey / AppSecret
- 进入「我的应用」→ 创建应用
- 获取
AppKey和AppSecret,后续 API 调用需要用到
4. 开启告警推送
- 在「告警服务」中开启设备的移动侦测/门铃事件推送
- 配置告警推送地址为你的服务器回调 URL
步骤二:部署消息回调服务
萤石开放平台会将告警事件以 HTTP POST 方式推送到你的服务器,需要部署一个回调服务来接收并转发。
服务功能
萤石告警推送 → 回调服务解析 → 提取消息文本和图片URL
│
├─→ 发布到 MQTT Broker (qiongmu/mqtt/ys)
└─→ 推送飞书机器人 webhook
部署方式
- 服务器:任意 Linux 服务器(阿里云、腾讯云等)
- HTTPS:萤石回调要求 HTTPS,可用 Nginx 反向代理 + Let’s Encrypt 证书
- 保活:使用
systemd或supervisor管理进程
步骤三:接入飞书机器人
1. 创建飞书自定义机器人
- 打开飞书群聊 → 设置 → 群机器人 → 添加机器人 → 自定义机器人
- 获取 Webhook 地址
2. 配置回调服务
在上面的回调服务中配置 FEISHU_WEBHOOK,告警消息会同时推送到飞书:
🚨 门口有人出现!
📷 抓拍: https://i.ys7.com/streamer/alarm/url/get?fileId=...
3. 效果
- 手机端:飞书 APP 收到群消息推送,随时查看
- 带图片:点击链接可查看抓拍图片
- 可配合飞书自动化:设置特别关注、DND 时段等
步骤四:小智接入 MQTT 实现告警
设备端实现
在小智AI(ESP32-S3)上运行 MqttListener,独立订阅 MQTT 告警主题:
// mqtt_listener.h - 独立 MQTT 客户端
class MqttListener {
// TLS 连接 emqxsl.cn:8883
// 订阅 qiongmu/mqtt/ys
static MqttListener& GetInstance();
void Initialize();
void Start();
};
在 Application::Initialize() 中初始化:
auto& mqtt_listener = MqttListener::GetInstance();
mqtt_listener.SetMessageCallback([this](const std::string& topic, const std::string& payload) {
OnMqttMessage(payload);
});
mqtt_listener.Initialize();
// 网络激活后启动
mqtt_listener.Start();
消息处理
MQTT 消息通过事件队列进入主事件循环:
MQTT 收到 → 入队 → MAIN_EVENT_MQTT_MESSAGE
↓
HandleMqttMessageEvent()
↓
├── 解析 JSON → msg + pic
├── SetChatMessage("system", msg) → 显示气泡消息
├── PlaySound(OGG_SUCCESS) → 播放提示音(3次)
└── xTaskCreate() → 后台下载图片
图片下载与显示
完整流程
HTTP GET → 跟随 302 重定向(最多3次) → 下载 JPEG
↓
jpeg_to_image() → 解码为 RGB565
↓
最近邻缩放至 320×320(保持宽高比)
↓
LvglAllocatedImage → SetPreviewImage() 显示
重定向处理
萤石云图片 URL 返回 302 跳转到 AWS S3:
while (redirect_count < 3) {
auto http = Board::GetInstance().GetNetwork()->CreateHttp(3);
http->SetKeepAlive(false); // 禁用连接复用
http->Open("GET", current_url);
if (status_code == 302) {
std::string location = http->GetResponseHeader("Location");
http->Close();
current_url = location;
redirect_count++;
vTaskDelay(pdMS_TO_TICKS(200));
continue;
}
// 下载并解码...
}
JPEG 解码 + 缩放
使用 ESP-IDF 自带的 JPEG 解码器(优先硬件加速),解码后缩放到 320×320:
// JPEG 解码
uint8_t* rgb565 = nullptr;
esp_err_t err = jpeg_to_image(jpeg_data, content_length,
&rgb565, &rgb_len, &img_width, &img_height, &img_stride);
// 缩放到 320×320(保持宽高比)
const int MAX_DIM = 320;
if (img_width > MAX_DIM || img_height > MAX_DIM) {
int scale_w = (img_width > img_height) ? MAX_DIM : (MAX_DIM * img_width / img_height);
int scale_h = (img_height > img_width) ? MAX_DIM : (MAX_DIM * img_height / img_width);
uint8_t* scaled = heap_caps_malloc(scale_w * scale_h * 2, MALLOC_CAP_SPIRAM);
downscale_rgb565((uint16_t*)rgb565, img_width, img_height, scaled, scale_w, scale_h);
heap_caps_free(rgb565); // 立即释放原始大图
}
内存对比
| 原始分辨率 | 解码后 | 缩放后 |
|---|---|---|
| 1920×1080 | ~4MB | ~200KB |
| 1280×720 | ~1.8MB | ~112KB |
| 640×480 | ~600KB | ~150KB |
内存保护
ESP32 内存有限,下载前检查可用堆大小:
if (esp_get_free_heap_size() < 80 * 1024) {
ESP_LOGW(TAG, "Skipping image download, low memory (%zu KB)",
esp_get_free_heap_size() / 1024);
} else {
xTaskCreate(..., "mqtt_img", 4096 * 2, ...);
}
消息气泡持久化
移除了所有 SetChatMessage("system", "") 空内容调用,让 MQTT 消息在聊天区永久显示,直到:
- 消息数量超过 MAX_MESSAGES(默认 20 条)自动挤掉最旧的
- 新对话开始时清除
最终效果
| 终端 | 效果 |
|---|---|
| 小智AI | 屏幕弹出告警气泡 + 提示音 + 抓拍图片 |
| 飞书APP | 随时随地查看门铃动态 |
涉及的文件
| 文件 | 改动 |
|---|---|
main/CMakeLists.txt |
添加 mqtt_listener.cc |
main/application.cc |
MQTT 事件处理、图片下载显示 |
main/application.h |
MQTT 事件定义、队列、互斥锁 |
main/mqtt_listener.cc |
新增,独立 MQTT 客户端 |
main/mqtt_listener.h |
新增,MQTT 监听器接口 |
后续计划
- 图片点击放大全屏查看
- 支持更多图片格式(PNG/WebP)
- MQTT 告警消息数量统计
- 语音播报告警内容
- 飞书机器人交互式按钮(静音时段、确认已处理等)
项目:xiaozhi-esp32
作者:qiongmu.tech
更多推荐




所有评论(0)