多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

ESP32-C3蓝牙开锁实战:从硬件选型到固件开发全流程解析

ESP32-C3蓝牙开锁实战:从硬件选型到固件开发全流程解析 开门见山说结论用ESP32-C3做蓝牙开锁本质就是把“开锁”这个动作封装成一个蓝牙服务里的开关量手机靠近后通过BLE协议写一个指令主控判断通过就驱动继电器或锁体动作。这个方案比传统射频遥控器安全、比Wi-Fi开锁省电、比NFC开锁灵活而且ESP32-C3这颗芯片自带蓝牙5.0一颗芯片就能搞定主控和通信成本压到十块钱级别。这篇文章我就把从硬件选型到固件编写、从手机调试到问题排查的完整路径捋一遍适合正在做智能门锁、储物柜、快递柜、甚至电动车锁这类项目的朋友参考。我最早入坑蓝牙开锁是因为帮朋友做一批共享储物柜。当时对比过好几条技术路线433M射频方案便宜但每个柜子要配对钥匙Wi-Fi方案又要等路由器、功耗也扛不住电池供电最后选了蓝牙BLE。原因很简单手机原生支持、不需要额外配网、连接速度在100毫秒级别、广播态功耗能压到微安级。而ESP32-C3这颗芯片RISC-V架构自带蓝牙5.0和Wi-Fi价格比当年的ESP8266还便宜做量产设备非常合适。1. 需求拆解与方案选型为什么是ESP32-C31.1 蓝牙开锁场景的核心需求在做任何开发之前得先把需求拆清楚。蓝牙开锁看着简单但实际使用场景里至少有四个硬指标绕不开权限验证不是任何人拿手机靠近就能开锁必须有身份校验机制。响应速度人站在门前手机掏出来点一下到锁弹开整个过程超过3秒体验就很糟糕了。可靠性锁不能误开更不能该开的时候开不了尤其在断电、干扰、多设备同时连接的情况下要稳定。功耗控制如果是电池供电主控的待机电流、广播间隔、连接后的处理策略都直接影响续航。这四个需求直接决定了芯片选型和方案架构。ESP32-C3在低成本蓝牙方案里几乎是综合分最高的选择它不像HC-05那种经典蓝牙模块只是透传从机需要额外接一颗MCU做逻辑处理C3本身就是一颗完整的MCUADC、GPIO、定时器、SPI、I2C全集成这意味着“蓝牙通信”和“开锁逻辑”可以在同一颗芯片上完成省掉一颗主控的钱和PCB面积。1.2 芯片对比经典蓝牙模块与BLE SoC的本质区别很多刚接触蓝牙开发的读者第一个接触的是HC-05/HC-06这类经典蓝牙模块搜索热词里也经常出现“HC-05蓝牙模块连接不上”“HC-06 AT无响应”。这里我多说几句帮大家把概念理顺。HC-05走的是经典蓝牙BR/EDR协议栈在模块内部跑对外暴露串口模块本身没有编程能力必须外接单片机才能干活。它的优点是开发简单、对手机兼容性好缺点是功耗高、配对流程繁琐、模块本身没有计算能力。而且这类模块的AT指令配置是出了名的容易踩坑波特率不匹配、进入AT模式时序不对、模块被之前的历史配置锁死各种莫名其妙的问题都能遇到。ESP32-C3走的是低功耗蓝牙BLE它跟经典蓝牙是两套完全不同的协议体系。BLE把通信模型抽象成GATTGeneric Attribute Profile数据以Service和Characteristic的形式组织手机App通过读写这些特征值来和设备交互。这种模型天然适合“指令-响应”类的控制场景比如开锁、开关灯、读取传感器数据。而且C3芯片内部直接运行你的固件你可以把校验、超时、日志全写进去灵活度高一个量级。另外提一个很多人忽略的点ESP32-C3还支持Type-C直连烧录开发调试比那些还要单独买USB转TTL模块的方案舒服太多。板和板之间复制固件也方便量产烧录用esptool批量刷就行。1.3 开锁执行机构的选型思路蓝牙通信只是一半另一半是“锁”本身。不同的锁体对应不同的驱动方式选型错误会导致后面从头返工。电磁锁断电开锁型靠电流产生磁力吸合锁舌常用于门禁。驱动电流一般在几百毫安到1安培需要12V供电用继电器控制通断即可。电插锁通电开锁型平时锁舌靠弹簧弹出通电后电磁铁吸回锁舌。酒店门、玻璃门常见也是12V供电驱动逻辑和电磁锁相反。电机锁/马达锁通过电机正反转驱动锁舌需要H桥电路或专用驱动芯片逻辑相对复杂但适合那种“一定要有机械动作感”的场景。普通电子锁舌/挡板如果你只是做一个快递柜或者储物柜很多现成的电控锁模块本身带驱动电路只需要一个高/低电平触发信号这种最省事。我建议第一次做蓝牙开锁项目的朋友优先选“电平触发型”的电控锁模块GPIO直接控制一颗三极管或者MOS管就够了不需要接触继电器驱动电路的大电流开关设计和续流保护等跑通整个链路之后再升级到大电流锁体。2. 硬件准备与电路设计2.1 完整物料清单与工具以我的储物柜项目为例完整物料清单如下组件型号/规格数量说明主控ESP32-C3开发板或ESP32-C3-MINI-1模组1推荐带USB口的核心板方便调试继电器模块低电平触发单路继电器模块1隔离控制12V锁体电控锁12V电插锁或电磁锁1按实际门体选择电源12V/2A适配器锁体供电 AMS1117-3.3模块给C3供电11锁体和主控分开供电避免电磁干扰导致重启按键轻触开关1用于本地手动开锁/重置LED指示灯红绿双色LED1指示蓝牙连接状态和开锁状态其他杜邦线、面包板、万用表、电烙铁-调试必备两个避坑提醒第一继电器模块尽量选带光耦隔离的低电平触发型的因为ESP32的GPIO高电平驱动能力有限用低电平触发比较稳第二电源一定要分开如果你的锁体驱动电流大和主控共用电源会导致电压跌落大概率触发C3的欠压复位表现就是“手机一开锁开发板就重启”。2.2 接线方式与关键GPIO设计接线这块我会画一个标准参考但先强调一个原则GPIO不要乱选。ESP32-C3虽然引脚不多但有些引脚是Strapping引脚上电瞬间的电平状态会影响芯片启动模式比如GPIO2、GPIO8、GPIO9还有一些引脚是SPI Flash的专用引脚不能拿来当普通IO用。我习惯的引脚分配方案功能GPIO说明继电器控制GPIO4输出高电平驱动继电器导通开锁本地按键GPIO3内部上拉按下为低电平触发开锁状态LED绿GPIO10蓝牙已连接时点亮状态LED红GPIO7开锁动作时点亮2秒接线注意点继电器模块的IN引脚接GPIO4VCC接5V或者3.3V取决于继电器模块的工作电压1路继电器模块通常支持5V也有3.3V版本买的时候注意看参数GND必须和C3共地。如果不共地电平参考不一样继电器永远不动作。锁体的正极接12V电源正极负极接继电器模块的COM和NO端——锁体一端接COM一端接NO当继电器吸合时回路导通锁体得电动作。注意如果你的锁体是电感性的电磁锁、继电器都有线圈在继电器驱动锁体的回路里线圈两端一定要反向并联一个续流二极管1N4007级别否则断电瞬间的反向电动势会打坏继电器触点甚至耦合到控制端。2.3 供电设计与低功耗策略如果做的是插电设备供电不用太纠结12V适配器加一个稳压模块给C3供电就行。但如果是电池供电的便携锁——比如共享单车锁、临时门禁贴锁——那低功耗设计就是重头戏了。ESP32-C3在低功耗方面的核心手段是深度睡眠Deep Sleep。在深度睡眠模式下芯片会关闭大部分数字电路只保留RTC和少量唤醒源电流可以压到5微安级别实测大约4-7uA。关键是唤醒方式你可以用定时器唤醒也可以让GPIO外部触发唤醒——但BLE广播唤醒是不行的。那怎么让手机能把设备叫醒呢有两种思路定时唤醒广播每隔比如300ms唤醒一次广播几百毫秒再睡过去。手机在App里持续扫描扫到设备后马上发起连接设备在广播窗口内能响应当前连接请求连接成功后切到正常运行模式。这种方式的平均功耗取决于广播间隔实测用500ms间隔搭配100ms广播窗口平均电流大概在0.5-1mA级别。带外唤醒加一个独立BLE从机芯片比如TWS耳机仓方案里常用的那些专门做广播唤醒的芯片一直保持广播收到手机连接后拉高一个GPIO唤醒C3。这个方案更复杂我不建议第一次做就上先掌握定时广播方案就行。电池容量可以用一个简单公式粗估容量mAh ÷ 平均电流mA 理论工作时长小时再乘以0.7的转换效率系数。比如一颗1200mAh的锂电池平均电流1mA理论时间1200小时打七折差不多840小时也就是一个多月。如果要撑半年以上平均电流至少要压到0.3mA以下那广播策略就得进一步优化。3. 固件开发把“开锁”做成一个蓝牙服务3.1 开发环境选择ESP-IDF还是Arduino这是一个老生常谈但确实影响后续开发效率的问题。我的建议很简单如果你只是为了做产品原型、快速验证、甚至做一些内部工具直接用Arduino框架省心生态好踩坑资料多如果你要做量产固件、对内存和功耗控制要求极高、需要OTA升级和加密签名那必须上ESP-IDF。Arduino框架下开发ESP32-C3本质是把乐鑫的ESP-IDF封装成Arduino库所以底层还是IDF只是API简单了。对于蓝牙开锁这种逻辑不复杂的项目Arduino绰绰有余。我下面给的示例代码就是Arduino框架的直接拿Arduino IDE或者PlatformIO编译烧录都能跑。PlatformIO我更推荐一些因为它有项目结构管理、依赖管理、代码提示多文件项目不会像单文件IDE那样容易乱。3.2 BLE GATT服务结构设计在做BLE开发前你必须理解GATT的组织方式。简单类比一个GATT服务就像是一个衣柜服务Service是衣柜特征值Characteristic是抽屉特征值里存的才是真正有用的数据。手机和设备交互时就是去找某个服务下的某个特征值读它或者写它。我的开锁服务设计如下Service UUID: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E ├── Characteristic: 开锁指令UUID: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E │ └── 属性Write手机写入指令 └── Characteristic: 状态反馈UUID: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E └── 属性Notify设备主动推送状态开锁指令的特征值属性设置为Write手机往这个特征值写入一串字节C3收到后校验并执行开锁。状态反馈特征值设置为Notify设备开锁成功或失败后主动推个状态消息给手机App可以弹窗提示用户“门已打开”或者“开锁失败”。UUID的选择上面的UUID是Nordic半导体在开发套件里大量使用的经典自定义UUID。你当然可以用自己的UUID但要注意UUID必须是128位的不能随便写。如果只是做原型可以直接用现成的这个省得自己生成。3.3 核心代码实现BLE服务初始化与开锁逻辑下面的代码是精简版但可以直接编译通过的实现包含BLE服务初始化、接收手机写入、开锁动作三部分。#include BLEDevice.h #include BLEServer.h #include BLEUtils.h #include BLE2902.h #define SERVICE_UUID 6E400001-B5A3-F393-E0A9-E50E24DCCA9E #define LOCK_CHAR_UUID 6E400002-B5A3-F393-E0A9-E50E24DCCA9E #define STATUS_CHAR_UUID 6E400003-B5A3-F393-E0A9-E50E24DCCA9E #define RELAY_PIN 4 #define BUTTON_PIN 3 #define LED_GREEN_PIN 10 #define LED_RED_PIN 7 BLECharacteristic *pLockCharacteristic; BLECharacteristic *pStatusCharacteristic; bool deviceConnected false; // 开锁指令0xA5 0x01 0x5A // 帧格式帧头(0xA5) 命令码(0x01开锁) 校验(取反) // 校验字节 ~(0xA5 ^ 0x01)这样设计可以防止误触发 #define FRAME_HEADER 0xA5 #define CMD_UNLOCK 0x01 #define CMD_CHECK (uint8_t)(~(FRAME_HEADER ^ CMD_UNLOCK)) class MyServerCallbacks : public BLEServerCallbacks { void onConnect(BLEServer *server) { deviceConnected true; digitalWrite(LED_GREEN_PIN, HIGH); } void onDisconnect(BLEServer *server) { deviceConnected false; digitalWrite(LED_GREEN_PIN, LOW); // 断开后立即重新开始广播方便手机再次连接 BLEDevice::startAdvertising(); } }; class MyCallbacks : public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { std::string value pCharacteristic-getValue(); if (value.length() ! 3) return; uint8_t header value[0]; uint8_t cmd value[1]; uint8_t check value[2]; if (header ! FRAME_HEADER) return; if (check ! (uint8_t)(~(header ^ cmd))) return; if (cmd ! CMD_UNLOCK) return; unlockDoor(); pStatusCharacteristic-setValue(OK); pStatusCharacteristic-notify(); } }; void unlockDoor() { digitalWrite(RELAY_PIN, HIGH); // 继电器吸合锁体通电动作 digitalWrite(LED_RED_PIN, HIGH); delay(500); // 保持500ms锁体完成动作 digitalWrite(RELAY_PIN, LOW); // 断开继电器锁体复位 digitalWrite(LED_RED_PIN, LOW); } void setup() { pinMode(RELAY_PIN, OUTPUT); pinMode(BUTTON_PIN, INPUT_PULLUP); pinMode(LED_GREEN_PIN, OUTPUT); pinMode(LED_RED_PIN, OUTPUT); digitalWrite(RELAY_PIN, LOW); BLEDevice::init(ESP32-C3 SmartLock); BLEServer *pServer BLEDevice::createServer(); pServer-setCallbacks(new MyServerCallbacks()); BLEService *pService pServer-createService(SERVICE_UUID); pLockCharacteristic pService-createCharacteristic( LOCK_CHAR_UUID, BLECharacteristic::PROPERTY_WRITE ); pLockCharacteristic-setCallbacks(new MyCallbacks()); pStatusCharacteristic pService-createCharacteristic( STATUS_CHAR_UUID, BLECharacteristic::PROPERTY_NOTIFY ); pStatusCharacteristic-addDescriptor(new BLE2902()); pService-start(); BLEDevice::startAdvertising(); } void loop() { // 本地按键作为备用开锁方式 if (digitalRead(BUTTON_PIN) LOW) { delay(50); // 简单消抖 if (digitalRead(BUTTON_PIN) LOW) { unlockDoor(); while (digitalRead(BUTTON_PIN) LOW) { delay(10); } } } delay(10); }这里面有一个细节我要特别解释一下就是指令帧格式的设计。很多人做BLE写入控制的时候手机发一个单字节比如0x01就当作开锁指令初学者这么玩没问题但放在实际项目里这么做很危险。为什么因为BLE特征值写入是很容易被误触发的——手机App侧的逻辑Bug、扫描工具误操作、甚至协议栈的异常数据都可能导致写入一个随机的值。如果在协议层做了帧头命令码校验字节的三段式校验只有三个字节完全匹配才会执行开锁误触发的概率会低好几个数量级。3.4 键值设计背后的安全考量用了三段式帧结构能让“意外写入”不至于开门但是真正的安全攻击是另一回事。蓝牙广播数据是明文传输的任何人拿一个抓包工具看到你手机发出的三个字节A5 01 5A他就可以自编一个App扫到你的锁、连接、写入这段指令你的锁就开了。当年的很多廉价蓝牙锁就是这么被破解的网上甚至有人专门写文章演示破解过程。安全层面的推进方向大致有三个阶梯固定密码校验手机发来的指令里带一个预设的固定密钥字符串设备端校验通过才开锁。防君子不防小人抓包一次照样破解。动态Token设备端生成一个随机数手机使用动态Token和密钥共同计算设备再校验。这种方案安全性高很多但需要手机和设备之间有双向协商实现复杂度上升。加密连接BLE本身支持加密配对Just Works、Passkey、LESC但实现加密连接后手机首次配对需要交互用户体验变差而且如果开启了配对绑定换手机时还需要重新配对。我建议初期先做固定校验把整个流程跑通之后再加动态Token和加密配对。一步一步来不要一上来就啃安全这块容易卡住。4. App端与测试工具真正能“用起来”的客户端4.1 不用写代码的验证方式nRF Connect、微信小程序固件写完最急迫的就是要验证“手机能不能控制开锁”。如果直接上手写App很容易在还没验证硬件的时候就陷入一堆界面代码里。我更推荐先用现成的调试工具把链路打通再决定要不要写正式App。Android上用nRF ConnectiOS上也有LightBlue或者nRF Connect。连上设备后找到6E400002这个特征值点击Write输入A5:01:5A如果看到继电器动作、锁体弹开说明整个链路已经通了。这里有个小坑要提醒nRF Connect直接输入十六进制字符串时格式是带冒号的A5:01:5A不带冒号会识别成ASCII字符串。我见过不少新手卡在这步服务也找到了、特征值也找到了点击写入没反应回来后才发现是格式问题。微信小程序做蓝牙开锁也很成熟wx.openBluetoothAdapter、wx.startBluetoothDevicesDiscovery、wx.createBLEConnection这几个接口就能搞定大部分流程。网上有现成的IoT类小程序模板如果你只是做演示Demo小程序比折腾Android Studio快得多。但我自己的经验是小程序的蓝牙队列处理比较别扭如果你的App里有连续写多个特征值的需求要做好重试和错误处理。4.2 自研App的要点Android/iOS差异化如果你最终要交付一个正式产品App这关躲不掉。我简单梳理一下关键点权限申请Android 12以上需要BLUETOOTH_SCAN和BLUETOOTH_CONNECT运行时权限iOS需要NSBluetoothAlwaysUsageDescription。权限不申请接口直接返回失败。扫描与连接Android上扫描用BluetoothLeScanner连接用BluetoothGattiOS上用CBCentralManager扫描到设备后连接。两者逻辑一样但接口差异巨大跨平台开发建议直接上Flutter或uni-app而不是维护两套原生代码。通知接收如果你想收设备的Notify状态需要在Android上注册onCharacteristicChanged并且在连接后调用setCharacteristicNotification同时在特征值里添加CCCD描述符就是代码里的BLE2902。iOS只需要setNotifyValue:forCharacteristic:。这个CCCD的问题Android开发新手十有八九会漏掉漏掉的后果就是设备明明调了notify()手机端就是收不到。Flutter低功耗蓝牙这块有不少人在热搜词里问到“Flutter低功耗蓝牙iOS有问题嘛”我的经验是iOS上做BLE Flutter开发flutter_blue_plus库比老的flutter_blue稳定得多而且iOS的BLE是真后台运行受限App切后台后连接会挂收到状态更新需要靠CBPeripheralManager的后台模式支持这些坑都是平台特性导致的不是库的问题。4.3 调试利器BLE抓包与日志分析开发BLE应用很多玄学问题不抓包根本定位不了。比如设备明明发送了Notify手机却没收到或者数据偶尔丢一个字节。这些问题的答案都在协议栈里。硬件层面用nRF Sniffer配合Wireshark可以抓到空中的BLE报文能精确看到连接事件、数据包重传、白名单过滤等问题。软件层面Android上开启BluetoothHciSnoopLog在开发者选项里打开“启用蓝牙HCI信息收集日志”生成的btsnoop_hci.log可以用Wireshark打开分析。这个功能不用额外硬件强烈建议打开。固件层面在C3固件里用ESP_LOGD或者Serial.print把每次收到的写入指令打印出来能直接看到手机发过来的原始字节排查帧格式问题效率极高。我踩过最痛的一个坑就是固件里设置Notify并调用notify()手机却收不到。折腾了一晚上最后抓包发现设备侧其实已经在广播连接事件里发了通知包但手机没有发送CCCD订阅请求——换句话说手机端的订阅逻辑没生效。后来在App侧补了setCharacteristicNotification和CCCD写入问题立刻消失。5. 常见问题排查从连不上到开不了锁5.1 手机或电脑根本搜不到蓝牙设备搜不到设备是BLE开发里最常见的问题没有之一。排查顺序我已经整理了无数遍建议按这个顺序来设备有没有在广播用串口监视器看C3的日志确认有没有输出BLE初始化成功和广播开始的日志。如果日志里报错比如“createServer failed”大概率是内存不足或者BLE库初始化异常重启或优化代码。广播类型对不对有些代码把广播包和扫描响应包配置混在一起导致广播数据为空手机扫到了也显示不了名字。检查代码里有没有正确设置广播数据。手机蓝牙缓存Android手机在蓝牙开关关闭再打开、或者系统蓝牙服务被其他App占用时偶尔会出“幽灵缓存”旧设备信息残留。去系统设置里“已保存的设备”删除记录关闭手机蓝牙10秒再开。天线问题ESP32-C3模组的天线区域有走线覆盖或者金属遮挡信号会严重衰减开发板上模组不要贴地放置。再补一个我自己经常遇到的坑手机连Wi-Fi的2.4G频段和BLE都在2.4GHz频段不冲突但如果Wi-Fi信号极差有些手机系统会削弱蓝牙扫描能力——路由器换5G频段或关掉路由器的2.4G可以试一下。5.2 能连上但执行开锁无效能连上说明BLE链路是通的问题大概率出在应用层。重点查这几处UUID是否匹配手机连上的服务UUID和特征值UUID跟固件里定义的是否完全一致。大小写不敏感但多一个字符少一个字符都不行。特征值是否支持Write在调试工具里查看特征值的属性Properties如果只有Read和Notify而没有Write说明创建特征值时PROPERTY_WRITE没加。写入数据格式确认写入的是十六进制字节不是ASCII字符串。A5:01:5A这三个字节和字符串A5015A完全不是一回事。开锁条件是否满足如果你加了设备端校验逻辑比如状态机要求必须先通过某种握手写指令才会被接受。检查固件日志看onWrite回调的确切打印。5.3 开锁不稳定距离短经常断连这类问题通常是射频和电源的质量问题而不是代码问题。电源纹波太大继电器吸合瞬间电流突变导致C3的3.3V电压跌落超过100mV芯片可能重启或射频暂停。用示波器看3.3V波形如果吸合瞬间有跌落加一个大电容100uF以上或者换更好的稳压模块。天线周围有金属如果是金属外壳的锁体天线一定要引出或者用带外置天线的模组。PCB天线被金属包围信号强度可以衰减到原来的十分之一。连接参数不合适BLE连接后主从双方会协商连接间隔。如果连接间隔太长比如大于100ms数据交互会明显有“卡”的感觉。手机侧可以尝试设置请求更短连接间隔设备侧可以在onConnect里调用updateConnParams。5.4 与HC-05/HC-06经典蓝牙模块混用时的常见误区很多人在选择蓝牙模块时会把HC-05/HC-06和ESP32-C3放在一起比较搜索热词里也经常有“HC-05连接不上”“HC-06 AT无响应”的内容。这里我把关键区别总结一下对比项HC-05/HC-06ESP32-C3蓝牙版本经典蓝牙2.0/3.0BLE 5.0开发方式AT指令/串口透传外接MCU直接运行固件功耗毫安级不适合电池供电微安级睡眠适合电池供电手机兼容性需先配对连接较慢免配对扫描即连灵活性固定透传通道GATT服务可自定义典型应用老式模块化串口传输智能硬件、IoT设备如果你买的模块总是AT无响应首先查接线是否正确——很多HC-06模块的TX接单片机的RX、RX接单片机的TX交叉接是新手最容易弄错的。其次查波特率模块默认波特率可能是9600也可能是38400不同批次不一样。最后查电源模块的VCC要求3.3V或5V接错会直接烧模块。但说实话如果你在做新项目我不建议再用HC系列了ESP32-C3从成本、功耗、灵活性几个维度都是更好的选择。6. 可靠性设计与性能实测6.1 开锁响应时间与超时设计用蓝牙开锁从用户点击按钮到锁体动作我实测过的典型时延分布大概是手机扫描设备0.3-1秒取决于App是否提前缓存了设备、连接建立0.1-0.4秒、写入指令加设备处理10-50毫秒、继电器动作10-30毫秒。整体在0.5-1.5秒之间这是能接受的体验。要缩短时间从小处优化有几个点手机App里缓存上一次连接过的设备MAC下次直接发起连接跳过扫描环节能省掉大半时间。设备端广播间隔调短比如从100ms改成30ms手机会更快发现设备但功耗会上升。设备监听到连接后立即停止广播BLE会自动停止不需要手动处理减少射频开销指令响应更快。超时设计也很重要。我建议设备端设置一个“开锁命令有效窗口”比如手机写入指令后1秒内必须执行超过就丢弃。另外在loop里维护一个状态变量开锁过程中不允许重复触发防止用户狂点按钮导致继电器频繁通断。6.2 防重放、防误触与事件记录防重放攻击在联网锁领域是刚需。实现方案可以简单也可以复杂简单方案是加一个单调递增的计数器设备端记录一个序号手机每次开锁时带上这个序号加一的值设备端只接受“比上次序号大且差值为1”的指令。这种方法无需配对和加密代码量小能防住抓包重放但需要App端维护状态。复杂方案是挑战-响应认证设备广播一个随机数手机用动态密码算法算出验证码一起发送设备端再校验。这个方案适合安全要求比较高的场景但代码量成倍增加。日志功能强烈建议保留不管有没有显示屏。在固件里维护一个环形缓冲记录开锁时间、开锁方式蓝牙/按键、操作结果然后提供一条BLE指令读取这些日志排查问题的时候就方便了。我做的量产版本还会把最近100条事件存到NVS非易失存储里断电不丢失。6.3 低功耗实测与续航估算最后谈谈续航。用ESP32-C3做电池供电的锁核心功耗模型在广播和深度睡眠两个阶段。实测数据ESP32-C3开发板状态电流深度睡眠RTC唤醒约5uA广播100ms间隔平均1-2mA连接中30ms连接间隔平均8-15mA继电器吸合60-100mA锁体驱动电流按模块参数假设一台锁每天被使用20次每次连接10秒平均电流可以这样粗估睡眠时间23小时50分5uA × 85800秒 约0.12mAh工作时间20次 × 10秒 × 平均12mA约0.67mAh继电器动作20次 × 0.5秒 × 平均80mA约0.22mAh24小时总耗电约1mAh按这个估算一颗800mAh的锂电池理论能用800天扣掉自放电和转换效率一年以上是完全可行的。关键就是别让设备一直处于连接态连接态下电流太恐怖等于一直在烧电。7. 经验总结从原型到量产的几个建议项目做到这一步原型基本就完成了。但如果你和我一样最终目标是出货还有几个量产相关的坑提前给你打好预防针。第一天线匹配和认证问题。ESP32-C3模组出厂前已经做过天线匹配但PCB整体布局会影响天线效率。量产前务必做一次整机射频测试如果用了金属外壳建议直接用带IPEX座的外置天线版本模组。做产品出海还要过蓝牙认证BQB、FCC、CE这些这笔费用和技术成本要提前算进预算。第二固件远程升级不是可选项。锁装在人家的门上不可能出了问题把门拆了回来刷固件。ESP32-C3支持通过BLE进行OTA升级把新固件分包写入然后在App端完成升级。我强烈建议在开发早期集成OTA功能这时候改起来容易等产品铺开再想着加就非常痛苦了。第三安全设计要留出迭代空间。蓝牙锁的安全问题会一直存在你不可能在产品发布后就一劳永逸。方案设计时尽量把校验逻辑模块化比如密钥管理单独抽出来方便后续换算法、换密钥不至于改一处动全身。最后再分享一个我这几年做蓝牙硬件的小体会很多刚入行的朋友会把精力过多放在“怎么连上”这一步但其实连接只是入场券真正决定项目成色的是连接之后的事情——指令设计得是否严谨、异常处理是不是完备、低功耗策略是否合理、安全机制能不能防住攻击。这些才是蓝牙开锁类产品能稳定出货的关键。希望这篇文章能帮你少走一些弯路如果你也正在做类似的项目欢迎拿里面的思路去验证有问题随时可以交流。
返回列表