FEATURED · 精选文章

STM32+AS608+ESP8266:从零搭建无线指纹考勤系统

发布时间 / 2026/9/20 4:35:16
来源 / 创域科博编辑部
栏目 / 资讯中心
STM32+AS608+ESP8266:从零搭建无线指纹考勤系统 简介这是一份以STM32F103C8T6为核心的指纹考勤机完整设计文档面向嵌入式开发学习者、电子竞赛参赛者及物联网项目开发者。项目融合AS608指纹识别、ESP8266 WIFI通信、OLED显示、SQLite数据库与Qt上位机开发既有设备端C语言寄存器编程、指纹录入/删除/考勤等核心逻辑也包含Windows/Android上位机UI设计与CSV导出方案。文档按项目开发流程编排涵盖背景综述、硬件选型、原理图、实物组装、KEIL工程代码解析、Qt环境搭建及逐模块功能讲解并配有系统框架图、原理图与实物图便于从零复现或二次开发。资源为单个PDF文件约38.25MB目前已有81人学习适合作为毕业设计、课程设计或智能考勤系统开发的参考资料。1. 为什么是STM32 指纹考勤机这条技术路线指纹考勤机这个品类看似成熟但真正在小型企业、实验室或校园场景落地时产品化方案往往卡在“数据封闭、协议私有、二次开发成本高”三个问题上。市面上一台商用指纹考勤机几百到上千元不等数据导出受品牌App限制想要接入现有OA或HR系统非常吃力。而基于STM32F103C8T6自行搭建的这套方案本质上是把指纹识别、无线通信、数据存储和上位机管理全部解耦每一层都能替换、能调试总硬件成本可以压到百元以内。对于嵌入式开发者来说这个项目的价值不只是复现一台考勤机而是把“传感器采集—串口协议—状态机控制—无线传输—GUI数据闭环”这条完整的物联网设备链路走通一遍。AS608指纹模块负责身份特征提取ESP8266用AP模式建了一个轻量TCP服务器STM32以寄存器方式直接操作外设上位机则用Qt同时产出Windows与Android客户端配合SQLite做本地持久化。下文按硬件选型、设备端固件、上位机通信、排错与进阶四个层面拆解。2. 硬件架构与通信协议先确定数据怎么流2.1 核心器件选型依据主控选择STM32F103C8T6看重的是它的性价比与外设匹配度64KB Flash、20KB SRAM常规考勤场景下足够承载指纹模块的串口驱动、ESP8266的AT指令解析和SQLite裁剪后的轻量数据存储。更重要的是F103系列支持UART、I2C、SPI、定时器、RTC等多种外设一块芯片可以同时驱动指纹模块UART、OLEDI2C和ESP8266UART不需要额外扩展IO。AS608指纹模块的选型理由很直接模块内部已经集成了指纹图像采集、特征提取和比对算法对外只暴露一个UART串口通过指令包即可完成录入、搜索、删除和比对操作。它最多可以存储1000个指纹模板每个模板约占用400字节左右的Flash空间。这种封装方式大幅降低了主控侧的算法负担STM32只需要处理指令帧的组装与解析不需要自己实现指纹特征点匹配算法。ESP8266在这里的工作模式是AP模式加TCP Server。为什么不用STA模式直连路由器原因在于考勤机需要部署在没有固定网络的场景比如临时会议室、工地或移动考勤点。AP模式下手机或PC直接连接设备发出的Wi-Fi热点建立TCP连接后即可通信不依赖外部网络基础设施。ESP8266在AP模式下最多支持4个客户端连接对单机考勤场景完全够用。模块通信接口关键参数作用STM32F103C8T6GPIO/UART/I2C72MHz主频64KB Flash主控逻辑与数据处理AS608UART默认波特率57600支持1000枚指纹指纹录入与比对ESP8266UART115200波特率AP模式TCP Server与上位机无线通信OLED 0.96寸I2C地址0x3C4针接口本地状态显示蜂鸣器GPIO高电平触发有源蜂鸣器操作反馈2.2 AS608指纹模块的指令帧格式与UART接线AS608采用自定义的串口通信协议数据包结构固定为包头0xEF01 芯片地址4字节默认0xFFFFFFFF 包标识 包长度 指令码 参数/数据 校验和。以最简单的“检测指纹模块是否存在”的握手指令为例整个指令包为EF 01 FF FF FF FF 01 00 03 01 00 05逐字节说明EF 01是固定包头FF FF FF FF是模块默认地址01表示命令包00 03代表包长度为301是指令码验证密码00是默认密码高位05是校验和01000301取低字节。STM32发送这一帧后AS608会返回确认帧表示模块通信正常。连接方面AS608模块的TXD接STM32的PA10USART1_RXRXD接PA9USART1_TX电源接5VGND共地。模块自身带有电平转换电路可以直接与3.3V逻辑的STM32通信不需要额外加转换芯片。实际布线时需要注意AS608的排线方向模块上的箭头标识对应排线插座的1脚不要插反。2.3 ESP8266的AT指令配置与TCP Server建立ESP8266默认出厂固件支持AT指令集上电后通过串口发送AT指令即可完成模式配置。关键指令序列如下ATCWMODE2 // 设置为AP模式 ATCWSAPAttendance_AP,12345678,5,3 // 配置热点名称、密码、信道、加密方式 ATCIPMUX1 // 开启多连接 ATCIPSERVER1,8080 // 启动TCP Server端口8080配置完成后模块会在默认波特率115200下持续监听。上位机连接热点后通过TCP客户端连接192.168.4.1:8080即可与STM32建立数据通道。ESP8266收到TCP数据后会通过串口输出IPD前缀的数据帧例如IPD,0,5:hello表示连接ID为0的客户端发来了5字节数据hello。STM32侧解析这个前缀提取连接ID和实际数据长度再按自定义协议处理业务数据。2.4 OLED与蜂鸣器的低资源占用设计0.96寸OLED采用I2C接口在STM32上使用PB6作为SCL、PB7作为SDA地址为0x3C。由于I2C速率在标准模式下只有100kHz显示一帧128×64像素的内容大约需要20ms刷新频率控制在10帧每秒以内比较合理。蜂鸣器是简单的高电平触发有源蜂鸣器接在PB12上通过GPIO输出高电平即可发声。代码中定义了一个BEEP_ON和BEEP_OFF宏在不同的操作节点触发不同长度的鸣叫次数——录入成功短响两次识别成功短响一次失败长响一次这样用户可以在不看屏幕的情况下区分操作结果。3. 设备端固件寄存器级控制与业务状态机3.1 Keil工程结构与寄存器初始化设备端代码在Keil uVision 5环境下开发不依赖HAL库或标准外设库而是直接操作寄存器。这样做的收益是代码体积小、执行路径清晰调试时可以精确知道每条指令操作了哪个寄存器。工程文件结构上按照模块划分main.c主循环、uart.c串口收发、fingerprint.cAS608驱动、wifi.cESP8266通信、oled.c显示驱动、rtc.c时间管理、database.cSQLite封装和key.c按键逻辑。系统初始化代码中比较关键的是USART1和USART2的配置。USART1用于连接AS608指纹模块波特率57600USART2用于连接ESP8266波特率115200。两个串口都开启接收中断接收数据存入各自的环形缓冲区。初始化关键代码void USART1_Init(void) { RCC-APB2ENR | RCC_APB2ENR_USART1EN; // 使能USART1时钟 RCC-APB2ENR | RCC_APB2ENR_IOPAEN; // 使能GPIOA时钟 GPIOA-CRH ~(0xFF 4); // PA9 TX, PA10 RX 配置为复用推挽输入 GPIOA-CRH | (0x8B 4); // TX模式复用推挽输出50MHzRX模式浮空输入 USART1-BRR 72000000 / 57600; // 设置波特率 USART1-CR1 | USART_CR1_RE | USART_CR1_TE; // 使能收发 USART1-CR1 | USART_CR1_RXNEIE; // 开启接收中断 NVIC_EnableIRQ(USART1_IRQn); }这里BRR寄存器的计算方式是72MHz系统时钟除以目标波特率。USART1挂在APB2总线上APB2时钟最大72MHz所以直接取72MHz计算。USART2挂在APB1总线上APB1最大36MHz需要先确认RCC初始化里是否正确配置了PPRE1分频器。这是初学者最容易踩的坑如果只改了BRR而没有确认总线时钟串口收到的数据会全部乱码。3.2 指纹录入与比对的状态机设计指纹业务逻辑不适合在串口中断里做因为AS608的一次完整操作需要多次收发交互。以录入指纹为例流程是上位机下发录入指令-STM32向AS608发送录入指令-AS608采集第一次指纹-提示用户移开手指-采集第二次指纹-两次采集匹配成功后生成模板-将模板存储在AS608的Flash中-返回结果给上位机。整个流程需要数秒期间涉及按键确认、OLED提示和蜂鸣器反馈所以实际实现中采用状态机轮询的方式typedef enum { FP_IDLE 0, // 空闲 FP_WAIT_PRESS, // 等待按下手指 FP_CAPTURE_1, // 第一次采集完成 FP_WAIT_RELEASE, // 等待移开手指 FP_CAPTURE_2, // 第二次采集 FP_MERGE_TEMPLATE, // 合并模板 FP_STORE_TEMPLATE, // 存储模板 } FP_State_t;状态机的驱动位置在主循环中每次轮询当前状态调用对应的指令函数。例如FP_CAPTURE_1状态下向AS608发送采集指纹图像指令指令码0x01等待返回确认帧如果确认帧参数区显示采集超时则退回FP_IDLE并要求上位机重新发起录用流程。这种设计把异步的串口通信转化为同步的轮询状态推进代码可读性和排查难度都大幅降低。比对操作的指令是搜索指纹指令码0x0B需要先采集指纹图像再从缓冲区中搜索匹配的模板ID。AS608的比对速度通常在100ms到300ms之间整个流程从用户按下手指到得到比对结果大约在1秒以内。3.3 上位机指令帧协议设计设备端与上位机之间的通信需要一套明确的指令协议。考虑到帧经过ESP8266的TCP链路最终由USART2接收所以协议设计的原则是状态码长度校验避免粘包和半包问题。指令帧格式设计如下帧头(2字节) | 指令码(1字节) | 数据长度(2字节) | 数据(可变) | CRC8校验(1字节) 帧头固定为 0xAA 0x55指令码定义0x01录入指纹0x02删除指纹0x03清空指纹库0x04指纹比对0x05设置考勤时间0x06查询考勤记录0x07导出CSV0x08设置RTC时间。在STM32的USART2中断中接收到的字节先存入环形缓冲区主循环中调用parse_frame()函数逐字节扫描检测到帧头后开始按长度字段收帧收满后计算CRC8校验值校验通过则进入指令分发逻辑void handle_command(uint8_t cmd, uint8_t *data, uint16_t len) { switch (cmd) { case CMD_ENROLL_FP: fp_enroll_start(data[0]); // data[0]是工号 break; case CMD_DELETE_FP: fp_delete_from_buffer(data[0]); // data[0]是模板ID break; case CMD_SET_ATTEND_TIME: rtc_set_period(data[0], data[1]); // 设置考勤开始与结束小时 break; case CMD_SET_RTC: rtc_set_time(data); // 6字节年/月/日/时/分/秒 break; case CMD_EXPORT_CSV: db_export_attendance(data[0]); // data[0]是导出类型0当日/1月度 break; } }数据帧中的工号、模板ID都以小端序传输。上位机发送录入指纹指令时需要先通过TCP链路把工号和姓名同步到设备端SQLite数据库然后再触发AS608的采集流程这样指纹模板存于AS608中人员信息存于SQLite中两者通过工号关联。3.4 RTC时间逻辑与考勤时段判定考勤系统的核心是时间判定。STM32F103C8T6内部RTC使用外部32.768kHz晶振走时精度存在一定偏移通常每个月会有几十秒到几分钟的误差。项目中通过上位机的“设置时间”指令将PC或手机的系统时间写入设备RTC校准操作建议每周执行一次。RTC走时逻辑在定时器中断中维护每秒更新时分秒并在主循环中检查当前星期。考勤时段判定逻辑如下上位机下发当日考勤时间段例如上午08:00-12:00、下午14:00-18:00。STM32在收到指纹比对成功的通知后先读RTC当前时间与设定的时间段做比较。如果在时间段内则记录考勤成功否则在OLED上显示“当前不在考勤时间段”并在返回帧中携带错误码0xE1。这里有一个容易被忽略的细节跨天的时间段处理。如果下班时间段是22:00到次日02:00单纯比较序列号会判定失败需要先判断结束时间小于开始时间的场景这种情况下要增加跨天标志位处理。uint8_t time_in_range(uint8_t cur_hour, uint8_t start_hour, uint8_t end_hour) { if (start_hour end_hour) { return (cur_hour start_hour cur_hour end_hour) ? 1 : 0; } else { // 跨天场景例如22:00-02:00 return (cur_hour start_hour || cur_hour end_hour) ? 1 : 0; } }3.5 按键逻辑与状态输出设备端保留实体按键用于本地操作。按键连接在PB0上通过外部中断和消抖定时器实现。按键的功能逻辑与上位机配合短按一次切换OLED显示页面显示时间页和显示最近考勤记录页长按3秒进入指纹录入模式此时需要上位机先下发录入指令设备端进入等指状态。按键检测逻辑放在10ms定时中断中扫描避免在主循环中阻塞。4. Qt上位机与数据库链路从TCP到CSV导出的完整闭环4.1 Qt跨平台工程搭建与TCP通信上位机开发采用Qt 5框架一套代码同时编译Windows桌面版和Android移动版。工程创建时选择Qt Widgets Application模板在.pro文件中添加网络和数据库支持QT core gui network sql greaterThan(QT_MAJOR_VERSION, 4): QT widgetsTCP客户端使用QTcpSocket类连接ESP8266创建的TCP Server。关键连接逻辑放在界面初始化过程中tcpSocket new QTcpSocket(this); connect(tcpSocket, QTcpSocket::connected, this, MainWindow::onConnected); connect(tcpSocket, QTcpSocket::readyRead, this, MainWindow::onReadyRead); tcpSocket-connectToHost(192.168.4.1, 8080);readyRead信号触发时调用readAll()读取缓冲区数据存入一个QByteArray再按协议解析。由于TCP是流式协议一次readyRead信号可能只收到半帧数据所以解析函数需要维护一个部分帧缓冲区将未完成的数据缓存起来等待下一次数据到达后拼接。4.2 SQLite表结构设计设备端嵌入SQLite数据库上位机也内嵌一份完整的数据两个方向的数据同步由TCP指令完成。考勤系统的核心表结构如下CREATE TABLE employees ( emp_id INTEGER PRIMARY KEY, emp_no TEXT NOT NULL UNIQUE, -- 员工工号 emp_name TEXT NOT NULL, -- 姓名 fp_template_id INTEGER, -- AS608中的模板ID create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE attendance_records ( record_id INTEGER PRIMARY KEY AUTOINCREMENT, emp_no TEXT NOT NULL, emp_name TEXT NOT NULL, check_time TIMESTAMP NOT NULL, -- 考勤时间 status TEXT DEFAULT normal -- normal/early/late );实际设计中emp_no是关联两表的外键考勤记录中冗余存储emp_name是为了避免查询考勤记录时需要频繁JOIN员工表这种反规范化设计在嵌入式场景中能显著提升查询效率。SQLite在STM32上运行时的限制是内存占用需要将SQLITE_THREADSAFE宏设为0单线程模式同时关闭WAL日志模式以节省Flash写入次数。4.3 考勤记录查询与CSV导出实现当日考勤记录查询使用带时间范围的SQL语句QSqlQuery query; query.prepare(SELECT emp_no, emp_name, check_time, status FROM attendance_records WHERE check_time ? AND check_time ? ORDER BY check_time); query.addBindValue(QDateTime::currentDateTime().toString(yyyy-MM-dd 00:00:00)); query.addBindValue(QDateTime::currentDateTime().toString(yyyy-MM-dd 23:59:59)); query.exec();导出CSV时使用QFile和QTextStream逐行写入注意添加BOM头\xEF\xBB\xBF否则生成的CSV文件用Excel打开时中文会乱码QFile file(filePath); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return; QTextStream out(file); out.setCodec(UTF-8); out \xEF\xBB\xBF; // 写入BOM out 工号,姓名,考勤时间,状态\n; while (query.next()) { out query.value(0).toString() , query.value(1).toString() , query.value(2).toString() , query.value(3).toString() \n; } file.close();CSV导出分两种场景当日考勤和月度考勤。月度导出的SQL条件改为check_time BETWEEN ? AND ?参数是月初第一天和月末最后一天。如果数据量超过5000条建议分批次查询每批1000条避免一次性加载全部记录导致Android端内存溢出。4.4 上位机人员管理与指纹操作交互上位机的“添加员工”操作分为两个阶段。第一阶段是信息录入用户在界面上输入工号和姓名点击保存后先写入SQLite。第二阶段是指纹采集上位机发送录入指令用户将手指按在设备端AS608上设备完成两次采集后返回模板ID上位机收到成功后更新employees表中的fp_template_id字段。如果在采集过程中用户移开了手指AS608会返回错误码0x0A采集失败上位机需要弹出“采集失败请重试”的提示并将事务回滚避免数据库中出现只有工号没有指纹模板的孤儿记录。删除员工的流程也需要注意顺序问题上位机先向设备发送删除指纹指令确认删除成功后再删除本地SQLite中的员工记录和考勤记录。如果顺序反过来设备端删除指纹失败会导致脏数据。实际操作中可以增加两步确认上位机将删除操作设计为弹窗二次确认防止误操作。5. 设备联调排错与识别率提升的实践技巧5.1 典型坑点与排查方法指纹模块串口乱码是最常见的问题。先检查BRR寄存器计算的是否为对应总线时钟再用逻辑分析仪抓取UART波形验证波特率是否准确。ESP8266的TCP连接频繁断开时优先检查电源模块在Wi-Fi发射瞬间电流峰值可达300mA如果供电线缆压降过大会造成模块重启此时用示波器观察ESP8266 VCC引脚是否有明显跌落建议在模块电源引脚旁并联一个100uF电解电容和100nF陶瓷电容。考勤数据未能写入SQLite时先确认数据库文件是否在STM32 Flash上正确创建。受Flash擦写寿命和容量的限制SQLite数据库文件建议存放在外部SPI Flash或SD卡中STM32内部Flash的十万次擦写寿命在频繁写入考勤记录时很快会耗尽。数据库操作放在一个单独的线程中执行避免数据插入过程中阻塞指纹采集。5.2 指纹识别率低时的优化方向AS608的识别率受手指干湿状态、按压位置偏移和指纹老化因素影响。我实测下来识别失败率在5%左右属于正常范围但如果用户频繁识别失败可以重新录用指纹并在录入时要求用户变换按压角度保证两张指纹图像的有效区域有足够重叠。另一个可行方案是让AS608从多个方向采样指纹生成拼接模板但这需要修改模块内部算法AS608官方固件不支持只能通过增加每个用户录入两枚手指的方式兜底。5.3 轮询时间片与系统实时性调优设备端主循环中同时处理指纹状态机、ESP8266数据解析、OLED刷新和按键扫描各模块任务耗时不同简单的顺序轮询会造成高优先级延迟。我一般会用一个10ms定时器作为系统心跳在主循环中设置一个待处理标志位各模块在10ms内必须让出CPU核心逻辑如下if (time_elapsed 10) { time_elapsed 0; key_scan(); // 按键扫描消抖后更新事件状态 fp_state_machine(); // 指纹状态机推进 wifi_process_packet(); // 解析ESP8266收到的数据帧 oled_refresh(); // 根据错误码刷新显示 }这样设计后指纹采集的一次完整交互被拆分成多个10ms时间片期间如果ESP8266收到上位机的紧急指令比如清空指纹库可以在下一个时间片立即处理系统响应时间不超过20ms。5.4 业务扩展方向与改造建议项目已经具备考勤机的最小完整闭环如果想进一步产品化可以从以下几个方向扩展增加SD卡模块存储考勤照片实现刷卡拍照溯源使用ESP32替换ESP8266利用其双核优势在通信之外增加片内指纹特征比对将SQLite迁移至嵌入式Redis或平替的FlashFS文件系统进一步提升并发写入能力。硬件层面还可以把USB 5V供电改为锂电池加充电管理芯片配合ESP8266的Modem-Sleep低功耗模式将设备改为便携移动终端。这些改造都能在现有代码框架下平滑演进。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻