智能门锁嵌入式编程:实现指纹识别、密码校验与蓝牙开锁的多逻辑调度
智能门锁不是“指纹任务+密码任务+蓝牙任务各管各的”,那样必出三源同时触发、电机被重复驱动、蓝牙指令重放。正确架构是输入源只产生认证事件,调度器统一仲裁,执行层只认一条开锁指令。指纹、键盘、BLE都别直接碰电机。
一、三层解耦
采集层:指纹UART、矩阵键盘、BLE GATT
认证层:匹配/比对/解密,产出auth_event
调度层:状态机+队列,判断是否允许开锁
执行层:电机驱动+锁舌反馈+日志
各任务通过消息队列发给主锁控任务,谁先合法谁触发,触发后进入“已处理”窗口,别源重复计。
二、指纹:UART只收结果
AS608/ZW101类模块内部完成采图匹配,主控别去解析图像,只发指令收确认码。
typedef enum {
AUTH_NONE, AUTH_FP_OK, AUTH_PWD_OK,
AUTH_BLE_OK, AUTH_FAIL
} auth_src_t;
void fp_task(void*p){
uint8_t rsp[32];
if(fp_wait_finger()){ // TOUCH中断唤醒
fp_send_cmd(CMD_GET_IMAGE);
fp_send_cmd(CMD_GEN_CHAR);
uint8_t code = fp_search(rsp); // 0x00成功
if(code==0x00)
lock_post_event(AUTH_FP_OK, rsp[2]); // 匹配ID
else
lock_post_event(AUTH_FAIL, 0);
}
}
坑点:UART接收放任务里,别在串口中断里解帧;模块供电要独立,采图瞬间电流抖动会让主控复位。
三、密码:虚位+错误计数掉电保持
密码输入别只比“等于”,做子串匹配:输入缓冲里包含真实密码连续序列即过,前后可加干扰数字。
int pwd_check(const uint8_t*buf,int len,const uint8_t*pwd,int plen){
for(int i=0;i+plen<=len;i++)
if(memcmp(buf+i,pwd,plen)==0) return 1;
return 0;
}
错误计数不能放RAM,断电归零等于没锁。写Backup SRAM或EEPROM,带RTC时间戳:
void pwd_fail_record(void){
fail_cnt++;
last_fail_ts = rtc_sec();
bkp_write(FAIL_ADDR, fail_cnt);
if(fail_cnt>=5){
lock_state = LOCK_FROZEN;
freeze_release_ts = rtc_sec()+60; // 锁1分钟
}
}
虚位密码要限总长,避免无限输入蹭缓冲;确认键#收尾,超时15s回IDLE清缓冲。
四、蓝牙:BLE指令必须防重放
HC-06明文配对别上量;正经方案走BLE LESC配对+应用层再验一次。
开锁指令结构:
typedef struct {
uint8_t cmd;
uint32_t ts; // 时间戳
uint32_t nonce; // 随机数
uint8_t mac[8]; // HMAC
} ble_unlock_t;
校验顺序:
链路层已加密(LTK)
ts与本地偏差<5s → 否则丢(防重放)
nonce大于上次记录
HMAC用主密钥验
再判断用户权限
void ble_task(void*p){
ble_unlock_t u;
if(ble_recv((uint8_t*)&u,sizeof(u))){
if(abs(u.ts-rtc_sec())>5) return;
if(!hmac_ok(&u)) return;
if(u.cmd==CMD_UNLOCK)
lock_post_event(AUTH_BLE_OK, 0);
}
}
Just Works配对不够,门锁用Numeric Comparison或带外密钥。
五、调度器:单点仲裁
所有认证源都调lock_post_event,主状态机决定做不做:
typedef enum { LS_IDLE, LS_VERIFYING,
LS_OPENING, LS_FROZEN } lock_st_t;
static lock_st_t ls = LS_IDLE;
void lock_post_event(auth_src_t s, uint8_t id){
static uint32_t open_guard;
if(ls==LS_OPENING) return; // 正在开,去重
if(ls==LS_FROZEN){
if(rtc_sec()>=freeze_release_ts){ ls=LS_IDLE; }
else return;
}
if(s==AUTH_FP_OK||s==AUTH_PWD_OK||s==AUTH_BLE_OK){
motor_open(); // 只这里动电机
log_access(s,id);
ls = LS_OPENING;
}else{
pwd_fail_record();
}
}
规则:
指纹/密码/蓝牙任一合法即可开(单因子)
高安全模式要求双因子:先指纹再BLE确认
同一次按压/一次连接只开一次
电机动作期间屏蔽新事件
六、电机执行防卡死
开锁不是“IN1=1转两秒”。要带超时+过流+位置反馈:
void motor_open(void){
uint32_t t0=systick();
motor_set(FWD);
while(!hall_locked() && systick()-t0 < 800); // 等锁舌到位
motor_set(BRAKE);
if(systick()-t0>=800) log_err(E_MECH); // 卡死记录
}
驱动加OCP,电流超阈值切输出;半锁状态别硬顶,回退重试一次;霍尔/微动开关确认到位再回IDLE。
七、落坑清单
指纹任务里直接motor_open()→蓝牙又触发重复开
密码错误计数放局部变量→断电清零
虚位密码不限长→缓冲溢出
BLE只比cmd==1→重放包照开
Just Works配对→中间人伪造
串口中断里解帧→阻塞卡死
电机无超时→指纹识别后卡死不回弹
三任务同写全局状态→竞态
开锁中不关中断事件→连发抖动
八、落地顺序
建锁控状态机LS_IDLE/VERIFYING/OPENING/FROZEN
指纹UART任务只发事件
键盘任务做虚位匹配+Backup SRAM错误计数
BLE任务做LESC+时间戳+nonce+HMAC
统一lock_post_event入队
主任务唯一调用motor_open
电机带霍尔反馈+800ms超时+过流保护
每次开锁写日志(源/ID/时间)
智能门锁多逻辑调度的本质不是“三个功能都能开”,是三个输入源都不能绕过调度器。指纹快、密码稳、蓝牙远控,但执行出口只有一个;事件队列隔开中断和驱动,状态机隔开多源冲突,防重放和错误冻结补上安全底线。代码不复杂,乱的是把认证和驱动耦合在一起——谁直接碰电机,谁就是bug源头。





