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 硬件控制到云端扣款逻辑——把这些模块一个个拆开验证再合拢,项目就跑起来了。

我是黒漂技术佬。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐