STM32+电磁锁+MQTT+IPC联调:一套无人售货柜的完整开门闭环
STM32+电磁锁+MQTT+IPC联调:一套无人售货柜的完整开门闭环
前面五篇把 IPC 摄像头的 RTSP 拉流、SD 卡录像、GB28181、流媒体服务都打通了。现在来到终章:把 STM32 单片机(控制电磁锁+门磁检测)、MQTT 通信(设备与云端双向指令)、IPC 摄像头(拍照识别商品)全部串联起来,跑通一个「用户扫码 → 门锁打开 → 拿商品关门 → IPC 拍照 → 云端扣款」的完整闭环。
大家好,我是黒漂技术佬。
之前在 ai-视觉零售系列里,我搭过一台无人售货柜原型机(RK3588 做主控、USB 摄像头拍照)。后来有一次调试,RK3588 主板重启导致 USB 摄像头掉线,柜子直接成了「敞开的冰箱」——用户拿了商品也没法扣款。这让我决定加一层独立的安全兜底:让 STM32 直接控制门锁和门磁,通过 MQTT 和云端保持独立通信,即使主控板挂掉或重启,门锁控制不受影响。
这一篇,我就把这个方案的硬件接线、STM32 代码逻辑、MQTT 上下行指令格式、以及和 IPC 的配合全部分享出来。
一、为什么要加一块 STM32?
先看架构对比:
老方案(纯 RK3588 控制):
RK3588 --GPIO--> 继电器 --> 电磁锁
RK3588 --GPIO--> 门磁传感器
RK3588 --USB--> 摄像头
RK3588 --MQTT--> 云端(自己跑 MQTT 客户端)
问题:RK3588 是 Linux 系统,系统启动慢(30 秒+)、偶尔卡死需重启。门锁控制依赖主板的稳定性,单点风险高。
新方案(STM32 独立门控):
+---------------------------------------------------------------+
| 云端(SpringBoot + MQTT Broker) |
+---------------------------------------------------------------+
| MQTT | MQTT
v v
+-------------------+ +-------------------+
| STM32F103C8T6 | | RK3588 |
| (门锁控制版) | | (主控 / AI推理) |
| | | |
| GPIO --> 继电器 | | RTSP --> IPC 摄像头 |
| GPIO --> 门磁 | | YOLO 商品识别 |
| 串口 --> ESP8266 | | MQTT 业务上报 |
+-------------------+ +-------------------+
好处:
- 门锁独立控制,RK3588 挂了也不影响关门和开门。
- STM32 上电 0.5 秒就进正常工作状态,不像 Linux 要等半分钟。
- 双 MQTT 通道:STM32 走门控指令,RK3588 走识别和交易指令,责任清晰。
二、硬件接线清单
| 部件 | 型号 | 数量 | 连到 |
|---|---|---|---|
| 单片机 | STM32F103C8T6 最小系统板 | 1 | - |
| WiFi 模块 | ESP8266-01S | 1 | STM32 USART1(PA9/PA10) |
| 继电器模块 | 5V 低电平触发 | 1 | STM32 PA0 |
| 电磁锁 | 12V 小型 | 1 | 继电器常开端 + 12V 电源 |
| 门磁传感器 | MC-38 有线 | 1 | STM32 PA1(上拉输入) |
| 电源 | 12V 3A 开关电源 | 1 | 电磁锁 + 5V 降压给 STM32 |
电路示意图:
12V电源 ──┬──→ 电磁锁 ──→ 继电器常开 ──→ GND
│
└──→ 5V降压模块 ──→ STM32 VCC / ESP8266 VCC
STM32 PA0 ──→ 继电器 IN(低电平触发,锁开)
STM32 PA1 ──→ 门磁(上拉输入,门关=高电平,门开=低电平)
STM32 PA9 ──→ ESP8266 RX
STM32 PA10 ──→ ESP8266 TX
⚠️ 避坑:ESP8266 是 3.3V 电平,STM32F103 的 GPIO 也是 3.3V,可以直接连。但电磁锁是 12V 感性负载,继电器断开瞬间会产生反向电动势,一定要在电磁锁两端反向并联一个续流二极管(1N4007),不然继电器触点打火、STM32 可能被干扰复位。
三、STM32 端代码核心逻辑
基于 HAL 库,主循环逻辑:
// 全局状态
uint8_t door_status = 0; // 0=关, 1=开
uint8_t lock_cmd = 0; // 0=无指令, 1=开锁, 2=关锁
uint8_t mqtt_connected = 0;
// MQTT 回调:收到云端指令
void mqtt_callback(char* topic, char* payload) {
if (strcmp(topic, "cabinet/001/cmd") == 0) {
if (strcmp(payload, "open") == 0) {
lock_cmd = 1; // 执行开锁
}
}
}
// 主循环
while(1) {
// 1. 维持 MQTT 连接 + 心跳
mqtt_loop();
// 2. 读取门磁状态(PA1)
door_status = (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET) ? 0 : 1;
// 3. 执行开关锁
if (lock_cmd == 1) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 低电平触发继电器,锁开
HAL_Delay(500); // 保持 500ms
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // 释放
lock_cmd = 0;
// 上报云端:锁已开
mqtt_publish("cabinet/001/status", "{\"lock\":\"open\"}");
}
// 4. 检测门状态变化,上报
static uint8_t last_door = 0;
if (door_status != last_door) {
last_door = door_status;
if (door_status == 1) {
mqtt_publish("cabinet/001/status", "{\"door\":\"open\"}");
} else {
mqtt_publish("cabinet/001/status", "{\"door\":\"close\"}");
}
}
HAL_Delay(100);
}
这段代码的精简版即可完成门控闭环。ESP8266 通过 AT 固件或直接烧 MQTT 透传固件连接 WiFi,给 STM32 提供 MQTT 通道。
四、MQTT 上下行指令设计
Topic 规划
| Topic | 方向 | 作用 |
|---|---|---|
cabinet/001/status |
STM32 → 云端 | 上报门锁和门磁状态 |
cabinet/001/cmd |
云端 → STM32 | 下发开锁 / 关锁指令 |
cabinet/001/photo |
云端 → RK3588 | 触发摄像头拍照识别 |
指令格式(JSON)
门状态上报(STM32 → 云端):
{"door":"open","lock":"closed","ts":1719650400}
开门指令(云端 → STM32):
{"cmd":"open","order_id":"20260630-00123"}
⚠️ 带上
order_id很关键。后续交易对账时,可以把开门记录、IPC 拍照的图片、商品识别结果、扣款金额都串在同一个订单号下面。
五、完整业务流程(一次用户购物)
1. 用户扫小程序码
└→ 小程序请求后端 → 后端生成订单号
2. 后端下发开门指令(通过 MQTT)
└→ MQTT Topic: cabinet/001/cmd
└→ Payload: {"cmd":"open","order_id":"O20260630-00123"}
3. STM32 收到指令 → 继电器吸合 500ms → 电磁锁释放
└→ 上报: {"lock":"open","order_id":"O20260630-00123"}
4. 用户拉开门 → 门磁状态变化
└→ STM32 检测到门开 → 上报: {"door":"open"}
5. 用户拿商品、关门
└→ STM32 检测到门关 → 上报: {"door":"close"}
6. 云端收到关门事件 → 发指令给 RK3588 拍照
└→ MQTT Topic: cabinet/001/photo
└→ RK3588 收到 → 从 IPC RTSP 流中截一帧 → 跑 YOLO 识别
7. 对比开门前/关门后的商品变化 → 算出拿了什么 → 扣款
整个链路里,IPC 摄像头在第 6 步出场——它不是一直拍,而是被事件触发后才抓帧识别。这让 IPC 的 RTSP 流从「时刻推着」变成了「按需取帧」,大幅降低了无意义的带宽和计算消耗。
六、实测踩过的坑
坑 1:继电器干扰 ESP8266
电磁锁动作瞬间,ESP8266 经常断连 WiFi。排查发现继电器的触点火花产生了电磁干扰。解决方案:电磁锁并 1N4007 续流二极管 + 继电器线圈并 1N4148 保护二极管 + 继电器和 ESP8266 模块之间加物理距离(至少 5cm)。
坑 2:门磁抖动
关门瞬间门磁会抖动几十毫秒,产生多次高低电平跳变。STM32 100ms 轮询周期下会被误判为「又开了一次门」。解决方案:加 200ms 消抖窗口——电平变化后 200ms 内不再响应。
if (door_status != last_door) {
HAL_Delay(200); // 等抖动过去
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == (door_status ? GPIO_PIN_RESET : GPIO_PIN_SET)) {
// 确认状态已稳定,上报
}
}
坑 3:MQTT 断线后指令丢失
STM32 WiFi 掉线时,云端发的开门指令会丢失。解决方案:MQTT 用 QoS 1(至少一次送达),STM32 重连后自动订阅 cabinet/001/cmd,服务端在指令未被消费时做重试。
本篇简结
- STM32 独立控锁比 RK3588 直接控锁更可靠(上电快、不受主控死机影响)。
- ESP8266 透传 MQTT,STM32 负责 GPIO 控制和状态上报。接线记得加续流二极管。
- MQTT 上下行指令统一用 JSON,带上 order_id 串起整条交易链路。
- IPC 摄像头按事件触发抓帧,不必一直推流,省带宽省算力。
- 门磁消抖、继电器抗干扰、MQTT QoS 1——这三处细节决定柜子稳定性。
这大概是我在无人售货柜项目里做门控 + IPC 联调最完整的记录。从 IPC 视频流到 MQTT 指令通道,从 STM32 硬件控制到云端扣款逻辑——把这些模块一个个拆开验证再合拢,项目就跑起来了。
我是黒漂技术佬。
更多推荐


所有评论(0)