Files
bishe-docs/01_STM32固件端文档.md
T
2026-05-04 21:03:06 +08:00

341 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
X-Encrypted-Response: true
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`,响应 JSON 也会以 AES-128-CBC + Base64 返回。STM32 的 `ESP8266_HttpGet` 会为时间、同步状态、查重和今日记录等 GET 请求带上 `X-Encrypted-Response: true`,接收后先解密,再把明文 JSON 交给上层解析。
## 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` |