初始提交:雷达系统代码,包含WiFi管理和雷达数据处理功能

This commit is contained in:
userName
2026-03-11 10:33:16 +08:00
commit 0fa483f450
17 changed files with 7105 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
Git提交方法.md
.trae/

10
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

52
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,52 @@
{
"files.associations": {
"cmath": "cpp",
"array": "cpp",
"string": "cpp",
"string_view": "cpp",
"new": "cpp",
"atomic": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"map": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"limits": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp"
},
"C_Cpp.errorSquiggles": "disabled"
}

516
BLE_API.md Normal file
View File

@@ -0,0 +1,516 @@
# ESP32 BLE API 文档
## 概述
本文档描述了 ESP32 雷达设备的 BLE蓝牙低功耗通信 API用于通过蓝牙连接配置和管理设备。
## 通信协议
- **传输方式**: BLE (Bluetooth Low Energy)
- **数据格式**: JSON
- **编码**: UTF-8
- **分包大小**: 20 字节/包
- **特征值 UUID**: `beb5483e-36e1-4688-b7f5-ea07361b26a8`
- **服务 UUID**: `a8c1e5c0-3d5d-4a9d-8d5e-7c8b6a4e2f1a`
## 通用响应格式
所有响应都遵循以下 JSON 格式:
```json
{
"type": "响应类型",
"success": true/false,
"message": "可选的消息描述",
...
}
```
## API 列表
### 1. WiFi 扫描
扫描周围可用的 WiFi 网络。
#### 请求
```json
{
"command": "scanWiFi"
}
```
#### 响应
成功时:
```json
{
"type": "scanWiFiResult",
"success": true,
"count": 3,
"networks": [
{
"ssid": "WiFi名称1",
"rssi": -45,
"channel": 6,
"encryption": 3
},
{
"ssid": "WiFi名称2",
"rssi": -60,
"channel": 11,
"encryption": 4
}
]
}
```
失败时:
```json
{
"type": "scanWiFiResult",
"success": false,
"message": "未扫描到任何WiFi网络",
"networks": [],
"count": 0
}
```
#### 字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `ssid` | string | WiFi 网络名称 |
| `rssi` | number | 信号强度dBm值越大信号越强 |
| `channel` | number | WiFi 通道号1-13 |
| `encryption` | number | 加密类型:<br>0 = 开放<br>1 = WEP<br>2 = WPA_PSK<br>3 = WPA2_PSK<br>4 = WPA_WPA2_PSK |
#### 示例
```javascript
// 发送扫描命令
const scanCommand = JSON.stringify({ command: "scanWiFi" });
await characteristic.writeValue(scanCommand);
// 接收响应
characteristic.addEventListener('characteristicvaluechanged', (event) => {
const response = JSON.parse(event.target.value);
if (response.type === "scanWiFiResult") {
console.log(`扫描到 ${response.count} 个网络`);
response.networks.forEach(network => {
console.log(`SSID: ${network.ssid}, 信号: ${network.rssi} dBm`);
});
}
});
```
---
### 2. 获取已保存的 WiFi 网络
获取设备中已保存的 WiFi 网络列表。
#### 请求
```json
{
"command": "getSavedNetworks"
}
```
#### 响应
成功时:
```json
{
"type": "savedNetworks",
"success": true,
"count": 2,
"networks": [
{
"ssid": "MyWiFi"
},
{
"ssid": "OfficeWiFi"
}
]
}
```
无保存网络时:
```json
{
"type": "savedNetworks",
"success": true,
"count": 0,
"networks": []
}
```
#### 字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `ssid` | string | 已保存的 WiFi 网络名称(不包含密码) |
#### 示例
```javascript
// 发送获取命令
const getCommand = JSON.stringify({ command: "getSavedNetworks" });
await characteristic.writeValue(getCommand);
// 接收响应
characteristic.addEventListener('characteristicvaluechanged', (event) => {
const response = JSON.parse(event.target.value);
if (response.type === "savedNetworks") {
console.log(`已保存 ${response.count} 个网络`);
response.networks.forEach(network => {
console.log(`SSID: ${network.ssid}`);
});
}
});
```
---
### 3. 配置 WiFi
设置 WiFi 网络配置。
#### 请求
```json
{
"command": "setWiFiConfig",
"ssid": "WiFi名称",
"password": "WiFi密码"
}
```
#### 响应
成功时:
```json
{
"type": "wifiConfigResult",
"success": true,
"message": "WiFi配置成功"
}
```
失败时:
```json
{
"type": "wifiConfigResult",
"success": false,
"message": "错误描述"
}
```
#### 错误说明
| 错误信息 | 原因 | 解决方案 |
|---------|--------|---------|
| `未扫描到任何WiFi网络请检查设备位置` | 设备扫描不到任何 WiFi 网络 | 将设备移到更靠近路由器的位置 |
| `未找到目标WiFi网络请检查WiFi名称是否正确` | 扫描结果中不存在目标 WiFi | 检查 WiFi 名称是否拼写正确,确保设备在 WiFi 覆盖范围内 |
| `目标WiFi信号过弱请将设备靠近路由器` | 目标 WiFi 信号强度低于阈值(-200 dBm | 将设备移到更靠近路由器的位置 |
| `WiFi配置失败请检查密码是否正确` | 密码错误或连接超时 | 检查 WiFi 密码是否正确,确保设备在 WiFi 覆盖范围内 |
#### 字段说明
| 字段 | 类型 | 必需 | 说明 |
|------|------|--------|------|
| `ssid` | string | ✅ | WiFi 网络名称(最多 31 字符) |
| `password` | string | ✅ | WiFi 密码(最多 63 字符) |
#### 示例
```javascript
// 发送配置命令
const configCommand = JSON.stringify({
command: "setWiFiConfig",
ssid: "MyWiFi",
password: "mypassword"
});
await characteristic.writeValue(configCommand);
// 接收响应
characteristic.addEventListener('characteristicvaluechanged', (event) => {
const response = JSON.parse(event.target.value);
if (response.type === "wifiConfigResult") {
if (response.success) {
console.log("WiFi 配置成功");
} else {
console.log("WiFi 配置失败:", response.message);
}
}
});
```
---
### 4. 查询设备状态
查询设备的当前状态,包括 WiFi 连接状态、设备 ID 等。
#### 请求
```json
{
"command": "queryStatus"
}
```
#### 响应
```json
{
"type": "deviceStatus",
"success": true,
"deviceId": 1001,
"wifiConfigured": true,
"wifiConnected": true,
"ipAddress": "192.168.137.241"
}
```
#### 字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `deviceId` | number | 设备 ID1000-1999 |
| `wifiConfigured` | boolean | 是否已配置 WiFi |
| `wifiConnected` | boolean | WiFi 是否已连接 |
| `ipAddress` | string | 设备 IP 地址(已连接时) |
#### 示例
```javascript
// 发送查询命令
const queryCommand = JSON.stringify({ command: "queryStatus" });
await characteristic.writeValue(queryCommand);
// 接收响应
characteristic.addEventListener('characteristicvaluechanged', (event) => {
const response = JSON.parse(event.target.value);
if (response.type === "deviceStatus") {
console.log(`设备 ID: ${response.deviceId}`);
console.log(`WiFi 已配置: ${response.wifiConfigured}`);
console.log(`WiFi 已连接: ${response.wifiConnected}`);
console.log(`IP 地址: ${response.ipAddress}`);
}
});
```
---
### 5. 设置设备 ID
设置设备的唯一标识 ID。
#### 请求
```json
{
"command": "setDeviceId",
"newDeviceId": 1001
}
```
#### 响应
成功时:
```json
{
"type": "setDeviceIdResult",
"success": true,
"message": "设备ID设置成功",
"newDeviceId": 1001
}
```
失败时:
```json
{
"type": "setDeviceIdResult",
"success": false,
"message": "设备ID超出范围有效范围: 1000-1999"
}
```
#### 字段说明
| 字段 | 类型 | 必需 | 说明 |
|------|------|--------|------|
| `newDeviceId` | number | ✅ | 新设备 ID1000-1999 |
---
### 6. 启动持续发送
启动雷达数据的持续发送模式。
#### 请求
```json
{
"command": "startContinuousSend",
"interval": 200
}
```
#### 响应
```json
{
"type": "startContinuousSendResult",
"success": true,
"message": "已启动持续发送模式",
"interval": 200
}
```
#### 字段说明
| 字段 | 类型 | 必需 | 说明 |
|------|------|--------|------|
| `interval` | number | ❌ | 发送间隔(毫秒),默认 200ms范围 100-10000 |
---
### 7. 停止持续发送
停止雷达数据的持续发送模式。
#### 请求
```json
{
"command": "stopContinuousSend"
}
```
#### 响应
```json
{
"type": "stopContinuousSendResult",
"success": true,
"message": "已停止持续发送模式"
}
```
---
### 8. 查询雷达数据
查询当前的雷达传感器数据。
#### 请求
```json
{
"command": "queryRadarData"
}
```
#### 响应
```json
{
"type": "radarData",
"success": true,
"deviceId": 1001,
"timestamp": 1234567890,
"presence": 1,
"heartRate": 75.5,
"breathRate": 16.2,
"motion": 0,
"heartbeatWaveform": 120,
"breathingWaveform": 85,
"sleepState": 0
}
```
#### 字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `timestamp` | number | 时间戳(毫秒) |
| `presence` | number | 是否检测到人体0=无1=有) |
| `heartRate` | number | 心率(次/分钟) |
| `breathRate` | number | 呼吸率(次/分钟) |
| `motion` | number | 运动状态0=静止1=轻微2=明显) |
| `heartbeatWaveform` | number | 心跳波形值 |
| `breathingWaveform` | number | 呼吸波形值 |
| `sleepState` | number | 睡眠状态0=清醒1=浅睡2=深睡3=REM |
---
### 9. 回显测试
用于测试 BLE 连接是否正常。
#### 请求
```json
{
"command": "echo",
"content": "测试内容"
}
```
#### 响应
```json
{
"type": "echoResponse",
"originalContent": "测试内容",
"receivedSuccessfully": true
}
```
---
## 错误响应
当请求失败时,设备会返回错误响应:
```json
{
"type": "error",
"message": "错误描述",
"receivedData": "原始请求数据"
}
```
## 连接流程
1. 扫描并连接 BLE 设备
2. 开启 notify 订阅
3. 发送 `queryStatus` 命令获取设备状态
4. 根据需要配置 WiFi 或查询雷达数据
## 注意事项
1. **分包传输**: 所有 JSON 数据都会被分包发送(每包 20 字节),客户端需要正确拼接
2. **UTF-8 编码**: 确保使用 UTF-8 编码处理中文字符
3. **超时处理**: 建议为每个命令设置超时时间(推荐 5-10 秒)
4. **设备 ID 范围**: 有效范围为 1000-1999
5. **WiFi 限制**: 仅支持 2.4GHz WiFi 网络
6. **最大保存网络数**: 最多保存 10 个 WiFi 配置
## 版本历史
| 版本 | 日期 | 说明 |
|------|------|------|
| 1.0 | 2025-02-03 | 初始版本,包含所有基础 API |

0
LVEDO.cpp Normal file
View File

259
README.md Normal file
View File

@@ -0,0 +1,259 @@
# Rader_Success_5 - ESP32 雷达数据采集与传输系统
## 项目简介
本项目是一个基于 ESP32 的智能雷达数据采集与传输系统,通过 R60ABD1 雷达传感器采集人体生命体征数据并通过蓝牙BLE与小程序交互同时支持 WiFi 连接将数据上传到云服务器数据库。
## 主要功能
### 1. 雷达数据采集
- **人体存在检测**:实时检测区域内是否有人
- **心率监测**监测人体心率BPM
- **呼吸率监测**:监测人体呼吸频率
- **睡眠监测**:监测睡眠状态(清醒/浅睡/深睡/REM
- **运动检测**:检测人体运动状态
- **距离测量**:测量人体与设备的距离
- **睡眠质量评分**:提供睡眠质量评估
### 2. 蓝牙通信BLE
- 设备配置与管理
- WiFi 网络扫描与配置
- 设备状态查询
- 雷达数据实时查询
- 持续数据发送模式
- 设备 ID 设置
### 3. WiFi 连接
- 自动 WiFi 连接与重连
- 支持多 WiFi 网络配置(最多 10 个)
- 网络信号强度检测
- 网络状态监控
### 4. 数据上传
- 将雷达数据上传到 InfluxDB 云数据库
- 支持每日数据汇总
- 支持睡眠数据专项上传
## 硬件配置
### 主控芯片
- **ESP32**:双核微控制器,支持 WiFi 和 BLE
### 雷达传感器
- **型号**R60ABD1
- **通信接口**UART
- **检测范围**:人体存在、心率、呼吸率、睡眠状态等
### 引脚定义
| 引脚 | 功能 | 说明 |
|------|------|------|
| GPIO 0 | Boot 按钮 | 配置清除按钮 |
| GPIO 48 | 网络状态 LED | 指示网络连接状态 |
| GPIO 4 | 配置清除 LED | 指示配置清除状态 |
| GPIO 8 | 自定义 GPIO | 用户自定义功能 |
| GPIO 9 | 自定义 GPIO | 用户自定义功能 |
### LED 状态指示
| 状态 | LED 行为 | 说明 |
|------|----------|------|
| 初始化/未连接 | 慢闪1秒间隔 | 正在初始化或未连接网络 |
| 连接中 | 快闪200ms间隔 | 正在连接 WiFi |
| 已连接 | 呼吸灯 | WiFi 已连接 |
| 断开连接 | 慢闪1秒间隔 | WiFi 断开连接 |
## 软件架构
### 核心模块
#### 1. RadarManager雷达管理器
- 雷达数据采集与解析
- UART 通信管理
- 数据队列管理
- BLE 数据发送
#### 2. WiFiManagerWiFi 管理器)
- WiFi 网络扫描
- WiFi 连接管理
- 配置存储与加载
- 自动重连机制
#### 3. 主程序main.cpp
- 任务调度与管理
- LED 状态控制
- 按钮监控
- 系统初始化
### FreeRTOS 任务
| 任务名称 | 功能 | 优先级 |
|----------|------|--------|
| uartProcessTask | UART 数据处理 | 高 |
| radarDataTask | 雷达数据处理 | 高 |
| bleSendTask | BLE 数据发送 | 中 |
| vitalSendTask | 生命体征数据发送 | 中 |
| wifiMonitorTask | WiFi 状态监控 | 中 |
| ledControlTask | LED 状态控制 | 低 |
| bootButtonMonitorTask | 按钮监控 | 低 |
| configClearLedTask | 配置清除 LED 控制 | 低 |
## BLE API 文档
详细的 BLE API 文档请参考 [BLE_API.md](BLE_API.md)
### 主要 API 命令
- `scanWiFi` - 扫描 WiFi 网络
- `getSavedNetworks` - 获取已保存的 WiFi 网络
- `setWiFiConfig` - 配置 WiFi 网络
- `queryStatus` - 查询设备状态
- `setDeviceId` - 设置设备 ID
- `startContinuousSend` - 启动持续发送
- `stopContinuousSend` - 停止持续发送
- `queryRadarData` - 查询雷达数据
- `echo` - 回显测试
### BLE 连接参数
- **服务 UUID**: `a8c1e5c0-3d5d-4a9d-8d5e-7c8b6a4e2f1a`
- **特征值 UUID**: `beb5483e-36e1-4688-b7f5-ea07361b26a8`
- **分包大小**: 20 字节/包
- **数据格式**: JSONUTF-8 编码)
## 数据结构
### 传感器数据SensorData
```cpp
typedef struct {
float breath_rate; // 呼吸率
float heart_rate; // 心率
uint8_t breath_valid; // 呼吸率有效标志
uint8_t heart_valid; // 心率有效标志
uint8_t presence; // 存在状态
uint8_t motion; // 运动状态
int heartbeat_waveform; // 心跳波形
int breathing_waveform; // 呼吸波形
uint16_t distance; // 距离
uint8_t sleep_state; // 睡眠状态
uint32_t sleep_time; // 睡眠时长
uint8_t sleep_score; // 睡眠评分
// ... 更多字段
} SensorData;
```
### 睡眠状态定义
| 值 | 状态 | 说明 |
|----|------|------|
| 0 | 清醒 | 处于清醒状态 |
| 1 | 浅睡 | 处于浅度睡眠 |
| 2 | 深睡 | 处于深度睡眠 |
| 3 | REM | 快速眼动睡眠 |
## 配置管理
### 设备 ID
- **有效范围**1000-1999
- **存储位置**ESP32 FlashPreferences
- **默认值**0000未设置
### WiFi 配置
- **最大保存数量**10 个网络
- **最小信号强度**-200 dBm
- **连接超时**15 秒
- **重连间隔**2 秒
### 配置清除
- **触发方式**:启动时按住 Boot 按钮 3 秒
- **清除内容**:所有 WiFi 配置和设备 ID
- **LED 指示**:呼吸灯模式
## 编译与烧录
### 开发环境
- **IDE**Arduino IDE 2.x
- **框架**Arduino ESP32
- **编译器**GCC for ESP32
### 依赖库
- `Arduino.h` - Arduino 核心库
- `WiFi.h` - WiFi 功能库
- `BLEDevice.h` - BLE 功能库
- `ArduinoJson.h` - JSON 处理库
- `Preferences.h` - Flash 存储库
- `HTTPClient.h` - HTTP 客户端库
### 编译步骤
1. 使用 platformIO ,在vscode中打开项目
3. 选择正确的串口
4. 点击"上传"按钮编译并烧录
## 使用说明
### 首次使用
1. 上电启动设备
2. 通过手机蓝牙连接设备
3. 使用小程序扫描 WiFi 网络
4. 配置 WiFi 连接信息
5. 设置设备 ID可选
6. 设备自动连接 WiFi 并开始上传数据
### 日常使用
1. 设备自动连接已保存的 WiFi
2. 雷达数据实时采集
3. 数据自动上传到云服务器
4. 可通过 BLE 查询设备状态和雷达数据
### 配置清除
1. 断电重启设备
2. 按住 Boot 按钮不放
3. 上电后保持按住 3 秒
4. 配置清除 LED 呼吸闪烁
5. 释放按钮,配置已清除
## 项目结构
```
Rader_Success_5/
├── src/
│ ├── main.cpp # 主程序
│ ├── radar_manager.cpp # 雷达管理器实现
│ ├── radar_manager.h # 雷达管理器头文件
│ ├── wifi_manager.cpp # WiFi 管理器实现
│ └── wifi_manager.h # WiFi 管理器头文件
├── BLE_API.md # BLE API 文档
├── 传感器数据.txt # 传感器数据说明
└── README.md # 项目说明文档
```
## 技术特点
1. **模块化设计**:雷达管理和 WiFi 管理分离,便于维护和扩展
2. **多任务处理**:使用 FreeRTOS 实现多任务并发处理
3. **队列通信**:使用 FreeRTOS 队列实现任务间数据传递
4. **流控机制**BLE 数据发送采用流控,避免数据拥塞
5. **自动重连**WiFi 断开后自动重连,提高系统稳定性
6. **Flash 存储**:使用 Preferences 持久化存储配置信息
7. **LED 状态指示**:直观的 LED 状态显示,便于用户了解设备状态
## 注意事项
1. **WiFi 限制**:仅支持 2.4GHz WiFi 网络
2. **BLE 连接**:一次只能连接一个 BLE 客户端
3. **数据上传**:需要确保 WiFi 连接正常才能上传数据
4. **设备 ID**必须在有效范围内1000-1999
5. **配置清除**:清除配置后需要重新配置 WiFi 和设备 ID
6. **雷达安装**:建议安装在床铺正上方,距离 1-2 米
## 版本历史
| 版本 | 日期 | 说明 |
|------|------|------|
| 5.0 | 2025-02-28 | 第5版重构代码结构添加雷达和 WiFi 管理器模块 |
## 许可证
本项目仅供学习和研究使用。
## 联系方式
如有问题或建议,请联系项目维护者。
---
**项目地址**http://lmhrt.cn:6771/ming/Rader_Success_5.git

37
include/README Normal file
View File

@@ -0,0 +1,37 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the convention is to give header files names that end with `.h'.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

46
lib/README Normal file
View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into the executable file.
The source code of each library should be placed in a separate directory
("lib/your_library_name/[Code]").
For example, see the structure of the following example libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
Example contents of `src/main.c` using Foo and Bar:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
The PlatformIO Library Dependency Finder will find automatically dependent
libraries by scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

17
platformio.ini Normal file
View File

@@ -0,0 +1,17 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:freenove_esp32_s3_wroom]
platform = espressif32
board = freenove_esp32_s3_wroom
framework = arduino
lib_deps =
bblanchon/ArduinoJson@^7.4.2
emelianov/modbus-esp8266@^4.1.0

620
src/main.cpp Normal file
View File

@@ -0,0 +1,620 @@
#include <Arduino.h>
#include <WiFi.h>
#include <esp_task_wdt.h>
#include <ArduinoJson.h>
#include <Preferences.h>
#include "wifi_manager.h"
#include "radar_manager.h"
// ESP32 GPIO控制演示
#define BOOT_BUTTON_PIN 0 // Boot按钮引脚
#define NETWORK_LED_PIN 5 // 网络状态LED指示灯开发板48引脚雷达板5引脚
#define CONFIG_CLEAR_PIN 4 // 配置清除指示灯
#define GPIO8 8 // 自定义GPIO8
#define GPIO9 9 // 自定义GPIO9
uint8_t WiFi_Connect_First_bit = 1; // WiFi首次连接标志位
// 配置清除指示灯状态枚举
enum ConfigClearStatus {
CONFIG_NORMAL, // 正常运行 - LOW
CONFIG_PREPARING, // 准备清除 - HIGH
CONFIG_CLEARING, // 清除过程中 - 呼吸灯
CONFIG_COMPLETED // 清除完成 - 快速闪烁3次
};
NetworkStatus currentNetworkStatus = NET_INITIAL; // 当前网络状态
unsigned long lastBlinkTime = 0; // 上次LED闪烁时间
bool ledState = false; // LED状态
int breatheValue = 0; // 呼吸灯当前亮度值
bool breatheIncreasing = true; // 呼吸灯是否在增加亮度
ConfigClearStatus currentConfigClearStatus = CONFIG_NORMAL; // 当前配置清除状态
unsigned long lastConfigBlinkTime = 0; // 上次配置清除LED闪烁时间
bool configLedState = false; // 配置清除LED状态
int configBreatheValue = 0; // 配置清除呼吸灯当前亮度值
bool configBreatheIncreasing = true; // 配置清除呼吸灯是否在增加亮度
const int SLOW_BLINK_INTERVAL = 1000; // 慢闪间隔(毫秒)
const int FAST_BLINK_INTERVAL = 200; // 快闪间隔(毫秒)
const int BREATHE_INTERVAL = 40; // 呼吸灯更新间隔(毫秒)
const int BREATHE_MIN = 0; // 呼吸灯最小亮度值
const int BREATHE_MAX = 155; // 呼吸灯最大亮度值
const int BREATHE_STEP = 5; // 呼吸灯亮度步进值
const uint16_t MIN_DEVICE_ID = 1000; // 最小设备ID
const uint16_t MAX_DEVICE_ID = 1999; // 最大设备ID
const unsigned long CLEAR_CONFIG_DURATION = 3000; // 清除配置持续时间(毫秒)
Preferences preferences; // Flash存储对象
WiFiManager wifiManager; // WiFi管理器对象
uint16_t currentDeviceId = 0000; // 当前设备ID
bool clearConfigRequested = false; // 清除配置请求标志
bool forceLedOff = false; // 强制关闭LED标志
void configClearLedTask(void *parameter);
void bootButtonMonitorTask(void *parameter);
void checkBootButton();
void clearStoredConfig();
void ledControlTask(void *parameter);
void setNetworkStatus(NetworkStatus status);
void wifiMonitorTask(void *parameter);
void WiFiEvent(WiFiEvent_t event);
void loadDeviceId();
void saveDeviceId();
String getFieldNameByProtocolId(int protocolId);
/**
* @brief 根据协议ID获取字段名称
* 将协议ID映射到对应的字段名称用于数据序列化和反序列化
* @param protocolId 协议ID
* @return 对应的字段名称字符串
*/
String getFieldNameByProtocolId(int protocolId) {
switch(protocolId) {
case 1:
return "heartRate";
case 2:
return "breathingRate";
case 13:
return "personDetected";
case 14:
return "humanActivity";
case 15:
return "humanDistance";
case 16:
return "humanPosition";
case 17:
return "sleepState";
default:
return "unknown";
}
}
/**
* @brief 检查Boot按钮状态
* 在启动时检查Boot按钮是否被按下如果按下则进入配置清除流程
*/
void checkBootButton() {
Serial.println("🔍 检查Boot按钮状态...");
pinMode(BOOT_BUTTON_PIN, INPUT_PULLUP);
delay(10);
int buttonState = digitalRead(BOOT_BUTTON_PIN);
Serial.printf("📊 Boot按钮状态: %s\n", buttonState == LOW ? "按下" : "释放");
if (buttonState == LOW) {
Serial.println("⚠️ 检测到Boot按钮按下请释放按钮后继续启动");
Serial.println("⏰ 等待按钮释放...");
while (digitalRead(BOOT_BUTTON_PIN) == LOW) {
delay(100);
}
Serial.println("✅ Boot按钮已释放正常启动");
} else {
Serial.println("✅ Boot按钮未按下正常启动");
}
}
/**
* @brief 加载设备ID
* 从Flash中读取保存的设备ID
*/
void loadDeviceId() {
currentDeviceId = preferences.getUShort("deviceId", 1001);
Serial.printf("从Flash加载设备ID: %u\n", currentDeviceId);
}
/**
* @brief 保存设备ID
* 将设备ID保存到Flash中
*/
void saveDeviceId() {
preferences.putUShort("deviceId", currentDeviceId);
Serial.printf("设备ID已保存到Flash: %u\n", currentDeviceId);
}
/**
* @brief 清除存储的配置
* 清除Flash中保存的所有配置包括设备ID和WiFi配置
*/
void clearStoredConfig() {
Serial.println("🧹 开始清除存储的配置...");
uint16_t oldDeviceId = preferences.getUShort("deviceId", 0);
preferences.remove("deviceId");
preferences.remove("wifi_first");
wifiManager.clearAllConfigs();
Serial.println("✅ 配置已清除完成");
Serial.printf("🗑️ 被清除的设备ID: %u\n", oldDeviceId);
currentDeviceId = 1001;
WiFi_Connect_First_bit = 1;
WiFi.disconnect(true);
setNetworkStatus(NET_DISCONNECTED);
Serial.println("🔄 已清除Flash与内存中的配置请重新配置WiFi和设备ID");
if (deviceConnected) {
sendStatusToBLE();
}
}
/**
* @brief 配置清除LED控制任务
* 根据配置清除状态控制CONFIG_CLEAR_PIN引脚的LED显示
* @param parameter 任务参数(未使用)
*/
void configClearLedTask(void *parameter) {
while (1) {
switch (currentConfigClearStatus) {
case CONFIG_NORMAL:
analogWrite(CONFIG_CLEAR_PIN, 0);
break;
case CONFIG_PREPARING:
analogWrite(CONFIG_CLEAR_PIN, 255);
break;
case CONFIG_CLEARING:
if (millis() - lastConfigBlinkTime >= BREATHE_INTERVAL) {
analogWrite(CONFIG_CLEAR_PIN, configBreatheValue);
if (configBreatheIncreasing) {
configBreatheValue += 5;
if (configBreatheValue >= BREATHE_MAX) {
configBreatheValue = BREATHE_MAX;
configBreatheIncreasing = false;
}
} else {
configBreatheValue -= 5;
if (configBreatheValue <= BREATHE_MIN) {
configBreatheValue = BREATHE_MIN;
configBreatheIncreasing = true;
}
}
lastConfigBlinkTime = millis();
}
break;
case CONFIG_COMPLETED:
if (millis() - lastConfigBlinkTime >= FAST_BLINK_INTERVAL) {
configLedState = !configLedState;
digitalWrite(CONFIG_CLEAR_PIN, configLedState ? HIGH : LOW);
lastConfigBlinkTime = millis();
static int blinkCount = 0;
blinkCount++;
if (blinkCount >= 6) {
blinkCount = 0;
currentConfigClearStatus = CONFIG_NORMAL;
digitalWrite(CONFIG_CLEAR_PIN, LOW);
}
}
break;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
/**
* @brief BOOT按钮监控任务
* 持续监控BOOT按钮状态检测长按3秒事件并触发配置清除
* @param parameter 任务参数(未使用)
*/
void bootButtonMonitorTask(void *parameter) {
Serial.println("🔍 启动BOOT按钮监控任务...");
pinMode(BOOT_BUTTON_PIN, INPUT_PULLUP);
unsigned long buttonPressStartTime = 0;
bool buttonPressed = false;
while (1) {
int buttonState = digitalRead(BOOT_BUTTON_PIN);
if (buttonState == LOW && !buttonPressed) {
buttonPressed = true;
buttonPressStartTime = millis();
Serial.println("⚠️ 检测到BOOT按钮按下长按3秒将清除配置");
currentConfigClearStatus = CONFIG_PREPARING;
}
else if (buttonState == HIGH && buttonPressed) {
if (!clearConfigRequested) {
currentConfigClearStatus = CONFIG_NORMAL;
Serial.println("❌ 按钮释放,取消清除操作");
}
buttonPressed = false;
}
if (buttonPressed && (millis() - buttonPressStartTime >= CLEAR_CONFIG_DURATION)) {
if (!clearConfigRequested) {
clearConfigRequested = true;
forceLedOff = true;
ledcWrite(0, 0);
Serial.println("✅ 长按3秒确认将清除配置");
Serial.println("💡 网络LED已强制熄灭");
clearStoredConfig();
Serial.println("🔄 配置清除完成LED将闪烁3次表示完成...");
analogWrite(CONFIG_CLEAR_PIN, 0);
Serial.println("🔄 系统即将重启...");
vTaskDelay(1000 / portTICK_PERIOD_MS);
ESP.restart();
}
}
vTaskDelay(50 / portTICK_PERIOD_MS);
esp_task_wdt_reset();
}
}
/**
* @brief LED控制任务
* 根据网络状态控制NETWORK_LED_PIN引脚的LED显示
* 支持慢闪、快闪和呼吸灯效果
* @param parameter 任务参数(未使用)
*/
void ledControlTask(void *parameter) {
while (1) {
if (forceLedOff) {
ledcWrite(0, 0);
vTaskDelay(10 / portTICK_PERIOD_MS);
continue;
}
switch (currentNetworkStatus) {
case NET_INITIAL:
case NET_DISCONNECTED:
if (millis() - lastBlinkTime >= SLOW_BLINK_INTERVAL) {
ledState = !ledState;
if(ledState) {
ledcWrite(0, 255);
} else {
ledcWrite(0, 0);
}
lastBlinkTime = millis();
}
break;
case NET_CONNECTING:
if (millis() - lastBlinkTime >= FAST_BLINK_INTERVAL) {
ledState = !ledState;
if(ledState) {
ledcWrite(0, 255);
} else {
ledcWrite(0, 0);
}
lastBlinkTime = millis();
}
break;
case NET_CONNECTED:
if (millis() - lastBlinkTime >= BREATHE_INTERVAL) {
ledcWrite(0, breatheValue);
if (breatheIncreasing) {
breatheValue += BREATHE_STEP;
if (breatheValue >= BREATHE_MAX) {
breatheValue = BREATHE_MAX;
breatheIncreasing = false;
}
} else {
breatheValue -= BREATHE_STEP;
if (breatheValue <= BREATHE_MIN) {
breatheValue = BREATHE_MIN;
breatheIncreasing = true;
}
}
lastBlinkTime = millis();
}
break;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
/**
* @brief 设置网络状态
* 更新当前网络状态,并重置呼吸灯参数
* @param status 网络状态
*/
void setNetworkStatus(NetworkStatus status) {
currentNetworkStatus = status;
if (status == NET_CONNECTED) {
breatheValue = BREATHE_MIN;
breatheIncreasing = true;
}
}
/**
* @brief WiFi事件处理函数
* 处理WiFi连接状态变化事件更新网络状态和LED显示
* @param event WiFi事件类型
*/
void WiFiEvent(WiFiEvent_t event) {
switch (event) {
case ARDUINO_EVENT_WIFI_STA_START:
setNetworkStatus(NET_INITIAL);
break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
setNetworkStatus(NET_CONNECTING);
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
setNetworkStatus(NET_CONNECTED);
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
setNetworkStatus(NET_DISCONNECTED);
break;
case ARDUINO_EVENT_WIFI_STA_STOP:
setNetworkStatus(NET_DISCONNECTED);
break;
}
}
/**
* @brief WiFi监控任务
* 定期更新WiFi管理器状态处理WiFi重连等逻辑
* @param parameter 任务参数(未使用)
*/
void wifiMonitorTask(void *parameter) {
Serial.println("📡 WiFi监控任务启动");
while(1) {
wifiManager.update();
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
/**
* @brief 系统初始化函数
* 初始化所有硬件外设、任务和通信模块
*/
void setup() {
Serial.begin(115200);
checkBootButton();
analogWrite(CONFIG_CLEAR_PIN, 0);
Serial.println("🚀 ESP32-R60ABD1系统启动");
Serial.println("🔧 初始化系统组件...");
pinMode(BOOT_BUTTON_PIN, INPUT);
pinMode(NETWORK_LED_PIN, OUTPUT);
pinMode(CONFIG_CLEAR_PIN, OUTPUT);
pinMode(GPIO8, OUTPUT);
pinMode(GPIO9, OUTPUT);
digitalWrite(CONFIG_CLEAR_PIN, LOW);
digitalWrite(GPIO8, LOW);
digitalWrite(GPIO9, LOW);
digitalWrite(NETWORK_LED_PIN, LOW);
digitalWrite(CONFIG_CLEAR_PIN, LOW);
ledcSetup(0, 5000, 8);
ledcSetup(1, 5000, 8);
ledcAttachPin(NETWORK_LED_PIN, 0);
ledcAttachPin(CONFIG_CLEAR_PIN, 1);
WiFi.onEvent(WiFiEvent);
setNetworkStatus(NET_INITIAL);
esp_task_wdt_init(30, true);
esp_task_wdt_add(NULL);
preferences.begin("radar_data", false);
wifiManager.begin();
Serial.println("💾 加载设备配置...");
loadDeviceId();
Serial.println("🏗️ 初始化雷达管理器...");
initRadarManager();
xTaskCreate(
configClearLedTask,
"Config Clear LED Task",
2048,
NULL,
1,
NULL
);
xTaskCreate(
bootButtonMonitorTask,
"Boot Button Monitor Task",
2048,
NULL,
1,
NULL
);
xTaskCreate(
ledControlTask,
"LED Control Task",
2048,
NULL,
1,
NULL
);
xTaskCreate(
wifiMonitorTask,
"WiFi Monitor Task",
4096,
NULL,
2,
NULL
);
Serial.println("✅ FreeRTOS任务创建成功");
Serial.println("📶 初始化BLE服务...");
String deviceName = "Radar_" + String(currentDeviceId);
BLEDevice::init(deviceName.c_str());
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY
);
pCharacteristic->setCallbacks(new MyCallbacks());
pCharacteristic->addDescriptor(new BLE2902());
pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06);
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
Serial.println(String("BLE已启动设备名称: Radar_") + String(currentDeviceId));
Serial.println("🌐 检查WiFi配置...");
if (wifiManager.getSavedNetworkCount() > 0) {
Serial.printf("💾 检测到 %d 个已保存的WiFi配置尝试连接...\n", wifiManager.getSavedNetworkCount());
if (wifiManager.initializeWiFi()) {
Serial.println("✅ WiFi连接成功");
} else {
Serial.println("❌ WiFi连接失败请通过BLE重新配置");
}
} else {
Serial.println("⚠️ 未检测到WiFi配置请通过BLE进行网络配置");
}
size_t wifi_first_len = preferences.getBytes("wifi_first", &WiFi_Connect_First_bit, sizeof(WiFi_Connect_First_bit));
if (wifi_first_len == sizeof(WiFi_Connect_First_bit)) {
Serial.printf("从Flash读取 WiFi_Connect_First_bit: %u\n", WiFi_Connect_First_bit);
} else {
Serial.println("Flash中无 wifi_first 条目,保留内存中原始值");
}
if(WiFi_Connect_First_bit == 0)
{
unsigned long wifiWaitStart = millis();
unsigned long lastWifiWaitPrint = 0;
const unsigned long WIFI_WAIT_TIMEOUT = 15000;
while (WiFi.status() != WL_CONNECTED && (millis() - wifiWaitStart) < WIFI_WAIT_TIMEOUT) {
if (millis() - lastWifiWaitPrint >= 1000) {
Serial.println("等待WiFi连接...");
lastWifiWaitPrint = millis();
}
yield();
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
Serial.println("WiFi连接成功");
initR60ABD1();
Serial.println("🎉 系统初始化完成,等待雷达数据...");
if (WiFi.status() == WL_CONNECTED) {
Serial.println("🌅 启动时发送睡眠数据到数据库");
sendSleepDataToInfluxDB();
}
}
/**
* @brief 主循环函数
* 处理BLE连接状态和定期发送雷达命令
*/
void loop() {
esp_task_wdt_reset();
if (!deviceConnected && oldDeviceConnected) {
vTaskDelay(500 / portTICK_PERIOD_MS);
pServer->startAdvertising();
Serial.println("开始BLE广播");
oldDeviceConnected = deviceConnected;
}
if (deviceConnected && !oldDeviceConnected) {
oldDeviceConnected = deviceConnected;
}
{
static const uint8_t radar_cmds[][3] = {
{0x84, 0x81, 0x0F},
{0x84, 0x8D, 0x0F},
{0x84, 0x8F, 0x0F},
{0x84, 0x8E, 0x0F},
{0x84, 0x91, 0x0F},
{0x84, 0x92, 0x0F},
{0x84, 0x83, 0x0F},
{0x84, 0x84, 0x0F},
{0x84, 0x85, 0x0F},
{0x84, 0x86, 0x0F},
{0x84, 0x90, 0x0F}
};
const size_t cmdCount = sizeof(radar_cmds) / sizeof(radar_cmds[0]);
static size_t cmdIndex = 0;
static unsigned long lastCmdMillis = 0;
const unsigned long CMD_INTERVAL = 2000UL;
unsigned long now = millis();
if (now - lastCmdMillis >= CMD_INTERVAL) {
sendRadarCommand(radar_cmds[cmdIndex][0], radar_cmds[cmdIndex][1], radar_cmds[cmdIndex][2]);
lastCmdMillis = now;
cmdIndex++;
if (cmdIndex >= cmdCount) cmdIndex = 0;
}
}
processBLEConfig();
esp_task_wdt_reset();
esp_task_wdt_reset();
esp_task_wdt_reset();
}

2582
src/main_backup.cpp.bak Normal file

File diff suppressed because it is too large Load Diff

1727
src/radar_manager.cpp Normal file

File diff suppressed because it is too large Load Diff

235
src/radar_manager.h Normal file
View File

@@ -0,0 +1,235 @@
#ifndef RADAR_MANAGER_H
#define RADAR_MANAGER_H
#include <Arduino.h>
#include <ArduinoJson.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <HTTPClient.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include <Preferences.h>
class WiFiManager;
#define SERVICE_UUID "a8c1e5c0-3d5d-4a9d-8d5e-7c8b6a4e2f1a" // BLE服务UUID
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" // BLE特征值UUID
#define UART_RX_BUFFER_SIZE 4096 // UART接收缓冲区大小
#define QUEUE_SIZE 50 // 队列大小
#define TASK_STACK_SIZE 8192 // 任务堆栈大小
#define FRAME_HEADER1 0x53 // 帧头字节1
#define FRAME_HEADER2 0x59 // 帧头字节2
#define FRAME_TAIL1 0x54 // 帧尾字节1
#define FRAME_TAIL2 0x43 // 帧尾字节2
// 控制字定义
#define CTRL_PRESENCE 0x80 // 人体存在检测
#define CTRL_BREATH 0x81 // 呼吸检测
#define CTRL_SLEEP 0x84 // 睡眠监测
#define CTRL_HEARTRATE 0x85 // 心率监测
// 命令字定义
#define CMD_REPORT 0x80 // 主动上报
#define CMD_QUERY 0x81 // 查询命令
#define CMD_SET 0x82 // 设置命令
// 定义R60ABD1数据结构
typedef struct {
uint8_t present; // 有人/无人状态 (DP1)
uint16_t distance; // 人体距离 (DP3) 单位cm
uint8_t heartRate; // 心率 (DP6) 单位BPM
uint8_t breathRate; // 呼吸率 (DP8) 单位次/分钟
uint8_t heartWave; // 心率波形 (DP7) 数值+128
uint8_t breathWave; // 呼吸波形 (DP10) 数值+128
uint8_t sleepState; // 睡眠状态 (DP12)
uint32_t sleepTime; // 睡眠时长 (DP13) 单位秒
uint8_t sleepScore; // 睡眠质量评分 (DP14)
uint8_t bedEntry; // 入床/离床状态 (DP11)
uint8_t abnormal; // 异常状态 (DP18)
} R60ABD1Data; // R60ABD1雷达数据结构体
typedef struct { // 传感器数据结构体
float breath_rate; // 呼吸率
float heart_rate; // 心率
uint8_t breath_valid; // 呼吸率有效标志
uint8_t heart_valid; // 心率有效标志
uint8_t presence; // 存在状态
uint8_t motion; // 运动状态
int heartbeat_waveform; // 心跳波形
int breathing_waveform; // 呼吸波形
uint16_t distance; // 距离
uint8_t body_movement; // 身体运动
uint8_t breath_status; // 呼吸状态
uint8_t sleep_state; // 睡眠状态
uint32_t sleep_time; // 睡眠时长
uint8_t sleep_score; // 睡眠评分
uint8_t sleep_grade; // 睡眠等级
uint8_t bed_entry; // 入床状态
uint8_t abnormal_state; // 异常状态
uint8_t avg_heart_rate; // 平均心率
uint8_t avg_breath_rate; // 平均呼吸率
uint8_t turn_count; // 翻身次数
uint8_t large_move_ratio; // 大幅运动比例
uint8_t small_move_ratio; // 小幅运动比例
int16_t pos_x; // X坐标
int16_t pos_y; // Y坐标
int16_t pos_z; // Z坐标
int8_t breath_waveform[5]; // 呼吸波形数组
int8_t heart_waveform[5]; // 心跳波形数组
uint16_t deep_sleep_time; // 深度睡眠时长
uint16_t light_sleep_time; // 浅度睡眠时长
uint16_t awake_time; // 清醒时长
uint16_t sleep_total_time; // 总睡眠时长
uint8_t deep_sleep_ratio; // 深度睡眠比例
uint8_t light_sleep_ratio; // 浅度睡眠比例
uint8_t awake_ratio; // 清醒比例
uint8_t turnover_count; // 翻身计数
uint8_t struggle_alert; // 挣扎警报
uint8_t no_one_alert; // 无人警报
uint8_t bed_status; // 床状态
uint8_t bed_Out_Time; // 离床时间
uint8_t apnea_count; // 呼吸暂停次数
} SensorData; // 传感器数据结构体
typedef struct { // 相位数据结构体
int heartbeat_waveform; // 心跳波形
int breathing_waveform; // 呼吸波形
} PhaseData; // 相位数据结构体
typedef struct { // 生命体征数据结构体
float heart_rate; // 心率
float breath_rate; // 呼吸率
uint8_t presence; // 存在状态
uint8_t motion; // 运动状态
uint16_t distance; // 距离
uint8_t sleep_state; // 睡眠状态
uint8_t sleep_score; // 睡眠评分
uint8_t body_movement; // 身体运动
uint8_t breath_status; // 呼吸状态
uint32_t sleep_time; // 睡眠时长
uint8_t bed_entry; // 入床状态
uint8_t abnormal_state; // 异常状态
uint8_t avg_heart_rate; // 平均心率
uint8_t avg_breath_rate; // 平均呼吸率
uint8_t turn_count; // 翻身次数
uint8_t large_move_ratio; // 大幅运动比例
uint8_t small_move_ratio; // 小幅运动比例
int16_t pos_x; // X坐标
int16_t pos_y; // Y坐标
int16_t pos_z; // Z坐标
uint16_t deep_sleep_time; // 深度睡眠时长
uint16_t light_sleep_time; // 浅度睡眠时长
uint16_t awake_time; // 清醒时长
uint16_t sleep_total_time; // 总睡眠时长
uint8_t deep_sleep_ratio; // 深度睡眠比例
uint8_t light_sleep_ratio; // 浅度睡眠比例
uint8_t awake_ratio; // 清醒比例
uint8_t turnover_count; // 翻身计数
uint8_t struggle_alert; // 挣扎警报
uint8_t no_one_alert; // 无人警报
uint8_t bed_status; // 床状态
uint8_t apnea_count; // 呼吸暂停次数
int heartbeat_waveform; // 心跳波形
int breathing_waveform; // 呼吸波形
} VitalData; // 生命体征数据结构体
typedef struct { // 上次发送数据结构体
float heart_rate; // 心率
float breath_rate; // 呼吸率
uint8_t presence; // 存在状态
uint8_t motion; // 运动状态
uint8_t sleep_state; // 睡眠状态
} LastSentData; // 上次发送数据结构体
class BLEFlowController { // BLE流控制器类
private:
size_t maxBytesPerSecond; // 最大每秒发送字节数
size_t bytesSent; // 已发送字节数
unsigned long lastResetTime; // 上次重置时间
unsigned long lastSendTime; // 上次发送时间
public:
BLEFlowController(size_t maxBps);
bool canSend(size_t dataSize);
bool check();
void recordSend(size_t dataSize);
void reset();
};
extern SensorData sensorData; // 传感器数据
extern HardwareSerial mySerial1; // 硬件串口1
extern QueueHandle_t phaseDataQueue; // 相位数据队列
extern QueueHandle_t vitalDataQueue; // 生命体征数据队列
extern QueueHandle_t uartQueue; // UART数据队列
extern TaskHandle_t bleSendTaskHandle; // BLE发送任务句柄
extern TaskHandle_t vitalSendTaskHandle; // 生命体征发送任务句柄
extern TaskHandle_t uartProcessTaskHandle; // UART处理任务句柄
extern BLEServer* pServer; // BLE服务器指针
extern BLECharacteristic* pCharacteristic; // BLE特征值指针
extern bool deviceConnected; // 设备连接状态
extern bool oldDeviceConnected; // 旧设备连接状态
extern String receivedData; // 接收到的数据
extern String completeData; // 完整数据
extern unsigned long lastReceiveTime; // 上次接收数据时间
extern bool continuousSendEnabled; // 持续发送使能标志
extern unsigned long continuousSendInterval; // 持续发送间隔
extern BLEFlowController bleFlow; // BLE流控制器
extern unsigned long lastSensorUpdate; // 上次传感器更新时间
extern LastSentData lastSentData; // 上次发送的数据
extern unsigned long lastCheckTime; // 上次检测时间
extern uint16_t currentDeviceId; // 当前设备ID
extern Preferences preferences; // Flash存储对象
extern WiFiManager wifiManager; // WiFi管理器
void initRadarManager();
void initR60ABD1();
bool parseR60ABD1Frame(uint8_t *frame, uint16_t frameLen);
int16_t parseSignedCoordinate(uint16_t raw_value);
void sendRadarCommand(uint8_t ctrl, uint8_t cmd, uint8_t value);
void IRAM_ATTR serialRxCallback();
void bleSendTask(void *parameter);
void vitalSendTask(void *parameter);
void radarDataTask(void *parameter);
void uartProcessTask(void *parameter);
void sendDataInChunks(const String& data);
void sendJSONDataToBLE(const String& jsonData);
bool sendCustomJSONData(const String& jsonType, const String& jsonString);
void sendRadarDataToBLE();
bool processQueryRadarData(JsonDocument& doc);
bool processStartContinuousSend(JsonDocument& doc);
bool processStopContinuousSend(JsonDocument& doc);
void processBLEConfig();
bool processSetDeviceId(JsonDocument& doc);
bool processQueryStatus(JsonDocument& doc);
bool processWiFiConfigCommand(JsonDocument& doc);
bool processScanWiFi(JsonDocument& doc);
bool processGetSavedNetworks(JsonDocument& doc);
bool processEchoRequest(JsonDocument& doc);
void sendRawEchoResponse(const String& rawData);
void sendStatusToBLE();
void loadDeviceId();
void saveDeviceId();
void sendDailyDataToInfluxDB(String dailyDataLine);
void sendSleepDataToInfluxDB();
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer);
void onDisconnect(BLEServer* pServer);
};
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic);
};
#endif

782
src/wifi_manager.cpp Normal file
View File

@@ -0,0 +1,782 @@
#include "wifi_manager.h"
// 外部变量和函数声明
extern bool deviceConnected;
void sendJSONDataToBLE(const String& jsonData);
void setNetworkStatus(NetworkStatus status);
/**
* @brief WiFi管理器构造函数
* 初始化WiFi管理器的成员变量
*/
WiFiManager::WiFiManager() {
savedNetworkCount = 0;
currentState = WIFI_IDLE;
lastReconnectAttempt = 0;
isScanning = false;
}
/**
* @brief 初始化WiFi管理器
* 开启Preferences存储并加载保存的WiFi配置
*/
void WiFiManager::begin() {
preferences.begin("wifi_manager", false);
loadWiFiConfigs();
}
/**
* @brief 加载保存的WiFi配置
* 从Flash中读取之前保存的WiFi网络配置
* @return 是否成功加载到配置
*/
bool WiFiManager::loadWiFiConfigs() {
savedNetworkCount = 0;
for (int i = 0; i < MAX_WIFI_NETWORKS; i++) {
String key = "wifi_" + String(i);// 构建WiFi配置键名
String configStr = preferences.getString(key.c_str(), "");// 从Preferences中读取字符串
if (configStr.length() > 0) {
JsonDocument doc;
DeserializationError error = deserializeJson(doc, configStr);// 反序列化JSON字符串
if (!error) {
const char* ssid = doc["ssid"];
const char* password = doc["password"];
if (ssid && password) {
strncpy(savedNetworks[savedNetworkCount].ssid, ssid, 31);// 保存SSID
savedNetworks[savedNetworkCount].ssid[31] = '\0';
strncpy(savedNetworks[savedNetworkCount].password, password, 63);
savedNetworks[savedNetworkCount].password[63] = '\0';
savedNetworkCount++;
Serial.printf("📶 加载WiFi配置 %d: %s\n", savedNetworkCount, ssid);
}
}
}
}
Serial.printf("✅ 共加载 %d 个WiFi配置\n", savedNetworkCount);
return savedNetworkCount > 0;
}
/**
* @brief 保存WiFi配置
* 将WiFi网络配置保存到Flash中
* @param ssid WiFi网络名称
* @param password WiFi网络密码
* @return 是否保存成功
*/
bool WiFiManager::saveWiFiConfig(const char* ssid, const char* password) {
// 检查是否已存在该网络配置
for (int i = 0; i < savedNetworkCount; i++) {
// 已存在相同的SSID
if (strcmp(savedNetworks[i].ssid, ssid) == 0) {
// 更新现有配置
strncpy(savedNetworks[i].password, password, 63);// 更新密码
savedNetworks[i].password[63] = '\0';
JsonDocument doc;
doc["ssid"] = ssid;
doc["password"] = password;
String configStr;
serializeJson(doc, configStr);// 序列化JSON文档
String key = "wifi_" + String(i);
preferences.putString(key.c_str(), configStr);// 保存到Preferences
Serial.printf("🔄 更新WiFi配置: %s\n", ssid);
return true;
}
}
// 未找到相同的SSID添加新的配置
if (savedNetworkCount < MAX_WIFI_NETWORKS) {
strncpy(savedNetworks[savedNetworkCount].ssid, ssid, 31);// 保存SSID
savedNetworks[savedNetworkCount].ssid[31] = '\0';
strncpy(savedNetworks[savedNetworkCount].password, password, 63);// 保存密码
savedNetworks[savedNetworkCount].password[63] = '\0';
JsonDocument doc;
doc["ssid"] = ssid;
doc["password"] = password;
String configStr;
serializeJson(doc, configStr);
String key = "wifi_" + String(savedNetworkCount);
preferences.putString(key.c_str(), configStr);
savedNetworkCount++;
Serial.printf(" 新增WiFi配置: %s (总计: %d)\n", ssid, savedNetworkCount);
return true;
}
Serial.println("❌ WiFi配置已满无法添加");
return false;
}
/**
* @brief 连接到指定WiFi网络
* 尝试连接到给定的WiFi网络
* @param ssid WiFi网络名称
* @param password WiFi网络密码
* @return 是否连接成功
*/
bool WiFiManager::connectToNetwork(const char* ssid, const char* password) {
Serial.printf("🌐 [WiFi] 尝试连接到 SSID: %s\n", ssid);
currentState = WIFI_CONNECTING;
setNetworkStatus(NET_CONNECTING);
WiFi.mode(WIFI_STA);
vTaskDelay(200 / portTICK_PERIOD_MS);
WiFi.begin(ssid, password);
unsigned long startTime = millis();
unsigned long lastStatusPrint = 0;
while (WiFi.status() != WL_CONNECTED && (millis() - startTime) < WIFI_CONNECT_TIMEOUT) {
if (millis() - lastStatusPrint >= 500) {
Serial.printf("[WiFi] 连接中,状态: %d\n", WiFi.status());
lastStatusPrint = millis();
}
yield();
vTaskDelay(50 / portTICK_PERIOD_MS);
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("✅ [WiFi] 连接成功!");
Serial.printf("🌐 IP地址: %s\n", WiFi.localIP().toString().c_str());
Serial.printf("🔒 信号强度: %d dBm\n", WiFi.RSSI());
currentState = WIFI_CONNECTED;
setNetworkStatus(NET_CONNECTED);
// 向蓝牙发送当前连接的WiFi配置信息
if (deviceConnected) {
JsonDocument doc;
doc["type"] = "wifiConnected";
doc["success"] = true;
doc["ssid"] = ssid;
doc["password"] = password;
doc["ip"] = WiFi.localIP().toString();
doc["rssi"] = WiFi.RSSI();
String jsonStr;
serializeJson(doc, jsonStr);
sendJSONDataToBLE(jsonStr);
Serial.printf("📱 [BLE] 发送WiFi连接信息: %s\n", jsonStr.c_str());
}
return true;
} else {
Serial.println("❌ [WiFi] 连接超时");
currentState = WIFI_DISCONNECTED;
setNetworkStatus(NET_DISCONNECTED);
// 向蓝牙发送连接失败信息
if (deviceConnected) {
JsonDocument doc;
doc["type"] = "wifiConnected";
doc["success"] = false;
doc["ssid"] = ssid;
doc["message"] = "WiFi连接失败请检查密码是否正确";
String jsonStr;
serializeJson(doc, jsonStr);
sendJSONDataToBLE(jsonStr);
Serial.printf("📱 [BLE] 发送WiFi连接失败信息: %s\n", jsonStr.c_str());
}
return false;
}
}
/**
* @brief 扫描并匹配WiFi网络
* 扫描附近的WiFi网络并尝试匹配已保存的配置
* 如果找到多个匹配的网络,按信号强度从强到弱依次尝试连接
* @return 是否成功连接到匹配的网络
*/
bool WiFiManager::scanAndMatchNetworks() {
if (isScanning) {
Serial.println("⏳ [WiFi] 正在扫描中,等待扫描完成...");
int waitCount = 0;
while (isScanning && waitCount < 50) {
vTaskDelay(100 / portTICK_PERIOD_MS);
waitCount++;
}
if (isScanning) {
Serial.println("⚠️ [WiFi] 等待超时,跳过本次扫描");
return false;
}
Serial.println("✅ [WiFi] 扫描已完成,开始新的扫描");
}
Serial.println("🔍 [WiFi] 开始扫描WiFi网络...");
currentState = WIFI_SCANNING;
isScanning = true;
if (WiFi.status() == WL_CONNECTED) {
Serial.println("📶 WiFi已连接断开后扫描");
WiFi.disconnect(false);
vTaskDelay(200 / portTICK_PERIOD_MS);
}
WiFi.mode(WIFI_STA);
vTaskDelay(200 / portTICK_PERIOD_MS);
int n = WiFi.scanNetworks();
Serial.printf("🔍 扫描到 %d 个WiFi网络\n", n);
if (n <= 0) {
Serial.println("❌ 未扫描到任何WiFi网络或扫描失败");
currentState = WIFI_DISCONNECTED;
isScanning = false;
return false;
}
// 收集所有匹配的、信号强度符合要求的网络
struct CandidateNetwork {
const char* ssid;
const char* password;
int rssi;
};
CandidateNetwork availableNetworks[MAX_WIFI_NETWORKS];
int availableCount = 0;
// 遍历已保存的网络,寻找匹配的网络
for (int i = 0; i < savedNetworkCount; i++) {
for (int j = 0; j < n; j++) {
if (WiFi.SSID(j) == String(savedNetworks[i].ssid)) {
int rssi = WiFi.RSSI(j);
Serial.printf("📶 找到匹配网络: %s, 信号: %d dBm\n",
savedNetworks[i].ssid, rssi);
// 检查信号强度是否符合要求
if (rssi >= MIN_RSSI_THRESHOLD) {
// 添加到可用网络列表
if (availableCount < MAX_WIFI_NETWORKS) {
availableNetworks[availableCount].ssid = savedNetworks[i].ssid;
availableNetworks[availableCount].password = savedNetworks[i].password;
availableNetworks[availableCount].rssi = rssi;
availableCount++;
Serial.printf("✅ 添加到候选列表: %s, 信号: %d dBm\n",
savedNetworks[i].ssid, rssi);
}
} else {
Serial.printf("⚠️ 信号强度过低,跳过\n");
}
break; // 找到匹配后跳出内层循环
}
}
}
// 如果没有找到任何可用网络
if (availableCount == 0) {
Serial.println("❌ 未找到匹配的WiFi网络或信号过弱");
WiFi.scanDelete();
currentState = WIFI_DISCONNECTED;
isScanning = false;
return false;
}
// 按信号强度从强到弱排序(冒泡排序)
for (int i = 0; i < availableCount - 1; i++) {
for (int j = 0; j < availableCount - i - 1; j++) {
if (availableNetworks[j].rssi < availableNetworks[j + 1].rssi) {
CandidateNetwork temp = availableNetworks[j];
availableNetworks[j] = availableNetworks[j + 1];
availableNetworks[j + 1] = temp;
}
}
}
Serial.printf("📋 找到 %d 个可用网络,按信号强度排序:\n", availableCount);
for (int i = 0; i < availableCount; i++) {
Serial.printf(" %d. %s (信号: %d dBm)\n", i + 1,
availableNetworks[i].ssid, availableNetworks[i].rssi);
}
WiFi.scanDelete();
// 依次尝试连接所有可用网络
for (int i = 0; i < availableCount; i++) {
Serial.printf("🔄 [%d/%d] 尝试连接: %s (信号: %d dBm)\n",
i + 1, availableCount,
availableNetworks[i].ssid,
availableNetworks[i].rssi);
vTaskDelay(300 / portTICK_PERIOD_MS);
if (connectToNetwork(availableNetworks[i].ssid, availableNetworks[i].password)) {
Serial.printf("✅ 成功连接到: %s\n", availableNetworks[i].ssid);
isScanning = false;
return true;
}
// 连接失败,继续尝试下一个
Serial.printf("❌ %s 连接失败\n", availableNetworks[i].ssid);
// 如果不是最后一个,准备尝试下一个
if (i < availableCount - 1) {
Serial.println("➡️ 尝试下一个网络...");
}
}
Serial.println("❌ 所有可用网络均连接失败");
currentState = WIFI_DISCONNECTED;
isScanning = false;
// 向蓝牙发送所有WiFi连接失败信息
if (deviceConnected) {
String wifiList = "{\"type\":\"wifiConnected\",\"success\":false,\"message\":\"所有保存的WiFi均连接失败\",\"networks\":[";
bool first = true;
for (int i = 0; i < availableCount; i++) {
if (!first) wifiList += ",";
wifiList += "{\"ssid\":\"" + String(availableNetworks[i].ssid) + "\",\"rssi\":" + String(availableNetworks[i].rssi) + "}";
first = false;
}
wifiList += "],\"count\":" + String(availableCount) + "}";
sendJSONDataToBLE(wifiList);
Serial.printf("📱 [BLE] 发送所有WiFi连接失败信息: %s\n", wifiList.c_str());
}
return false;
}
/**
* @brief 初始化WiFi连接
* 启动WiFi初始化过程尝试连接到已保存的网络
* @return 是否初始化成功
*/
bool WiFiManager::initializeWiFi() {
Serial.println("🚀 [WiFi] 初始化WiFi连接...");
if (savedNetworkCount == 0) {
Serial.println("⚠️ 未保存的WiFi配置");
currentState = WIFI_IDLE;
return false;
}
if (scanAndMatchNetworks()) {
Serial.println("✅ WiFi初始化成功");
return true;
} else {
Serial.println("❌ WiFi初始化失败");
currentState = WIFI_DISCONNECTED;
return false;
}
}
/**
* @brief 扫描WiFi网络并发送结果
* 扫描附近的WiFi网络过滤信号弱的网络将结果通过BLE发送给客户端
*/
void WiFiManager::scanAndSendResults() {
if (isScanning) {
Serial.println("⏳ [WiFi] 正在扫描中,等待扫描完成...");
int waitCount = 0;
while (isScanning && waitCount < 50) {
vTaskDelay(100 / portTICK_PERIOD_MS);
waitCount++;
}
if (isScanning) {
Serial.println("⚠️ [WiFi] 等待超时,跳过本次扫描");
if (deviceConnected) {
String errorMsg = String("{\"type\":\"scanWiFiResult\",\"success\":false,\"message\":\"等待扫描超时,请稍后再试\",\"networks\":[],\"count\":0}");
sendJSONDataToBLE(errorMsg);
}
return;
}
Serial.println("✅ [WiFi] 扫描已完成,开始新的扫描");
}
Serial.println("📱 [BLE-WiFi] 开始WiFi扫描...");
isScanning = true;
// 确保完全断开WiFi
if (WiFi.status() == WL_CONNECTED) {
Serial.println("📶 WiFi已连接断开后扫描");
WiFi.disconnect(true);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
// 更完整的WiFi初始化
WiFi.mode(WIFI_OFF);
vTaskDelay(100 / portTICK_PERIOD_MS);
WiFi.mode(WIFI_STA);
vTaskDelay(500 / portTICK_PERIOD_MS);
// 尝试多次扫描,提高成功率
int n = 0;
int retryCount = 3;
while (n <= 0 && retryCount > 0) {
Serial.printf("🔍 扫描WiFi网络 (尝试 %d/3)...\n", 4 - retryCount);
// 使用更可靠的扫描参数被动扫描包含隐藏SSID更长的扫描时间
n = WiFi.scanNetworks(false, true, true, 5000); // 5000ms扫描时间
if (n <= 0) {
Serial.printf("❌ 扫描失败,返回值: %d, 剩余重试: %d\n", n, retryCount - 1);
retryCount--;
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
Serial.printf("🔍 最终扫描到 %d 个WiFi网络\n", n);
if (n <= 0) {
Serial.println("❌ 未扫描到任何WiFi网络或扫描失败");
isScanning = false;
if (deviceConnected) {
String errorMsg = String("{\"type\":\"scanWiFiResult\",\"success\":false,\"message\":\"未扫描到任何WiFi网络或扫描失败\",\"networks\":[],\"count\":0}");
sendJSONDataToBLE(errorMsg);
}
return;
}
// 构建WiFi网络列表的JSON数据
String wifiList = String("{\"type\":\"scanWiFiResult\",\"success\":true,\"count\":") + String(n) + String(",\"networks\":[");
bool first = true;
for (int i = 0; i < n; ++i) {
if (WiFi.RSSI(i) >= MIN_RSSI_THRESHOLD) {
if (!first) {
wifiList += ",";
}
wifiList += String("{\"ssid\":\"") + WiFi.SSID(i) + String("\",\"rssi\":") +
String(WiFi.RSSI(i)) + String(",\"channel\":") +
String(WiFi.channel(i)) + String(",\"encryption\":");
// 根据加密类型添加相应的描述
switch (WiFi.encryptionType(i)) {
case WIFI_AUTH_OPEN:
wifiList += String("\"open\"");
break;
case WIFI_AUTH_WEP:
wifiList += String("\"WEP\"");
break;
case WIFI_AUTH_WPA_PSK:
wifiList += String("\"WPA\"");
break;
case WIFI_AUTH_WPA2_PSK:
wifiList += String("\"WPA2\"");
break;
case WIFI_AUTH_WPA_WPA2_PSK:
wifiList += String("\"WPA/WPA2\"");
break;
case WIFI_AUTH_WPA2_ENTERPRISE:
wifiList += String("\"WPA2-EAP\"");
break;
case WIFI_AUTH_WPA3_PSK:
wifiList += String("\"WPA3\"");
break;
case WIFI_AUTH_WPA2_WPA3_PSK:
wifiList += String("\"WPA2/WPA3\"");
break;
default:
wifiList += String("\"unknown\"");
break;
}
wifiList += "}";
first = false;
}
}
wifiList += "]}";
Serial.printf("✅ 发送WiFi扫描结果包含 %d 个可用网络\n", first ? 0 : n);
WiFi.scanDelete();
isScanning = false;
if (deviceConnected) {
sendJSONDataToBLE(wifiList);
}
}
/**
* @brief 开始配网模式
* 进入配网模式扫描WiFi网络并发送结果给客户端
* @return 是否成功进入配网模式
*/
bool WiFiManager::startConfiguration() {
Serial.println("⚙️ [WiFi] 开始配网模式...");
currentState = WIFI_CONFIGURING;
scanAndSendResults();
return true;
}
/**
* @brief 处理配网数据
* 处理从客户端收到的WiFi配网信息先扫描WiFi是否有匹配的网络再尝试连接并保存
* @param ssid WiFi网络名称
* @param password WiFi网络密码
* @return 是否配置成功
*/
bool WiFiManager::handleConfigurationData(const char* ssid, const char* password) {
Serial.printf("📱 [BLE-WiFi] 收到配网信息: SSID='%s'\n", ssid);
if (ssid == nullptr || password == nullptr || strlen(ssid) == 0) {
Serial.println("❌ 配网参数无效");
return false;
}
if (isScanning) {
Serial.println("⏳ [WiFi] 正在扫描中,等待扫描完成...");
int waitCount = 0;
while (isScanning && waitCount < 50) {
vTaskDelay(100 / portTICK_PERIOD_MS);
waitCount++;
}
if (isScanning) {
Serial.println("⚠️ [WiFi] 等待超时");
if (deviceConnected) {
String errorMsg = String("{\"type\":\"wifiConfigResult\",\"success\":false,\"message\":\"等待扫描超时,请稍后再试\"}");
sendJSONDataToBLE(errorMsg);
}
return false;
}
Serial.println("✅ [WiFi] 扫描已完成,开始新的扫描");
}
// 先扫描WiFi网络检查是否存在匹配的网络
Serial.println("🔍 [WiFi] 扫描WiFi网络检查是否存在匹配的网络...");
currentState = WIFI_SCANNING;
isScanning = true;
int n = WiFi.scanNetworks();
Serial.printf("🔍 扫描到 %d 个WiFi网络\n", n);
if (n == 0) {
Serial.println("❌ 未扫描到任何WiFi网络");
WiFi.scanDelete();
isScanning = false;
currentState = WIFI_DISCONNECTED;
if (deviceConnected) {
String resultMsg = String("{\"type\":\"wifiConfigResult\",\"success\":false,\"message\":\"未扫描到任何WiFi网络请检查设备位置\"}");
sendJSONDataToBLE(resultMsg);
}
return false;
}
bool networkFound = false;
bool signalTooWeak = false;
int foundRssi = 0;
for (int i = 0; i < n; i++) {
if (WiFi.SSID(i) == String(ssid)) {
foundRssi = WiFi.RSSI(i);
Serial.printf("📶 找到匹配网络: %s, 信号: %d dBm\n", ssid, foundRssi);
if (foundRssi >= MIN_RSSI_THRESHOLD) {
Serial.printf("✅ 信号强度符合要求,准备连接...\n");
networkFound = true;
break;
} else {
Serial.printf("⚠️ 信号强度过低,跳过\n");
signalTooWeak = true;
}
}
}
if (!networkFound) {
String errorMsg;
if (signalTooWeak) {
errorMsg = String("{\"type\":\"wifiConfigResult\",\"success\":false,\"message\":\"目标WiFi信号过弱请将设备靠近路由器\"}");
Serial.printf("❌ 目标WiFi信号过弱: %d dBm (阈值: %d dBm)\n", foundRssi, MIN_RSSI_THRESHOLD);
} else {
errorMsg = String("{\"type\":\"wifiConfigResult\",\"success\":false,\"message\":\"未找到目标WiFi网络请检查WiFi名称是否正确\"}");
Serial.println("❌ 未找到目标WiFi网络");
}
WiFi.scanDelete();
isScanning = false;
currentState = WIFI_DISCONNECTED;
if (deviceConnected) {
sendJSONDataToBLE(errorMsg);
}
return false;
}
WiFi.scanDelete();
isScanning = false;
vTaskDelay(300 / portTICK_PERIOD_MS);
// 尝试连接到指定网络
if (connectToNetwork(ssid, password)) {
// 连接成功后保存配置
if (saveWiFiConfig(ssid, password)) {
Serial.println("✅ WiFi配置成功并已保存");
if (deviceConnected) {
String resultMsg = String("{\"type\":\"wifiConfigResult\",\"success\":true,\"message\":\"WiFi配置成功\"}");
sendJSONDataToBLE(resultMsg);
}
return true;
}
}
Serial.println("❌ WiFi配置失败");
isScanning = false;
currentState = WIFI_DISCONNECTED;
if (deviceConnected) {
String resultMsg = String("{\"type\":\"wifiConfigResult\",\"success\":false,\"message\":\"WiFi配置失败请检查密码是否正确\"}");
sendJSONDataToBLE(resultMsg);
}
return false;
}
/**
* @brief 处理WiFi重连
* 检查WiFi连接状态当断开连接时尝试重连
*/
void WiFiManager::handleReconnect() {
// 检查当前是否已连接
if (currentState == WIFI_CONNECTED) {
if (WiFi.status() == WL_CONNECTED) {
return;
}
// 连接已断开
currentState = WIFI_DISCONNECTED;
setNetworkStatus(NET_DISCONNECTED);
Serial.println("⚠️ WiFi连接断开");
// 扫描并发送WiFi列表到蓝牙
scanAndSendResults();
}
// 处理重连逻辑
if (currentState == WIFI_DISCONNECTED) {
unsigned long currentTime = millis();
// 按照设定的间隔尝试重连
if (currentTime - lastReconnectAttempt >= WIFI_RECONNECT_INTERVAL) {
lastReconnectAttempt = currentTime;
if (scanAndMatchNetworks()) {
Serial.println("✅ WiFi重连成功");
} else {
Serial.println("❌ WiFi重连失败2秒后重试");
}
}
}
}
/**
* @brief 获取当前WiFi状态
* @return 当前的WiFi管理器状态
*/
WiFiManagerState WiFiManager::getState() {
return currentState;
}
/**
* @brief 检查WiFi是否已连接
* @return WiFi是否已成功连接
*/
bool WiFiManager::isConnected() {
return currentState == WIFI_CONNECTED && WiFi.status() == WL_CONNECTED;
}
/**
* @brief 断开WiFi连接
* 主动断开当前的WiFi连接
*/
void WiFiManager::disconnect() {
WiFi.disconnect(true);
currentState = WIFI_DISCONNECTED;
setNetworkStatus(NET_DISCONNECTED);
}
/**
* @brief 添加WiFi配置
* 向保存的配置中添加新的WiFi网络
* @param ssid WiFi网络名称
* @param password WiFi网络密码
* @return 是否添加成功
*/
bool WiFiManager::addWiFiConfig(const char* ssid, const char* password) {
return saveWiFiConfig(ssid, password);
}
/**
* @brief 清除所有WiFi配置
* 删除所有保存的WiFi网络配置
*/
void WiFiManager::clearAllConfigs() {
for (int i = 0; i < MAX_WIFI_NETWORKS; i++) {
String key = "wifi_" + String(i);
preferences.remove(key.c_str());
}
savedNetworkCount = 0;
Serial.println("🗑️ 已清除所有WiFi配置");
}
/**
* @brief 获取已保存的网络数量
* @return 已保存的WiFi网络配置数量
*/
int WiFiManager::getSavedNetworkCount() {
return savedNetworkCount;
}
/**
* @brief 获取已保存的WiFi网络列表
* 将保存的WiFi网络配置通过BLE发送给客户端
*/
void WiFiManager::getSavedNetworks() {
Serial.printf("📋 [WiFi] 获取已保存的WiFi网络列表共 %d 个\n", savedNetworkCount);
if (savedNetworkCount == 0) {
Serial.println("⚠️ 没有保存的WiFi网络");
if (deviceConnected) {
String responseMsg = String("{\"type\":\"savedNetworks\",\"success\":true,\"count\":0,\"networks\":[]}");
sendJSONDataToBLE(responseMsg);
}
return;
}
String wifiList = String("{\"type\":\"savedNetworks\",\"success\":true,\"count\":") + String(savedNetworkCount) + String(",\"networks\":[");
for (int i = 0; i < savedNetworkCount; i++) {
if (i > 0) {
wifiList += ",";
}
wifiList += String("{\"ssid\":\"") + String(savedNetworks[i].ssid) + String("\"}");
}
wifiList += "]}";
Serial.printf("📤 [WiFi] 发送已保存的WiFi网络列表: %s\n", wifiList.c_str());
if (deviceConnected) {
sendJSONDataToBLE(wifiList);
}
}
/**
* @brief 更新WiFi管理器状态
* 定期调用此函数处理WiFi重连等状态管理
*/
void WiFiManager::update() {
handleReconnect();
}

122
src/wifi_manager.h Normal file
View File

@@ -0,0 +1,122 @@
#ifndef WIFI_MANAGER_H
#define WIFI_MANAGER_H
#include <Arduino.h>
#include <WiFi.h>
#include <Preferences.h>
#include <ArduinoJson.h>
/**
* @brief 最大WiFi网络配置数量
* 定义设备可以保存的WiFi网络配置的最大数量
*/
#define MAX_WIFI_NETWORKS 10
/**
* @brief 最小信号强度阈值
* 定义WiFi网络信号强度的最低要求低于此值的网络会被过滤
* 单位dBm
*/
#define MIN_RSSI_THRESHOLD -200
/**
* @brief WiFi连接超时时间
* 定义WiFi连接的最大等待时间超过此时间认为连接失败
* 单位:毫秒
*/
#define WIFI_CONNECT_TIMEOUT 3000
/**
* @brief WiFi重连间隔时间
* 定义WiFi断开后尝试重新连接的时间间隔
* 单位:毫秒
*/
#define WIFI_RECONNECT_INTERVAL 2000
/**
* @brief WiFi网络信息结构
* 存储WiFi网络的详细信息用于扫描和显示
*/
typedef struct {
char ssid[32]; // WiFi网络名称
char password[64]; // WiFi网络密码
int rssi; // 信号强度单位dBm
uint8_t channel; // WiFi通道
uint8_t encryption; // 加密类型
} WiFiNetworkInfo;
/**
* @brief WiFi配置结构
* 存储WiFi网络的配置信息用于保存和加载
*/
typedef struct {
char ssid[32]; // WiFi网络名称
char password[64]; // WiFi网络密码
} WiFiConfig;
/**
* @brief WiFi管理器状态枚举
* 定义WiFi管理器的不同工作状态
*/
enum WiFiManagerState {
WIFI_IDLE, // 空闲状态
WIFI_SCANNING, // 扫描网络中
WIFI_CONNECTING, // 连接网络中
WIFI_CONNECTED, // 已连接
WIFI_DISCONNECTED, // 断开连接
WIFI_CONFIGURING // 配网模式
};
/**
* @brief 网络状态枚举
* 定义网络的不同状态用于LED控制
*/
enum NetworkStatus {
NET_INITIAL, // 初始化/未连接 - 慢闪
NET_CONNECTING, // 连接中 - 快闪
NET_CONNECTED, // 已连接 - 呼吸灯
NET_DISCONNECTED // 断开连接 - 慢闪
};
/**
* @brief WiFi管理器类
* 负责WiFi网络的扫描、连接、配置和管理
*/
class WiFiManager {
private:
Preferences preferences; // 用于存储WiFi配置的Preferences对象
WiFiConfig savedNetworks[MAX_WIFI_NETWORKS]; // 保存的WiFi网络配置数组
int savedNetworkCount; // 已保存的网络数量
WiFiManagerState currentState; // 当前WiFi管理器状态
unsigned long lastReconnectAttempt; // 上次尝试重连的时间
bool isScanning; // 是否正在扫描
bool scanAndMatchNetworks(); // 扫描并匹配网络
bool connectToNetwork(const char* ssid, const char* password); // 连接到指定网络
void sendScanResultsViaBLE(); // 发送扫描结果到BLE
bool saveWiFiConfig(const char* ssid, const char* password); // 保存WiFi配置
bool loadWiFiConfigs(); // 加载WiFi配置
public:
WiFiManager(); // 构造函数
void begin(); // 初始化WiFi管理器
bool initializeWiFi(); // 初始化WiFi连接
bool startConfiguration(); // 开始配网模式
bool handleConfigurationData(const char* ssid, const char* password); // 处理配网数据
void handleReconnect(); // 处理重连
WiFiManagerState getState(); // 获取当前状态
bool isConnected(); // 检查是否已连接
void disconnect(); // 断开连接
void scanAndSendResults(); // 扫描并发送结果
bool addWiFiConfig(const char* ssid, const char* password); // 添加WiFi配置
void clearAllConfigs(); // 清除所有配置
int getSavedNetworkCount(); // 获取已保存的网络数量
void getSavedNetworks(); // 获取已保存的WiFi网络列表
void update(); // 更新WiFi管理器状态
};
#endif

11
test/README Normal file
View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html

82
test_json_parser.js Normal file
View File

@@ -0,0 +1,82 @@
// JSON分包接收处理函数
let streamBuffer = ""
function addLog(message) {
console.log(message);
}
function handleParsedJson(obj) {
console.log("处理解析后的JSON对象:", obj);
}
function processChunk(fragment){
addLog('进入processChunk')
streamBuffer += fragment
let braceCount = 0
let jsonStart = -1
for(let i = 0; i < streamBuffer.length; i++){
const ch = streamBuffer[i]
if(ch === "{"){
if(braceCount === 0){
jsonStart = i
}
braceCount++
}else if(ch === "}"){
braceCount--
if(braceCount === 0 && jsonStart !== -1){
const jsonStr = streamBuffer.substring(jsonStart, i + 1)
addLog("收到完整JSON:" + jsonStr)
try{
const obj = JSON.parse(jsonStr)
handleParsedJson(obj)
}catch(e){
addLog("JSON解析失败:" + e)
}
streamBuffer = streamBuffer.substring(i + 1)
i = -1
jsonStart = -1
}
}
}
//缓冲区过大时清理防越界
if (streamBuffer.length > 20000) {
addLog("⚠ 清空超大缓冲区(异常保护)")
streamBuffer = ""
braceCount = 0
jsonStart = -1
}
}
// 模拟分包发送JSON数据
function simulateJSONSending() {
const jsonData = {
type: "radarData",
deviceId: 1001,
timestamp: Date.now(),
presence: 1,
heartRate: 72.5,
breathRate: 16.2,
motion: 0,
rssi: -45,
heartbeatWaveform: 120,
breathingWaveform: 45,
rawSignal: -25
};
const jsonString = JSON.stringify(jsonData);
console.log("原始JSON数据:", jsonString);
// 模拟分包发送
const packetSize = 15; // 每包15个字符
for (let i = 0; i < jsonString.length; i += packetSize) {
const packet = jsonString.substring(i, Math.min(i + packetSize, jsonString.length));
console.log(`发送分包 ${Math.floor(i/packetSize)+1}:`, packet);
processChunk(packet);
}
}
// 测试函数
console.log("=== 开始测试JSON分包接收处理 ===");
simulateJSONSending();
console.log("=== 测试完成 ===");