
简介这份资源是一套基于STM32F103ZET6设计的智能门锁完整项目源码包面向嵌入式爱好者、毕业设计学生及物联网开发者覆盖蓝牙开锁、微信小程序远程开锁、按键模拟钥匙、RC522刷卡识别四种功能。主控采用STM32F103ZET6蓝牙模块HC05、WiFi模块ESP8266、刷卡模块RFID-RC522并通过步进电机正反转模拟锁体开合动作。压缩包共163个文件核心包括STM32固件C/H源码、Keil工程文件、生成的hex固件、Android蓝牙开锁APK、微信小程序相关配置、原理图与PDF设计文档等整体大小约39.74MB目录结构清晰可快速定位硬件设计、程序源码和使用说明。目前已有982人学习下载具备一定参考热度。资料包内含详细设计文档、接线说明、实物图以及配套工具软件按文档购买硬件、编译下载即可复现整个项目有助于系统掌握外设驱动、蓝牙串口通信、ESP8266接入服务器及小程序远程控制等关键技能。1. 基于STM32的智能门锁微信小程序只是多了一路遥控基于STM32的智能门锁再加一个微信小程序远程开锁很多人第一反应是“这不就是蓝牙串口换个壳吗”。实际把HC05蓝牙、ESP8266 WiFi、RFID-RC522刷卡和按键都接到STM32F103ZET6之后会发现真正的难点不是某一项驱动而是四路开锁指令怎么去抢同一个门锁状态机。这份2023升级版完整源码包正好覆盖了这条链路Android蓝牙APP、微信小程序、RC522刷卡驱动、MQTT透传、步进电机模拟门锁全部齐了。适合嵌入式毕设、物联网课设或者想快速搭一套智能门锁原型的工程师。资料里已经带编译好的axf和APK拿到手主要精力可以放在接线和理解状态机而不是从零调SPI和串口。2. 硬件选型与接线STM32F103ZET6 如何同时带四路开锁外设2.1 主控与外设的选型理由STM32F103ZET6 在这个场景属于“性能冗余但接口刚好”的选择。它提供144引脚、5个USART、3个SPI和512KB Flash。当前方案里HC05占一个串口ESP8266占一个串口调试串口再占一个RC522走SPI按键用外部中断步进电机占四五个GPIO引脚数量和DMA通道都刚好够用。如果换成F103C8T6串口和Flash会比较紧张而且LQFP48封装手工焊接难度并不低。用ZET6的好处是后续想加指纹模块、OLED菜单、舵机锁体都有富余的定时器和USART。更重要的是网上大量STM32智能门锁源码都基于这个型号遇到问题能找到的参考更多。这里说的“选型理由”不是堆参数而是从源码包里的主控、蓝牙、WiFi、刷卡模块四条线往回推每一个外设都有明确的接口抢占点。2.2 外设接口与供电规划拿到资料包先不要上电先看设计文档里的接线说明。下面这个表格是常见接法也是典型STM32F103ZET6的默认外设映射具体以设计文档为准。外设通信方式STM32引脚供电要求注意事项HC05蓝牙USART2TX2-PA2RX2-PA33.3V电平兼容波特率需与代码一致ESP8266 WiFiUSART1TX1-PA9RX1-PA103.3V峰值电流300mA独立稳压勿与RC522共用LDORFID-RC522SPI1SCLK-PA5MISO-PA6MOSI-PA7CS-PA4RST-PB13.3VSPI时钟建议2MHz左右步进电机GPIOPB0-PB35V/12V独立电源必须用ULN2003驱动板按键EXTIPA0-PA23.3V上拉按下接地供电是最容易出问题的一环。ESP8266在发射WiFi信号时瞬时电流可能到300mA如果和RC522共用一个AMS1117-3.3刷卡瞬间WiFi刚好在重连服务器就会出现RC522读卡失败。我一般会把ESP8266的VCC单独从5V输入串一个低压差LDO并加大电容到470uF。步进电机也不要从STM32板载5V取电电机反转时会拉低电源导致HC05掉线。正确做法是ULN2003的VM接外部5V适配器GND和STM32共地。2.3 初始化代码骨架先把GPIO和串口跑通不要急着看门锁逻辑先核对工程里的GPIO初始化。下面这段是源码包main.c里常见的初始化片段void GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); // 步进电机 PB0-PB3推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // 按键 PA0-PA1上拉输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOA, GPIO_InitStructure); // RC522 RST引脚推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(GPIOB, GPIO_InitStructure); }这段代码有三个关键点。第一电机引脚必须用推挽输出不能开漏否则ULN2003输入高电平驱动不足。第二按键要配置成上拉输入因为按键一端接地按下时引脚被拉低上拉输入避免悬空误触。第三RC522的RST引脚如果复用为SPI片选之外的普通GPIO初始化必须在SPI初始化之前完成否则模块可能复位失败。2.4 串口分配与中断优先级源码包里的串口分配是有讲究的USART1给ESP8266USART2给HC05USART3给调试打印。这样分配的原因是ESP8266的AT指令返回长字符串会占用大量串口中断时间而HC05蓝牙开锁要求响应速度快。把USART1的NVIC优先级调低USART2设成最高可以避免WiFi连接的“忙等”拖慢蓝牙开锁。RC522通常使用SPI1片选CS接PA4RST接PB1。这里要注意SPI的默认极性和相位需要匹配RC522的MFRC522数据手册标准配置是SPI_Mode0即CPOL0、CPHA0。如果读卡返回超时先检查这两个参数。注意HC05和ESP8266的串口电平都是3.3VSTM32的TX直接连即可。但如果HC05是5V版本需要加电阻分压否则长时间工作会拉高PA3输入电压。3. 源码包文件拆解main.c、RFID_RC522.c、mqtt.c 的协同逻辑3.1 解压后这些文件各是什么打开压缩包会看到一长串文件很多是Keil工程自动生成的不用逐行读。文件/名称作用需要改吗STM32HD.uvguix.11266Keil MDK窗口布局文件不需要STM32HD.axf编译后的调试文件可加载到Keil或ST-Link Utility不需要system_stm32f10x.c时钟初始化设置72MHz主频不需要core_cm3.cCortex-M3内核支持文件不需要main.c主循环、状态机、按键扫描、命令解析需要RFID_RC522.cRC522底层驱动与寻卡实现需要看mqtt.cESP8266 WiFi连接与MQTT消息收发需要改VCL60.BPL / RTL60.BPLDelphi运行时库可能是上位机调试工具依赖不需要蓝牙开锁APP_1.apkAndroid蓝牙开锁APP直接安装其中VCL60.BPL和RTL60.BPL很容易被误认为病毒或工程垃圾其实它们是Borland Delphi的运行时库。有的上位机调试工具用Delphi写成运行时需要这两个文件和STM32固件没有关系。3.2 main.c 主循环状态机防止四路指令“抢锁”门锁最忌讳的是“蓝牙正在开锁微信小程序又发来开锁然后电机同时正转反转”。源码包的做法是把门锁状态收敛到一个状态机所有开锁指令统一走LockOpen()入口。// main.c 状态机要点 enum LOCK_STATE { LOCK_CLOSED 0, LOCK_OPENING, // 电机正转中 LOCK_OPEN, // 门锁已打开 LOCK_CLOSING, // 电机反转中 }; uint8_t LockOpen(uint8_t source) { if (state ! LOCK_CLOSED) { return 0; // 非闭锁状态忽略重复开锁 } state LOCK_OPENING; printf(OPEN source%d\r\n, source); MotorRun(180); // 电机正转半圈模拟开锁 state LOCK_OPEN; StartCloseTimer(5000); // 5秒后自动上锁 return 1; }这个函数里最重要的参数是source它标识本次开锁来自哪里1是蓝牙2是WiFi小程序3是刷卡4是按键。在串口调试助手上看到OPEN source1就能确定蓝牙路径已经跑通。StartCloseTimer(5000)是软件定时器不要改成HAL_Delay因为RC522寻卡和串口中断都需要主循环持续运行。3.3 RC522 刷卡驱动寻卡、防碰撞、白名单判断RC522驱动的核心是PCD_Request和PCD_Anticoll。PCD_Request向射频场发出寻卡请求PCD_Anticoll从多个卡片中分离出4字节序列号。源码包里的RFID_RC522.c已经封装好使用时只需要匹配白名单。// RFID_RC522.c 刷卡识别片段 uint8_t RC522_CheckCard(void) { uint8_t status; uint8_t snr[4]; status PCD_Request(PICC_REQIDL, g_atqa); // 寻卡 if (status ! MI_OK) return status; status PCD_Anticoll(snr); // 防碰撞取卡号 if (status ! MI_OK) return status; if (memcmp(snr, white_list[0], 4) 0) { // 与白名单比较 LockOpen(3); // source3 表示刷卡 } PCD_StopCrypto1(); // 结束加密释放射频场 return MI_OK; }代码里g_atqa是卡片对Request命令的应答数据可以忽略。white_list[0]是预存的4字节卡号这里只放了一张卡。如果需要多卡管理就要把white_list改成二维数组并用循环遍历。注意PCD_StopCrypto1()必须每次流程结束后调用否则下一张卡可能无法正常进入防碰撞流程。3.4 mqtt.cESP8266 如何接入微信小程序的远程指令mqtt.c的本质是把ESP8266的AT指令封装成几个函数连WiFi、连Broker、订阅Topic、接收消息。微信小程序下发OPEN后Broker转发给ESP8266STM32从串口读取并触发LockOpen(2)。// mqtt.c 中连接Broker的典型AT序列 void MQTT_Connect(const char *ssid, const char *pwd, const char *broker) { char buf[160]; ESP8266_SendCmd(ATRST\r\n, ready, 3000); ESP8266_SendCmd(ATCWMODE1\r\n, OK, 1000); sprintf(buf, ATCWJAP\%s\,\%s\\r\n, ssid, pwd); ESP8266_SendCmd(buf, WIFI GOT IP, 10000); sprintf(buf, ATMQTTCONN\%s\,1883,1,0,\dev_lock_01\\r\n, broker); ESP8266_SendCmd(buf, OK, 5000); }这里的ATMQTTCONN依赖于ESP8266固件支持MQTT扩展AT指令。如果固件版本老会返回ERROR这时要么换固件要么改用ATCIPSTART走TCP透传在STM32端手动拼MQTT报文。源码包里既然用了mqtt.c说明作者的固件已经支持这个指令拿到后不要随便刷机否则还要改一整套发送逻辑。3.5 调试日志给四路开锁挂上“追踪ID”在调试蓝牙、WiFi、刷卡、按键四路输入时我习惯在串口打印里加上来源编号。修改printf的格式化串把开锁来源变成可读字符串。void PrintOpenSource(uint8_t source) { const char *src_str[] {UNKNOWN, BLUETOOTH, MQTT, RFID, KEY}; printf(OPEN by %s\r\n, src_str[source]); }这样在上位机上能看到是BLE还是MQTT避免怀疑到错误的驱动上。很多单片机工程只打印OPEN一旦按键和蓝牙同时触发根本分不清谁先到。加一个来源枚举排错效率能提高一半。4. 微信小程序远程开锁WSS、MQTT Topic 与指令格式4.1 远程链路设计小程序为什么不能直连ESP8266微信小程序运行在微信客户端里只能访问HTTPS和WSS协议不能直接连接局域网IP更不可能直连ESP8266的TCP端口。因此需要一条中转链路小程序通过WebSocket连接公网MQTT BrokerESP8266通过MQTT协议连接同一个Broker两者通过Topic交换消息。微信小程序 - wss://broker.example.com:8084/mqtt - Topic: lock/device001/cmd ESP8266 - mqtt://broker.example.com:1883 - Topic: lock/device001/statecmd主题用于下发指令state主题用于设备上报状态。ESP8266只要能主动外连网络就能在公网环境下被小程序控制不需要公网IP也不需要路由器端口映射。4.2 小程序开锁代码发布OPEN指令源码包里的小程序工程已经封装好了MQTT客户端核心代码在pages/index/index.js中。// pages/index/index.js 开锁事件 const mqtt require(../../utils/mqtt.min.js); Page({ openLock() { const clientId wx_lock_ Math.random().toString(16).substr(2, 8); const client mqtt.connect(wss://broker.example.com:8084/mqtt, { clientId, timeout: 10, keepalive: 60, clean: true }); client.on(connect, () { client.publish(lock/device001/cmd, OPEN, { qos: 1, retain: false }); client.end(); }); client.on(error, () { wx.showToast({ title: 连接失败, icon: none }); }); } })这里有两个必须注意的点。第一clientId必须唯一如果两台手机使用同一个ClientID后连接的小程序会把前一个踢下线。我一般在ClientID后追加随机数。第二qos: 1意味着Broker会重发消息如果ESP8266侧没有去重远程开锁可能触发两次。源码包里默认用简单字符串你应该自行在STM32端记录最近一次消息ID重复则丢弃。4.3 MQTT参数与Topic约定参数建议值说明Broker地址云服务器或公共MQTT Broker商用必须自建公共Broker不稳定WSS端口443或8084小程序要求wss协议QoS1门锁指令建议至少QoS1cmd Topiclock/device001/cmd小程序发布设备订阅state Topiclock/device001/state设备发布小程序订阅指令内容OPEN / QUERY纯字符串方便串口查看公共MQTT Broker如broker.emqx.io适合测试但生产环境不要用。公共Broker没有SLA高峰期可能断开而且消息容易被其他topic订阅者看到。换成自建EMQX后只需改mqtt.connect里的URL其他逻辑不用动。4.4 蓝牙开锁APP与HC05配对Android蓝牙APP直接安装蓝牙开锁APP_1.apk打开后搜索HC05配对默认PIN码一般是1234或0000。配对后APP会以串口透传方式向HC05发送字符串OPEN。HC05出厂波特率常见9600但源码里的USART2不一定设置成9600可能用的是115200。如果蓝牙连上但门锁不动先用USB转TTL接HC05执行AT命令确认参数。# 用USB转TTL连接HC05进入AT模式 ATORGL ATROLE0 ATUART9600,0,0 ATRESETATROLE0表示从模式等待手机连接ATUART9600,0,0分别指波特率9600、停止位1、校验位无。设置后要把STM32端USART2初始化修改成相同波特率否则串口收到的全是乱码。注意回复OK后需要断电重启才生效。4.5 用开发者工具快速替换Broker地址拿到源码包后用微信开发者工具导入小程序目录。打开app.js文件找到全局配置// app.js 全局配置 globalData: { brokerUrl: wss://broker.example.com:8084/mqtt, deviceId: lock/device001 }把brokerUrl改成自己的Broker地址。如果还没有服务器测试阶段可以在开发者工具右上角点击“详情”勾选“不校验合法域名”同时把brokerUrl改成ws://这样能在电脑上联调。但真机预览时微信强制要求合法域名和wss协议这个跳过方式只在开发者工具里有效。5. 编译烧录与故障排查芯片包、AXF下载和门锁状态上报5.1 Keil5 安装STM32芯片包原始工程使用Keil MDK开发打开.uvprojx之前要先确认软件包里有STM32F1系列支持。点击Pack Installer搜索STM32F1安装Keil.STM32F1xx_DFP.2.x.x。如果网络安装失败到Keil官网下载离线pack双击安装路径不要带中文。工程编译前还要确认两个配置。第一Device选择STM32F103ZET6第二C/C选项卡Define里必须有STM32F10X_HD,USE_STDPERIPH_DRIVER。HD代表高密度Flash如果漏掉外设库很多函数不会被条件编译进来编译会报未定义。5.2 烧录与AXF调试使用ST-Link连接STM32的SWDIO、SWCLK、GND和3.3V。在Keil里选择Flash Download如果已经编译成功可以直接下载。源码包里的STM32HD.axf也可以直接用ST-Link Utility加载适合不重新编译只想快速看效果的情况。下载后按复位键串口调试助手接USART3波特率取代码中设置的数值正常会看到启动日志。5.3 高频故障排查表现象可能原因处理方式HC05连接后数据乱码STM32波特率与HC05不匹配用AT设置相同波特率微信小程序提示连接失败使用了ws://或域名未备案开发者工具勾选不校验合法域名RC522偶尔读不到卡SPI时钟过高或天线规格不匹配将SPI时钟降到2MHz卡片放中间步进电机不转供电电压不足或相位顺序错独立电源核对PB0-PB3顺序ESP8266返回ERRORAT固件不支持MQTT指令换固件或改用TCP透传其中RC522读不到卡最容易被忽略的是天线线圈。RC522的PCB天线对匹配电容很敏感不同厂家的模块需要微调发射部分的电阻电容。如果刷卡距离小于1cm不要先怀疑程序应该检查模块下方的铺铜是否影响磁场。5.4 技巧把门锁状态上报到微信小程序源码包里的MQTT链路更多是“下发指令”未必附带状态回传。我拿到后第一件事就是在mqtt.c里增加一个状态发布函数让开锁完成后设备主动上报。// mqtt.c 状态上报 void MQTT_PublishState(uint8_t source) { char msg[16]; sprintf(msg, OPEN_OK:%d, source); ESP8266_SendData(lock/device001/state, msg); }在LockOpen()的最后调用MQTT_PublishState(source)小程序端订阅lock/device001/state后就能在收到OPEN_OK:1时把页面图标变成蓝色。这样远程开锁有了明确反馈不会因为网络延迟或丢包用户连点三次按钮造成重复操作。实际调试时小程序收到OPEN_OK后要关闭连接否则长时间保持WebSocket连接会消耗手机电量而ESP8266侧也最好只在状态变化时发送不要每秒上报。本文还有配套的精品资源点击获取