340 lines
11 KiB
Markdown
340 lines
11 KiB
Markdown
# STM32 固件端文档
|
|
|
|
## 1. STM32 在系统中做什么
|
|
|
|
STM32 是整套考勤机的主控。它不做人脸识别,人脸识别由 K210 完成。STM32 负责把 K210 的识别结果变成“是否允许签到”的业务判断,并控制硬件反馈。
|
|
|
|
它主要做这些事:
|
|
|
|
1. 初始化 OLED、蜂鸣器、LED、串口、RTC、光敏传感器、补光灯等硬件。
|
|
2. 接收 K210 通过 USART3 发来的识别结果。
|
|
3. 判断识别置信度是否足够。
|
|
4. 判断当前时间是否在服务端配置的签到时间段内。
|
|
5. 判断当前学生在本时间段是否已经签过。
|
|
6. 生成签到记录。
|
|
7. 使用 AES-128-CBC 加密签到 JSON。
|
|
8. 通过 ESP8266 发送 HTTP 请求到服务端。
|
|
9. 网络异常时把记录留在本地,后续自动重试。
|
|
|
|
## 2. 相关文件
|
|
|
|
本节路径以 `stm32/` 目录为根。
|
|
|
|
| 文件 | 作用 |
|
|
| --- | --- |
|
|
| `User/main.c` | 主程序入口、初始化流程、主循环、联网和同步调度 |
|
|
| `Hardware/Attendance.c` | 考勤业务逻辑:时间段、重复判断、本地记录、同步状态 |
|
|
| `Hardware/Attendance.h` | 考勤数据结构和函数声明 |
|
|
| `Hardware/K210_USART.c` | 解析 K210 串口识别结果 |
|
|
| `Hardware/esp8266.c` | ESP8266 AT 指令、HTTP GET/POST、对时、查重 |
|
|
| `Hardware/Crypto.c` | AES-128-CBC 加密和 Base64 编码 |
|
|
| `Hardware/DS1302.c` | DS1302 RTC 时钟读写 |
|
|
| `Hardware/LightSensor.c` | 光敏电阻 ADC 和补光灯控制 |
|
|
| `Hardware/Buzzer_LED.c` | 蜂鸣器、成功 LED、状态 LED |
|
|
| `Hardware/OLED.c` | OLED 显示 |
|
|
| `System/Delay.c` | 延时和毫秒计时 |
|
|
| `System/Debug.c` | 调试串口输出 |
|
|
| `Project.uvprojx` | Keil 工程文件 |
|
|
|
|
## 3. 硬件连接
|
|
|
|
| 外设 | STM32 引脚 | 说明 |
|
|
| --- | --- | --- |
|
|
| ESP8266 TX/RX | PA10 / PA9 | USART1,波特率 115200 |
|
|
| K210 TX/RX | PB11 / PB10 | USART3,波特率 115200 |
|
|
| 调试串口 | PA3 / PA2 | USART2,波特率 115200 |
|
|
| OLED SCL/SDA | PB8 / PB9 | I2C |
|
|
| 成功 LED | PB4 | 高电平点亮 |
|
|
| 蜂鸣器 | PB5 | 低电平触发 |
|
|
| 光敏传感器 | PB0 | ADC1 通道 8 |
|
|
| 补光灯 | PB1 | 高电平点亮 |
|
|
| DS1302 CLK/DAT/RST | PA4 / PA5 / PA6 | 软件 GPIO 时序 |
|
|
| 板载状态 LED | PC13 | 低电平点亮,初始化中亮,就绪后灭 |
|
|
|
|
## 4. 代码中的主要配置
|
|
|
|
位置:`User/main.c`
|
|
|
|
| 配置 | 当前值 | 说明 |
|
|
| --- | --- | --- |
|
|
| `WIFI_SSID` | `yzfh` | ESP8266 要连接的 WiFi |
|
|
| `WIFI_PASSWORD` | `yzfhroot` | WiFi 密码 |
|
|
| `HTTP_SERVER_IP` | `27.106.113.205` | 服务端 IP |
|
|
| `HTTP_SERVER_PORT` | `8082` | 服务端 HTTP 端口 |
|
|
| `HTTP_API_TOKEN` | `atk_nKfVWLhMjL0RGSfRll3R_sdJS6IrIjuV` | API Token |
|
|
| `CLASS_ID` | `1` | 当前 STM32 设备所属班级编号 |
|
|
| `TIME_SYNC_INTERVAL_MS` | `3600000` | 每小时对时一次 |
|
|
| `TIMESLOT_DOWNLOAD_INTERVAL_MS` | `30000` | 每 30 秒检查一次时间段配置 |
|
|
| `WIFI_RECONNECT_INTERVAL_MS` | `30000` | WiFi 断开后每 30 秒重连 |
|
|
| `RECONNECT_INTERVAL_MS` | `5000` | 服务端连接不可用后每 5 秒尝试重连 |
|
|
| `MIN_CONFIDENCE_THRESHOLD` | `0.75` | 最低识别置信度 |
|
|
| `MAX_ATTENDANCE_RECORDS` | `100` | STM32 本地最多缓存 100 条记录 |
|
|
| `MAX_TIME_SLOT_COUNT` | `10` | 最多支持 10 个签到时间段 |
|
|
|
|
## 5. 启动流程
|
|
|
|
STM32 上电后按下面顺序执行:
|
|
|
|
1. 初始化调试串口。
|
|
2. 初始化延时模块。
|
|
3. 初始化 OLED。
|
|
4. 初始化蜂鸣器和 LED。
|
|
5. 初始化 USART1,用于连接 ESP8266。
|
|
6. 初始化 USART3,用于接收 K210 数据。
|
|
7. 初始化考勤模块。
|
|
8. 初始化 AES 加密模块。
|
|
9. 初始化 DS1302,并尝试读取时间。
|
|
10. 初始化光敏传感器和补光灯。
|
|
11. 初始化 ESP8266。
|
|
12. 连接 WiFi。
|
|
13. 从服务端获取当前时间。
|
|
14. 连接 HTTP 服务端。
|
|
15. 下载签到时间段配置。
|
|
16. 下载今日当前时段已签到记录。
|
|
17. 进入主循环。
|
|
|
|
如果 WiFi 或服务端连接失败,程序不会停机,而是进入离线或重连状态。后续主循环会自动重试。
|
|
|
|
## 6. 主循环每次做什么
|
|
|
|
`User/main.c` 的主循环大致如下:
|
|
|
|
```c
|
|
while (1) {
|
|
ProcessBeep();
|
|
UpdateCurrentTime();
|
|
CheckServerConnection();
|
|
SyncPendingRecords();
|
|
UpdateTimeSlotConfig();
|
|
LightSensor_Update();
|
|
|
|
if (K210_GetRxFlag()) {
|
|
Attendance_ProcessFaceResult(&K210_FaceResult);
|
|
}
|
|
}
|
|
```
|
|
|
|
逐项解释:
|
|
|
|
| 函数 | 作用 |
|
|
| --- | --- |
|
|
| `ProcessBeep` | 非阻塞处理蜂鸣器和成功 LED |
|
|
| `UpdateCurrentTime` | 从 DS1302 或软件时钟维护当前时间,并定期网络对时 |
|
|
| `CheckServerConnection` | 检查当前 HTTP/TCP 连接状态,连接不可用时按需重连 |
|
|
| `SyncPendingRecords` | 把本地未同步记录加密上传 |
|
|
| `UpdateTimeSlotConfig` | 定期下载服务端签到时间段 |
|
|
| `LightSensor_Update` | 根据环境光自动开关补光灯 |
|
|
| `K210_GetRxFlag` | 看是否收到 K210 的新识别结果 |
|
|
|
|
注意:当前 STM32 HTTP 请求和服务端响应都使用 `Connection: close`,所以它不是长期保持一个 TCP 长连接,而是在每次请求前检查连接状态,并在需要时重新建立连接。
|
|
|
|
## 7. K210 发给 STM32 的数据格式
|
|
|
|
K210 通过 UART1 发送文本,STM32 通过 USART3 接收。
|
|
|
|
识别成功:
|
|
|
|
```text
|
|
ID:20240001,NAME:Zhang_San,CONF:0.95
|
|
```
|
|
|
|
字段说明:
|
|
|
|
| 字段 | 含义 |
|
|
| --- | --- |
|
|
| `ID` | 学号或 K210 同步后的身份编号 |
|
|
| `NAME` | 显示名,K210 代码中会用下划线代替空格 |
|
|
| `CONF` | 置信度,范围一般是 0.0 到 1.0 |
|
|
|
|
未识别:
|
|
|
|
```text
|
|
UNKNOWN
|
|
```
|
|
|
|
STM32 收到 `UNKNOWN` 后不会签到。
|
|
|
|
## 8. 签到判断逻辑
|
|
|
|
STM32 对一次识别结果的判断顺序如下:
|
|
|
|
1. 判断 K210 是否识别成功。
|
|
2. 判断置信度是否低于 `0.75`。
|
|
3. 判断当前是否在启用的时间段内。
|
|
4. 判断当前时段本地记录中是否已经有该学生。
|
|
5. 判断从服务端下载的今日缓存中是否已经有该学生。
|
|
6. 如果服务端在线,再调用服务端查重接口确认。
|
|
7. 通过所有检查后,生成一条本地考勤记录。
|
|
8. 如果服务端在线,后续同步任务会加密上传。
|
|
|
|
当前默认没有任何时间段。必须由 Qt 管理端新增并保存时间段,否则第 3 步会失败,OLED 会显示不在签到时间。
|
|
|
|
## 9. 时间段同步
|
|
|
|
STM32 调用:
|
|
|
|
```text
|
|
GET /api/v1/sync/state?client=stm32&last_version=<本地版本>
|
|
```
|
|
|
|
服务端返回示例:
|
|
|
|
```json
|
|
{
|
|
"version": 5,
|
|
"changed": true,
|
|
"client": "stm32",
|
|
"server_time": "2026-05-01 10:00:00",
|
|
"state": {
|
|
"time_slots": [
|
|
{
|
|
"name": "上午签到",
|
|
"start_time": "08:00",
|
|
"end_time": "09:30",
|
|
"enabled": true
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
如果 `time_slots` 是空数组,STM32 会清空本地时间段配置。
|
|
|
|
## 10. 今日记录缓存
|
|
|
|
STM32 为了减少重复签到,会在开机连接服务端后下载今日当前时间段的已签到学号:
|
|
|
|
```text
|
|
GET /api/v1/attendance/today?slot_start=08:00&slot_end=09:30
|
|
```
|
|
|
|
返回:
|
|
|
|
```json
|
|
{
|
|
"date": "2026-05-01",
|
|
"records": ["20240001", "20240002"]
|
|
}
|
|
```
|
|
|
|
下载后,STM32 会把这些学号放进缓存。之后如果同一个学生在同一个时段再次识别,会被判定为重复签到。
|
|
|
|
## 11. 加密上传
|
|
|
|
STM32 上传签到记录时使用:
|
|
|
|
```text
|
|
POST /api/v1/attendance/checkins
|
|
X-Api-Token: <Token>
|
|
X-Encrypted: AES128-CBC
|
|
Content-Type: application/octet-stream
|
|
```
|
|
|
|
请求体是 Base64 编码后的 AES 密文。明文 JSON 类似:
|
|
|
|
```json
|
|
{
|
|
"student_id": "20240001",
|
|
"name": "Zhang San",
|
|
"confidence": 0.95,
|
|
"status": "success",
|
|
"class_id": "1"
|
|
}
|
|
```
|
|
|
|
服务端解密成功后写入 `attendance.db`。
|
|
|
|
## 12. 本地记录和同步状态
|
|
|
|
STM32 的本地记录结构体:
|
|
|
|
```c
|
|
typedef struct {
|
|
char student_id[20];
|
|
char student_name[32];
|
|
float confidence;
|
|
uint32_t timestamp;
|
|
uint8_t time_slot;
|
|
SyncStatus sync_status;
|
|
uint8_t retry_count;
|
|
uint32_t last_retry_time;
|
|
} AttendanceRecord;
|
|
```
|
|
|
|
同步状态:
|
|
|
|
| 状态 | 含义 |
|
|
| --- | --- |
|
|
| `SYNC_PENDING` | 新记录,等待上传 |
|
|
| `SYNC_SYNCING` | 正在上传 |
|
|
| `SYNC_SYNCED` | 上传成功 |
|
|
| `SYNC_FAILED` | 上传失败,后续重试 |
|
|
|
|
## 13. OLED 和声光反馈
|
|
|
|
| 情况 | OLED | 蜂鸣器/LED |
|
|
| --- | --- | --- |
|
|
| 初始化中 | 显示 ESP/WiFi/HTTP 状态 | PC13 状态灯亮 |
|
|
| 服务端在线 | `System Ready / Online Mode` | 成功提示 |
|
|
| WiFi 在线但服务端断开 | `System Ready / Reconnecting..` | 后台重连 |
|
|
| WiFi 正在重连 | `System Ready / WiFi retry...` | 后台重连 |
|
|
| ESP8266 不可用 | `System Ready / Offline Mode` | 错误提示 |
|
|
| 签到成功 | `Check-in OK!` | 成功提示 |
|
|
| 重复签到 | `Already Signed!` | 错误提示 |
|
|
| 置信度低 | `Low Confidence` | 错误提示 |
|
|
| 不在时间段 | `Not Check Time` | 错误提示 |
|
|
|
|
## 14. 编译与烧录
|
|
|
|
使用 Keil 打开:
|
|
|
|
```text
|
|
Project.uvprojx
|
|
```
|
|
|
|
一般步骤:
|
|
|
|
1. 安装 Keil MDK 和 STM32F10x 支持包。
|
|
2. 打开 `Project.uvprojx`。
|
|
3. 确认芯片型号和启动文件与实际开发板一致。
|
|
4. 修改 `User/main.c` 中的 WiFi、服务端 IP、班级编号和 Token。
|
|
5. 编译工程。
|
|
6. 使用 ST-Link 或串口 Bootloader 烧录。
|
|
7. 打开调试串口查看日志。
|
|
|
|
## 15. 调试建议
|
|
|
|
### 15.1 先看调试串口
|
|
|
|
调试串口会输出:
|
|
|
|
1. 硬件初始化状态。
|
|
2. ESP8266 AT 指令结果。
|
|
3. WiFi 连接结果。
|
|
4. HTTP 服务端连接结果。
|
|
5. 时间同步结果。
|
|
6. 时间段下载结果。
|
|
7. K210 原始数据和解析结果。
|
|
8. 加密上传结果。
|
|
|
|
### 15.2 常见问题
|
|
|
|
| 现象 | 可能原因 | 处理 |
|
|
| --- | --- | --- |
|
|
| 一直显示 WiFi retry | SSID/密码错误、ESP8266 没接好、供电不足 | 检查 `WIFI_SSID`、`WIFI_PASSWORD`、供电和串口 |
|
|
| HTTP connection failed | 服务端 IP/端口错误、防火墙拦截、服务端没启动 | 用电脑访问健康检查接口 |
|
|
| Not Check Time | 没有配置时间段,或当前时间不在时间段内 | Qt 新增时间段并保存,检查 DS1302/NTP |
|
|
| Already Signed | 当前时段已经签过 | 换时间段或清空记录 |
|
|
| Low Confidence | K210 比对分数偏低 | 调整光线、重新录入人脸 |
|
|
| Qt 看不到记录 | STM32 未同步成功或服务端地址不一致 | 看服务端日志和 Qt 服务端地址 |
|
|
|
|
## 16. 修改配置时要同步修改哪里
|
|
|
|
| 你修改了什么 | 需要同步修改 |
|
|
| --- | --- |
|
|
| 服务端 IP | `User/main.c`、`k210/main.py`、Qt 配置中心 |
|
|
| API Token | 服务端环境变量或代码、`User/main.c`、`k210/main.py`、Qt 环境变量 |
|
|
| AES Key/IV | `Hardware/Crypto.c`、`server/server_encrypted.cpp`、`QT/src/CryptoUtils.cpp` |
|
|
| 班级编号 | 每块 STM32 对应修改 `CLASS_ID` |
|
|
| WiFi | `User/main.c` 和 `k210/main.py` |
|