diff --git a/.trae/documents/plan_20260203_063543.md b/.trae/documents/plan_20260203_063543.md
new file mode 100644
index 0000000..0da299c
--- /dev/null
+++ b/.trae/documents/plan_20260203_063543.md
@@ -0,0 +1,58 @@
+# 修改蓝牙发送雷达数据模式
+
+## 1. 需求分析
+- 修改蓝牙发送机制,实现基于数据变化的发送
+- 当心率、呼吸、存在检测、体动状态、睡眠状态发生变化时才发送
+- 根据小程序设定的时间间隔进行定时检测
+- 如果检测到变化,立即更新数据
+- 按设定间隔检测数据更新,有变化则发送全部数据
+
+## 2. 实现方案
+
+### 2.1 添加数据存储结构
+- 在 `radar_manager.h` 中添加 `LastSentData` 结构体,用于存储上一次发送的数据
+- 包含需要检测变化的字段:心率、呼吸率、存在状态、运动状态、睡眠状态
+
+### 2.2 修改全局变量
+- 在 `radar_manager.cpp` 中添加 `lastSentData` 全局变量
+- 添加 `lastCheckTime` 变量用于记录上次检测时间
+
+### 2.3 实现数据变化检测函数
+- 添加 `isDataChanged()` 函数,比较当前数据与上次发送数据
+- 实现阈值判断,避免微小波动导致频繁发送
+
+### 2.4 重写 BLE 数据发送任务
+- 修改 `bleSendTask` 任务,实现以下逻辑:
+ 1. 定时检测数据变化(基于 `continuousSendInterval`)
+ 2. 检测到变化时立即发送数据
+ 3. 发送后更新 `lastSentData` 为当前数据
+ 4. 保持与现有命令处理的兼容性
+
+### 2.5 保持命令处理逻辑
+- 保留 `processStartContinuousSend` 和 `processStopContinuousSend` 函数
+- 确保小程序可以正常控制发送模式和间隔
+
+## 3. 代码修改点
+
+### 3.1 radar_manager.h
+- 添加 `LastSentData` 结构体定义
+- 在全局变量声明中添加 `lastSentData` 和相关变量
+
+### 3.2 radar_manager.cpp
+- 初始化 `lastSentData` 变量
+- 实现 `isDataChanged()` 函数
+- 重写 `bleSendTask` 任务函数
+- 确保数据发送格式与现有代码保持一致
+
+## 4. 技术要点
+- 使用阈值检测避免微小数据波动导致的频繁发送
+- 保持定时检测机制,确保数据及时性
+- 维持与现有 BLE API 的兼容性
+- 按照现有代码的注释格式编写新代码
+- 确保内存使用合理,避免内存泄漏
+
+## 5. 预期效果
+- 减少不必要的蓝牙数据传输
+- 只在数据发生实际变化时发送
+- 保持数据的实时性和准确性
+- 与现有小程序控制逻辑完全兼容
\ No newline at end of file
diff --git a/BLE_API.md b/BLE_API.md
new file mode 100644
index 0000000..5b7c715
--- /dev/null
+++ b/BLE_API.md
@@ -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 | 加密类型:
0 = 开放
1 = WEP
2 = WPA_PSK
3 = WPA2_PSK
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 | 设备 ID(1000-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 | ✅ | 新设备 ID(1000-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 |
diff --git a/src/Radar.cpp b/src/Radar.cpp
deleted file mode 100644
index e69de29..0000000
diff --git a/src/Radar.h b/src/Radar.h
deleted file mode 100644
index 416b754..0000000
--- a/src/Radar.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef RADAR_H
-#define RADAR_H
-// 在main.cpp顶部添加R60ABD1协议相关定义
-#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;
-
-
-#endif
diff --git a/src/main.cpp b/src/main.cpp
index 478b01c..f7f3330 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -3,35 +3,17 @@
#include
#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include // 添加这个头文件以支持memset函数
-#include
-#include // 用于数学函数
+#include "wifi_manager.h"
+#include "radar_manager.h"
// ESP32 GPIO控制演示
#define BOOT_BUTTON_PIN 0 // Boot按钮引脚
-#define NETWORK_LED_PIN 5 // 网络状态LED指示灯
+#define NETWORK_LED_PIN 48 // 网络状态LED指示灯开发板48引脚,雷达板5引脚
#define CONFIG_CLEAR_PIN 4 // 配置清除指示灯
#define GPIO8 8 // 自定义GPIO8
#define GPIO9 9 // 自定义GPIO9
-uint8_t presence_Bit = 1;//标志位
-uint8_t WiFi_Connect_First_bit = 1; //WiFi第一次连接标志位
-
-// 网络状态枚举
-enum NetworkStatus {
- NET_INITIAL, // 初始化/未连接 - 慢闪
- NET_CONNECTING, // 连接中 - 快闪
- NET_CONNECTED, // 已连接 - 呼吸灯
- NET_DISCONNECTED // 断开连接 - 慢闪
-};
+uint8_t WiFi_Connect_First_bit = 1; // WiFi首次连接标志位
// 配置清除指示灯状态枚举
enum ConfigClearStatus {
@@ -41,660 +23,474 @@ enum ConfigClearStatus {
CONFIG_COMPLETED // 清除完成 - 快速闪烁3次
};
-// 网络状态全局变量
-NetworkStatus currentNetworkStatus = NET_INITIAL;
-unsigned long lastBlinkTime = 0;
-bool ledState = false;
-int breatheValue = 0;
-bool breatheIncreasing = true;
+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;
-bool configLedState = false;
-int configBreatheValue = 0;
-bool configBreatheIncreasing = true;
+ConfigClearStatus currentConfigClearStatus = CONFIG_NORMAL; // 当前配置清除状态
+unsigned long lastConfigBlinkTime = 0; // 上次配置清除LED闪烁时间
+bool configLedState = false; // 配置清除LED状态
+int configBreatheValue = 0; // 配置清除呼吸灯当前亮度值
+bool configBreatheIncreasing = true; // 配置清除呼吸灯是否在增加亮度
-// LED控制相关参数
-const int SLOW_BLINK_INTERVAL = 1000; // 慢闪间隔 1秒
-const int FAST_BLINK_INTERVAL = 200; // 快闪间隔 0.2秒
-const int BREATHE_INTERVAL = 20; // 呼吸灯更新间隔
-const int BREATHE_MIN = 0; // 呼吸灯最小值
-const int BREATHE_MAX = 155; // 呼吸灯最大值
-const int BREATHE_STEP = 5; // 呼吸灯步进值
+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; // 呼吸灯亮度步进值
-// 协议ID映射表 - 对应Python脚本中的field_mapping
-const int PROTOCOL_HEART_RATE = 1; // 心率
-const int PROTOCOL_BREATH_RATE = 2; // 呼吸率
-const int PROTOCOL_PERSON_DETECTED = 13; // 人检
-const int PROTOCOL_HUMAN_ACTIVITY = 14; // 人体活动
-const int PROTOCOL_HUMAN_DISTANCE = 15; // 人体距离
-const int PROTOCOL_HUMAN_POSITION = 16; // 人体方位
-const int PROTOCOL_SLEEP_STATE = 17; // 睡眠状态
+const uint16_t MIN_DEVICE_ID = 1000; // 最小设备ID
+const uint16_t MAX_DEVICE_ID = 1999; // 最大设备ID
-// R60ABD1新增协议ID
-const int PROTOCOL_BODY_MOVEMENT = 18; // 体动幅度
-const int PROTOCOL_BREATH_STATUS = 19; // 呼吸信息
-const int PROTOCOL_SLEEP_TIME = 20; // 睡眠时长
-const int PROTOCOL_SLEEP_SCORE = 21; // 睡眠质量评分
-const int PROTOCOL_BED_ENTRY = 22; // 入床/离床状态
-const int PROTOCOL_ABNORMAL_STATE = 23; // 异常状态
-const int PROTOCOL_AVG_HEART_RATE = 24; // 平均心率
-const int PROTOCOL_AVG_BREATH_RATE = 25; // 平均呼吸率
-const int PROTOCOL_TURN_COUNT = 26; // 翻身次数
-const int PROTOCOL_LARGE_MOVE_RATIO = 27; // 大幅度体动占比
-const int PROTOCOL_SMALL_MOVE_RATIO = 28; // 小幅度体动占比
-const int PROTOCOL_POS_X = 29; // 人体X坐标
-const int PROTOCOL_POS_Y = 30; // 人体Y坐标
-const int PROTOCOL_POS_Z = 31; // 人体Z坐标
-const int PROTOCOL_DEEP_SLEEP_TIME = 32; // 深睡时长
-const int PROTOCOL_LIGHT_SLEEP_TIME = 33; // 浅睡时长
-const int PROTOCOL_AWAKE_TIME = 34; // 清醒时长
-const int PROTOCOL_SLEEP_TOTAL_TIME = 35; // 睡眠总时长
-const int PROTOCOL_DEEP_SLEEP_RATIO = 36; // 深睡占比
-const int PROTOCOL_LIGHT_SLEEP_RATIO = 37; // 浅睡占比
-const int PROTOCOL_AWAKE_RATIO = 38; // 清醒占比
-const int PROTOCOL_TURNOVER_COUNT = 39; // 翻身次数
-const int PROTOCOL_STRUGGLE_ALERT = 40; // 挣扎报警
-const int PROTOCOL_NO_ONE_ALERT = 41; // 无人计时报警
-const int PROTOCOL_BED_STATUS = 42; // 入床状态
-const int PROTOCOL_APNEA_COUNT = 43; // 呼吸暂停次数
+const unsigned long CLEAR_CONFIG_DURATION = 3000; // 清除配置持续时间(毫秒)
-bool clearConfigRequested = false;
+Preferences preferences; // Flash存储对象
+WiFiManager wifiManager; // WiFi管理器对象
-unsigned long bootButtonPressTime = 0;
+uint16_t currentDeviceId = 0000; // 当前设备ID
+bool clearConfigRequested = false; // 清除配置请求标志
+bool forceLedOff = false; // 强制关闭LED标志
-const unsigned long CLEAR_CONFIG_DURATION = 3000; // 长按3秒清除配置
+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();
-#define BUFFER_SIZE 2000 // 固定存储2000个数据点
-// 环形缓冲区结构
-struct CircularBuffer {
- float data[BUFFER_SIZE];
- unsigned long timestamps[BUFFER_SIZE]; // 存储时间戳
- int head; // 最新数据位置
- int tail; // 最旧数据位置
- int count; // 当前有效数据数量
- bool isFull; // 缓冲区是否已满
- float sum; // 当前数据总和
-};
-
-
-
-Preferences preferences;// 声明一个Preferences对象
-
-// 设备ID变量
-uint16_t currentDeviceId = 0000; // 默认设备ID为0000
-const uint16_t MIN_DEVICE_ID = 1000; // 设备ID最小值
-const uint16_t MAX_DEVICE_ID = 1999; // 设备ID最大值
-
-const uint32_t PHASE_SEND_INTERVAL = 1;// 每1毫秒发送一次相位数据
-const uint32_t VITAL_SEND_INTERVAL = 10;// 每10毫秒发送一次生命体征数据
-
-#define SERVICE_UUID "a8c1e5c0-3d5d-4a9d-8d5e-7c8b6a4e2f1a"
-// #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
-#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
-
-#define UART_RX_BUFFER_SIZE 4096
-
-BLEServer* pServer = NULL;
-BLECharacteristic* pCharacteristic = NULL;
-bool deviceConnected = false;
-bool oldDeviceConnected = false;
-String receivedData = "";
-String completeData = "";
-unsigned long lastReceiveTime = 0;
-
-WiFiClient client;
-char ssid[32] = "";
-char password[64] = "";
-bool wifiConfigured = false;
-
-// WiFi监控相关变量
-unsigned long lastWiFiCheckTime = 0;
-const unsigned long WIFI_CHECK_INTERVAL = 500; // 每0.5秒检查一次WiFi状态
-int wifiReconnectAttempts = 0;
-const int MAX_RECONNECT_ATTEMPTS = 10;// 最大重连尝试次数
-
-const char* influxDBHost = "8.134.11.76";
-const int influxDBPort = 8086;
-const char* influxDBToken = "KuTa5ZsqoHIhi2IglOO06zExUYw1_mJ6K0mcA9X1y6O6CJDog3_Cgr8mUw1SwpuCCKRElqxa6wAhrrhsYPytkg==";
-const char* influxDBOrg = "gzlg";
-const char* influxDBBucket = "gzlg";
-
-const unsigned long SENSOR_TIMEOUT = 40000;
-static uint32_t packetCounter = 0;
-static bool shouldSendOtherData = false;
-
-unsigned long lastSensorUpdate = 0;
-
-// 睡眠数据发送相关
-unsigned long lastSleepDataTime = 0;
-const unsigned long SLEEP_DATA_INTERVAL = 5000; // 5秒 = 5000毫秒
-
-// 更新SensorData结构体以支持R60ABD1的全部功能点
-typedef struct {
- float breath_rate; // 呼吸率 (DP8) 单位:次/分钟
- float heart_rate; // 心率 (DP6) 单位:BPM
- uint8_t breath_valid; // 呼吸率有效性
- uint8_t heart_valid; // 心率有效性
- uint8_t presence ; // 存在检测 (DP1) 0=无人, 1=有人
- uint8_t motion = 0; // 运动状态 (DP2) 0=无, 1=静止, 2=活跃
- int heartbeat_waveform; // 心跳波形 (DP7) 数值+128
- int breathing_waveform; // 呼吸波形 (DP10) 数值+128
-
- // R60ABD1新增字段
- uint16_t distance; // 人体距离 (DP3) 单位:cm
- uint8_t body_movement; // 体动幅度 (DP4) 0-100
- uint8_t breath_status = 04; // 呼吸信息 (DP9) 01=正常, 02=呼吸过高, 03=呼吸过低, 04=无
- uint8_t sleep_state = 3; // 睡眠状态 (DP12) 0=深睡, 1=浅睡, 2=清醒, 3=无睡眠
- uint32_t sleep_time; // 睡眠时长 (DP13) 单位:秒
- uint8_t sleep_score; // 睡眠质量评分 (DP14) 0-100
- uint8_t sleep_grade; //睡眠质量评级
- uint8_t bed_entry; // 入床/离床状态 (DP11) 0=离床, 1=入床
- uint8_t abnormal_state; // 异常状态 (DP18) 0=正常, 1=异常
- uint8_t avg_heart_rate; // 平均心率 (DP15)
- uint8_t avg_breath_rate; // 平均呼吸率 (DP15)
- uint8_t turn_count; // 翻身次数 (DP15)
- uint8_t large_move_ratio;// 大幅度体动占比 (DP15)
- uint8_t small_move_ratio;// 小幅度体动占比 (DP15)
-
- // 根据技术文档新增字段
- int16_t pos_x; // 人体X坐标 (DP5)
- int16_t pos_y; // 人体Y坐标 (DP5)
- int16_t pos_z; // 人体Z坐标 (DP5)
- int8_t breath_waveform[5]; // 呼吸波形数据 (DP10)
- int8_t heart_waveform[5]; // 心率波形数据 (DP7)
- uint16_t deep_sleep_time; // 深睡时长 (DP15) 单位:分钟
- uint16_t light_sleep_time; // 浅睡时长 (DP14) 单位:分钟
- uint16_t awake_time; // 清醒时长 (DP13) 单位:分钟
- uint16_t sleep_total_time; // 睡眠总时长 (DP18) 单位:分钟
- uint8_t deep_sleep_ratio; // 深睡占比 (DP16)
- uint8_t light_sleep_ratio; // 浅睡占比 (DP16)
- uint8_t awake_ratio; // 清醒占比 (DP16)
- uint8_t turnover_count; // 翻身次数 (DP16)
- uint8_t struggle_alert; // 挣扎报警 (DP21)
- uint8_t no_one_alert; // 无人计时报警 (DP22)
- uint8_t bed_status = 02; //入床状态 (DP11)
- uint8_t bed_Out_Time; // 离床时间 (DP11)
- uint8_t apnea_count; // 呼吸暂停次数
-} SensorData;
-
-SensorData sensorData ;
-
-// HardwareSerial mySerial1(1); // 使用UART1
-// const int BAUD_RATE = 115200;
-// const int UART1_RX = 2; // UART1的RX引脚,配合原理图
-// const int UART1_TX = 3; // UART1的TX引脚,配合原理图
-
-HardwareSerial mySerial1(1); // 使用UART1
-const int BAUD_RATE = 115200;
-const int UART1_RX = 3; // UART1的RX引脚,配合原理图
-const int UART1_TX = 2; // UART1的TX引脚,配合原理图
-
-// FreeRTOS任务和队列定义
-QueueHandle_t phaseDataQueue;
-QueueHandle_t vitalDataQueue;
-QueueHandle_t uartQueue = NULL; // 添加串口数据队列
-TaskHandle_t bleSendTaskHandle = NULL;
-TaskHandle_t vitalSendTaskHandle = NULL;
-TaskHandle_t uartProcessTaskHandle = NULL; // 添加串口处理任务句柄
-
-#define QUEUE_SIZE 50
-#define TASK_STACK_SIZE 8192
-#define TASK_PRIORITY 1
-
-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 = 3; // 睡眠状态
- 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;
-
-// 睡眠数据结构 - 对应Python脚本中的sleep_data
-typedef struct {
- uint8_t sleepQualityScore; // 睡眠质量评分 (0~100)
- uint16_t totalSleepDuration; // 睡眠总时长 (0~65535 分钟)
- uint8_t awakeDurationRatio; // 清醒时长占比 (0~100)
- uint8_t lightSleepRatio; // 浅睡时长占比 (0~100)
- uint8_t deepSleepRatio; // 深睡时长占比 (0~100)
- uint8_t outOfBedDuration; // 离床时长 (0~255)
- uint8_t outOfBedCount; // 离床次数 (0~255)
- uint8_t turnCount; // 翻身次数 (0~255)
- uint8_t avgBreathingRate; // 平均呼吸 (0~25)
- uint8_t avgHeartRate; // 平均心跳 (0~100)
- uint8_t apneaCount; // 呼吸暂停次数 (0~10)
-} SleepData;
-
-// 添加流量控制类
-class BLEFlowController {
-private:
- size_t maxBytesPerSecond;
- size_t bytesSent;
- unsigned long lastResetTime;
- unsigned long lastSendTime;
-
-public:
- BLEFlowController(size_t maxBps) : maxBytesPerSecond(maxBps), bytesSent(0) {
- lastResetTime = millis();
- lastSendTime = 0;
- }
-
- bool canSend(size_t dataSize) {
- unsigned long currentTime = millis();
-
- // 每秒重置计数器
- if(currentTime - lastResetTime >= 1000) {
- bytesSent = 0;
- lastResetTime = currentTime;
- }
-
- // 检查速率限制(放宽限制)
- if((bytesSent + dataSize) > maxBytesPerSecond) {
- return false;
- }
-
- // 最小发送间隔控制(重要!)
- if(currentTime - lastSendTime < 5) { // 进一步减小最小间隔到5ms
- return false;
- }
-
- return true;
- }
-
- bool check() {
- // 这是一个简化版本的检查方法,只检查最小发送间隔
- unsigned long currentTime = millis();
- if(currentTime - lastSendTime < 5) { // 最小间隔5ms
- return false;
- }
- return true;
- }
-
- void recordSend(size_t dataSize) {
- bytesSent += dataSize;
- lastSendTime = millis();
- }
-
- void reset() {
- bytesSent = 0;
- lastResetTime = millis();
- lastSendTime = 0;
- }
-};
-
-// 全局变量用于控制持续发送
-bool continuousSendEnabled = false;
-unsigned long continuousSendInterval = 500; // 默认1秒发送一次
-BLEFlowController bleFlow(500); // 提高到500 B/s限制,进一步提高数据传输速率
-
-void configClearLedTask(void *parameter); // 配置清除指示灯控制任务
-void bootButtonMonitorTask(void *parameter); // BOOT按钮监控任务
-void checkBootButton(); // 检查Boot按钮状态
-void clearStoredConfig(); // 清除所有存储的配置
-void ledControlTask(void *parameter); // LED控制任务
-void setNetworkStatus(NetworkStatus status); // 设置网络状态
-
-void processBLEConfig(); // 处理BLE配置命令
-void saveWiFiConfig(); // 保存WiFi配置
-void loadWiFiConfig(); // 加载WiFi配置
-bool connectWiFi(); // 连接WiFi
-bool processWiFiConfigCommand(JsonDocument& doc); // 处理WiFi配置命令
-
-bool parseR60ABD1Frame(uint8_t *frame, uint16_t frameLen); // 解析传感器数据行
-int16_t parseSignedCoordinate(uint16_t raw_value); // 解析有符号坐标值
-void initR60ABD1(); // 初始化R60ABD1雷达
-void sendRadarCommand(uint8_t ctrl, uint8_t cmd, uint8_t value); // 发送雷达命令
-
-void wifiMonitorTask(void *parameter); // WiFi监控任务
-void WiFiEvent(WiFiEvent_t event); // WiFi事件处理
-void loadDeviceId(); // 加载设备ID
-void saveDeviceId(); // 保存设备ID
-bool processSetDeviceId(JsonDocument& doc); // 处理设置设备ID命令
-void sendStatusToBLE(); // 发送状态信息到BLE
-bool processQueryStatus(JsonDocument& doc); // 处理查询状态命令
-
-// 发送日常数据到InfluxDB - 高频实时监测数据
-void sendDailyDataToInfluxDB(String dailyDataLine);
-
-// 发送睡眠数据到InfluxDB - 低频汇总数据
-void sendSleepDataToInfluxDB();
-
-bool processQueryRadarData(JsonDocument& doc); // 处理查询雷达数据命令
-bool processStartContinuousSend(JsonDocument& doc); // 处理开始持续发送命令
-bool processStopContinuousSend(JsonDocument& doc); // 处理停止持续发送命令
-void sendRadarDataToBLE(); // 发送雷达数据到BLE
-void sendDataInChunks(const String& data); // 分包发送函数
-void sendJSONDataToBLE(const String& jsonData); // 发送JSON数据到BLE(使用纯数据分包发送,不带包头)
-bool sendCustomJSONData(const String& jsonType, const String& jsonString); // 发送自定义JSON数据到BLE
-
-// 根据协议ID获取对应的字段名
String getFieldNameByProtocolId(int protocolId);
-// 根据协议ID获取对应的字段名
+
+/**
+ * @brief 根据协议ID获取字段名称
+ * 将协议ID映射到对应的字段名称,用于数据序列化和反序列化
+ * @param protocolId 协议ID
+ * @return 对应的字段名称字符串
+ */
String getFieldNameByProtocolId(int protocolId) {
switch(protocolId) {
- case PROTOCOL_HEART_RATE:
+ case 1:
return "heartRate";
- case PROTOCOL_BREATH_RATE:
+ case 2:
return "breathingRate";
- case PROTOCOL_PERSON_DETECTED:
+ case 13:
return "personDetected";
- case PROTOCOL_HUMAN_ACTIVITY:
+ case 14:
return "humanActivity";
- case PROTOCOL_HUMAN_DISTANCE:
+ case 15:
return "humanDistance";
- case PROTOCOL_HUMAN_POSITION:
+ case 16:
return "humanPosition";
- case PROTOCOL_SLEEP_STATE:
+ case 17:
return "sleepState";
default:
return "unknown";
}
}
-// FreeRTOS任务函数声明
-void bleSendTask(void *parameter);
-void vitalSendTask(void *parameter);
-void radarDataTask(void *parameter);
-void uartProcessTask(void *parameter); // 添加串口处理任务声明
-
-
-// 使用正确的函数签名 - 不带参数的回调函数
-void IRAM_ATTR serialRxCallback() {
- // 直接使用全局变量mySerial1和uartQueue
- if (uartQueue != NULL) {
- // 读取所有可用数据
- while(mySerial1.available()) {
- char c = mySerial1.read();
-
- // 将字符放入队列(从中断安全函数)
- BaseType_t xHigherPriorityTaskWoken = pdFALSE;
- xQueueSendFromISR(uartQueue, &c, &xHigherPriorityTaskWoken);
-
- if(xHigherPriorityTaskWoken) {
- portYIELD_FROM_ISR();
- }
+/**
+ * @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按钮未按下,正常启动");
}
}
-class MyServerCallbacks: public BLEServerCallbacks {
- void onConnect(BLEServer* pServer) {
- deviceConnected = true;
- Serial.println("✅ [BLE] 客户端已连接");
-
- // 发送连接状态信息
- sendStatusToBLE();
- };
+/**
+ * @brief 加载设备ID
+ * 从Flash中读取保存的设备ID
+ */
+void loadDeviceId() {
+ currentDeviceId = preferences.getUShort("deviceId", 1001);
+ Serial.printf("从Flash加载设备ID: %u\n", currentDeviceId);
+}
- void onDisconnect(BLEServer* pServer) {
- deviceConnected = false;
- Serial.println("🔴 [BLE] 客户端已断开");
-
- // 重置持续发送状态
- continuousSendEnabled = false;
- Serial.println("🔄 重置持续发送状态");
+/**
+ * @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();
}
-};
+}
-class MyCallbacks: public BLECharacteristicCallbacks {
- void onWrite(BLECharacteristic *pCharacteristic) {
- std::string value = pCharacteristic->getValue();
- Serial.printf("🔵 [BLE] 收到写入数据,长度: %d 字节\n", value.length());
-
- if (value.length() > 0) {
- String fragment = "";
- for (int i = 0; i < value.length(); i++)
- fragment += value[i];
+/**
+ * @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;
- Serial.printf("📄 [BLE] 接收数据片段: %s\n", fragment.c_str());
+ case CONFIG_PREPARING:
+ analogWrite(CONFIG_CLEAR_PIN, 255);
+ break;
- completeData += fragment;
- lastReceiveTime = millis();
-
- // 检查是否收到完整的JSON数据
- int openBrace = completeData.indexOf('{');
- int closeBrace = completeData.lastIndexOf('}');
-
- if (openBrace >= 0 && closeBrace > openBrace) {
- String jsonData = completeData.substring(openBrace, closeBrace + 1);
- completeData = completeData.substring(closeBrace + 1);
+ case CONFIG_CLEARING:
+ if (millis() - lastConfigBlinkTime >= BREATHE_INTERVAL) {
+ analogWrite(CONFIG_CLEAR_PIN, configBreatheValue);
- Serial.printf("📥 [BLE] 完整JSON数据: %s\n", jsonData.c_str());
-
- // 将JSON数据存储到receivedData变量中供后续处理
- receivedData = jsonData;
+ 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);
- // 第一步:检查Boot按钮是否被按下
checkBootButton();
- // Serial.println("⏳ 等待雷达启动 (30秒),配置清除指示灯正在进行呼吸灯闪烁...");
- // unsigned long startTime = millis();
- // const unsigned long waitDuration = 30000; // 30秒
+ analogWrite(CONFIG_CLEAR_PIN, 0);
- // while (millis() - startTime < waitDuration) {
- // unsigned long elapsed = millis() - startTime;
- // float progress = (float)(elapsed % 2000) / 2000.0; // 每2秒一个周期
-
- // // 使用正弦波函数创建平滑的呼吸效果
- // float brightness = (sin(progress * 2 * PI) + 1) / 2; // 0到1之间
- // int pwmValue = (int)(brightness * 255);
-
- // // 使用analogWrite设置亮度
- // analogWrite(CONFIG_CLEAR_PIN, pwmValue);
-
- // // 短暂延时以允许其他任务运行
- // delay(50);
-
- // // 每隔一秒打印一次倒计时
- // if (elapsed / 1000 != (elapsed - 50) / 1000) {
- // Serial.print(waitDuration/1000 - elapsed/1000);
- // Serial.print("...");
- // }
- // }
- // Serial.println();
-
- // 确保结束后恢复到正常状态
- analogWrite(CONFIG_CLEAR_PIN, 0); // 关闭呼吸灯
- Serial.println("稳定期结束,开始工作。");
-
- // 如果请求清除配置,这里会先执行清除操作
- if (clearConfigRequested) {
- clearStoredConfig();
- // 清除后重启系统
- Serial.println("🔄 系统即将重启...");
- delay(1000);
- ESP.restart();
- }
-
- // 添加启动信息
Serial.println("🚀 ESP32-R60ABD1系统启动");
Serial.println("🔧 初始化系统组件...");
- // 初始化GPIO模式
- pinMode(BOOT_BUTTON_PIN, INPUT); // Boot按钮引脚,输入模式,不启用内部上拉
- pinMode(NETWORK_LED_PIN, OUTPUT); // 网络状态LED,输出模式
- pinMode(CONFIG_CLEAR_PIN, OUTPUT); // 配置清除指示灯,输出模式
- pinMode(GPIO8, OUTPUT); // 自定义GPIO8,输出模式
- pinMode(GPIO9, OUTPUT); // 自定义GPIO9,输出模式
+ pinMode(BOOT_BUTTON_PIN, INPUT);
+ pinMode(NETWORK_LED_PIN, OUTPUT);
+ pinMode(CONFIG_CLEAR_PIN, OUTPUT);
+ pinMode(GPIO8, OUTPUT);
+ pinMode(GPIO9, OUTPUT);
- // 初始化配置清除指示灯为LOW(正常运行状态)
digitalWrite(CONFIG_CLEAR_PIN, LOW);
digitalWrite(GPIO8, LOW);
digitalWrite(GPIO9, LOW);
- // 初始状态设置
- digitalWrite(NETWORK_LED_PIN, LOW); // 网络状态LED,输出模式,输出低电平
- digitalWrite(CONFIG_CLEAR_PIN, LOW);
+ digitalWrite(NETWORK_LED_PIN, LOW);
+ digitalWrite(CONFIG_CLEAR_PIN, LOW);
- // 设置PWM通道用于呼吸灯效果
- ledcSetup(0, 5000, 8); // PWM通道0, 5kHz, 8位分辨率
- ledcSetup(1, 5000, 8); // PWM通道1, 5kHz, 8位分辨率
- ledcAttachPin(NETWORK_LED_PIN, 0); // 网络状态LED使用通道0
- ledcAttachPin(CONFIG_CLEAR_PIN, 1); // 配置清除LED使用通道1
+ ledcSetup(0, 5000, 8);
+ ledcSetup(1, 5000, 8);
+ ledcAttachPin(NETWORK_LED_PIN, 0);
+ ledcAttachPin(CONFIG_CLEAR_PIN, 1);
- // 注册WiFi事件
WiFi.onEvent(WiFiEvent);
-
- // 设置初始网络状态
setNetworkStatus(NET_INITIAL);
- esp_task_wdt_init(30, true); // 初始化任务看门狗,30秒超时
+ esp_task_wdt_init(30, true);
esp_task_wdt_add(NULL);
- // 增加串口缓冲区大小以处理R60ABD1数据
- mySerial1.setRxBufferSize(UART_RX_BUFFER_SIZE);
- mySerial1.begin(BAUD_RATE, SERIAL_8N1, UART1_RX, UART1_TX);
-
- Serial.println("UART1配置完成,缓冲区大小: 4096字节");
- delay(1000);
+ preferences.begin("radar_data", false);
- preferences.begin("radar_data", false); // 在setup()中打开命名空间
+ wifiManager.begin();
- // 加载设备ID和WiFi配置
Serial.println("💾 加载设备配置...");
- loadDeviceId();// 加载设备ID
- loadWiFiConfig();// 加载WiFi配置
+ loadDeviceId();
- Serial.println("🏗️ 创建FreeRTOS队列...");
- // 创建FreeRTOS队列
- phaseDataQueue = xQueueCreate(QUEUE_SIZE, sizeof(PhaseData));
- vitalDataQueue = xQueueCreate(QUEUE_SIZE, sizeof(VitalData));
- uartQueue = xQueueCreate(2048, sizeof(char)); // 创建串口数据队列
+ Serial.println("🏗️ 初始化雷达管理器...");
+ initRadarManager();
- if (phaseDataQueue == NULL || vitalDataQueue == NULL || uartQueue == NULL) {
- Serial.println("❌ 队列创建失败");
- } else {
- Serial.println("✅ FreeRTOS队列创建成功");
- }
-
- // 设置串口中断回调函数
- mySerial1.onReceive(serialRxCallback);
- Serial.println("✅ 串口中断回调已设置");
-
- Serial.println("🏃 创建FreeRTOS任务...");
- // 创建FreeRTOS任务
- xTaskCreatePinnedToCore(
- bleSendTask,
- "BleSendTask",
- TASK_STACK_SIZE,
+ xTaskCreate(
+ configClearLedTask,
+ "Config Clear LED Task",
+ 2048,
NULL,
- 3, // 提高优先级到3
- &bleSendTaskHandle,
- 1
+ 1,
+ NULL
);
- xTaskCreatePinnedToCore(
- vitalSendTask,
- "VitalSendTask",
- TASK_STACK_SIZE,
+ xTaskCreate(
+ bootButtonMonitorTask,
+ "Boot Button Monitor Task",
+ 2048,
NULL,
- 2, // 中等优先级
- &vitalSendTaskHandle,
- 1
+ 1,
+ NULL
);
- // 创建专门的雷达数据处理任务
- xTaskCreatePinnedToCore(
- radarDataTask,
- "RadarProcessTask",
+ xTaskCreate(
+ ledControlTask,
+ "LED Control Task",
+ 2048,
+ NULL,
+ 1,
+ NULL
+ );
+
+ xTaskCreate(
+ wifiMonitorTask,
+ "WiFi Monitor Task",
4096,
NULL,
- 4, // 最高优先级
- NULL,
- 1
- );
-
- // 创建R60ABD1串口数据处理任务
- xTaskCreatePinnedToCore(
- uartProcessTask, // R60ABD1串口处理任务
- "UartProcessTask",
- 4096,
- NULL,
- 5, // 最高优先级
- &uartProcessTaskHandle,
- 1
- );
-
- // 创建配置清除指示灯控制任务
- xTaskCreate(
- configClearLedTask, // 任务函数
- "Config Clear LED Task", // 任务名称
- 2048, // 堆栈大小
- NULL, // 参数
- 1, // 优先级
- NULL // 任务句柄
- );
-
- // 创建BOOT按钮监控任务
- xTaskCreate(
- bootButtonMonitorTask, // 任务函数
- "Boot Button Monitor Task", // 任务名称
- 2048, // 堆栈大小
- NULL, // 参数
- 1, // 优先级
- NULL // 任务句柄
- );
-
- // 创建LED控制任务
- xTaskCreate(
- ledControlTask, // 任务函数
- "LED Control Task", // 任务名称
- 2048, // 堆栈大小
- NULL, // 参数
- 1, // 优先级
- NULL // 任务句柄
- );
-
- // 创建WiFi监控任务
- xTaskCreate(
- wifiMonitorTask, // 任务函数
- "WiFi Monitor Task", // 任务名称
- 4096, // 堆栈大小
- NULL, // 参数
- 2, // 优先级
- NULL // 任务句柄
+ 2,
+ NULL
);
Serial.println("✅ FreeRTOS任务创建成功");
@@ -726,20 +522,17 @@ void setup() {
Serial.println(String("BLE已启动,设备名称: Radar_") + String(currentDeviceId));
Serial.println("🌐 检查WiFi配置...");
- if (wifiConfigured) {
- Serial.println("💾 检测到已保存的WiFi配置,尝试连接...");
- loadWiFiConfig();// 加载WiFi配置
- if (connectWiFi()) {
+ if (wifiManager.getSavedNetworkCount() > 0) {
+ Serial.printf("💾 检测到 %d 个已保存的WiFi配置,尝试连接...\n", wifiManager.getSavedNetworkCount());
+ if (wifiManager.initializeWiFi()) {
Serial.println("✅ WiFi连接成功!");
} else {
Serial.println("❌ WiFi连接失败,请通过BLE重新配置");
- wifiConfigured = false;
}
} else {
Serial.println("⚠️ 未检测到WiFi配置,请通过BLE进行网络配置");
}
- // 从Flash加载WiFi首次连接标志(模仿getString行为:若Flash无该键,则保持当前变量值不变)
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);
@@ -749,31 +542,39 @@ void setup() {
if(WiFi_Connect_First_bit == 0)
{
- while (WiFi.status() != WL_CONNECTED) { // 等待WiFi连接
- delay(1000);
- Serial.println("等待WiFi连接...");
+ 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连接成功!");
- // 初始化R60ABD1雷达模组
- initR60ABD1(); // 调用R60ABD1初始化函数
+ initR60ABD1();
Serial.println("🎉 系统初始化完成,等待雷达数据...");
- // 启动时发送一次睡眠数据
if (WiFi.status() == WL_CONNECTED) {
Serial.println("🌅 启动时发送睡眠数据到数据库");
sendSleepDataToInfluxDB();
}
}
+/**
+ * @brief 主循环函数
+ * 处理BLE连接状态和定期发送雷达命令
+ */
void loop() {
esp_task_wdt_reset();
if (!deviceConnected && oldDeviceConnected) {
- delay(500);
+ vTaskDelay(500 / portTICK_PERIOD_MS);
pServer->startAdvertising();
Serial.println("开始BLE广播");
oldDeviceConnected = deviceConnected;
@@ -781,30 +582,28 @@ void loop() {
if (deviceConnected && !oldDeviceConnected) {
oldDeviceConnected = deviceConnected;
}
- // 使用非阻塞方式轮询发送一系列查询命令,间隔2000ms
{
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} // 睡眠质量评级
+ {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; // 2秒
+ 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++;
@@ -815,1904 +614,6 @@ void loop() {
processBLEConfig();
esp_task_wdt_reset();
esp_task_wdt_reset();
- // updateStatusFlags();
- // 在主循环中添加更频繁的看门狗重置
esp_task_wdt_reset();
- delay(1);
}
-
-// 处理查询状态命令
-bool processQueryStatus(JsonDocument& doc) {
- const char* command = doc["command"];
- if (command != nullptr && strcmp(command, "queryStatus") == 0) {
- // 发送设备状态信息
- if (deviceConnected) {
- String statusMsg = String("{\"type\":\"deviceStatus\",\"success\":true,\"deviceId\":") +
- String(currentDeviceId) +
- String(",\"wifiConfigured\":") +
- String(wifiConfigured ? "true" : "false") +
- String(",\"wifiConnected\":") +
- String(WiFi.status() == WL_CONNECTED ? "true" : "false") +
- String(",\"ipAddress\":\"") +
- (WiFi.status() == WL_CONNECTED ? WiFi.localIP().toString() : "") +
- String("\"}");
-
- // 使用新的分包发送函数发送JSON数据
- sendJSONDataToBLE(statusMsg);
- Serial.println("已发送设备状态信息");
- }
- return true;
- }
- return false;
-}
-
-// 处理查询雷达数据命令(蓝牙)
-//这个函数是一个响应式的数据发送函数,仅在接收到查询请求时才会发送当前的雷达传感器数据
-bool processQueryRadarData(JsonDocument& doc) {
- const char* command = doc["command"];
- if (command != nullptr && strcmp(command, "queryRadarData") == 0) {
- Serial.println("收到查询雷达数据命令");
-
- // 发送最新的雷达数据
- if (deviceConnected) {
- String radarDataMsg = String("{\"type\":\"radarData\",\"success\":true") +
- String(",\"deviceId\":") + String(currentDeviceId) + // 设备ID
- String(",\"timestamp\":") + String(millis()) + // 时间戳
- String(",\"presence\":") + String(sensorData.presence) +
- String(",\"heartRate\":") + String(sensorData.heart_rate, 1) +
- String(",\"breathRate\":") + String(sensorData.breath_rate, 1) +
- String(",\"motion\":") + String(sensorData.motion) +
- String(",\"heartbeatWaveform\":") + String((int)sensorData.breath_waveform[0]) +
- String(",\"breathingWaveform\":") + String((int)sensorData.heart_waveform[0]) +
- String(",\"distance\":") + String(sensorData.distance) +
- String(",\"bodyMovement\":") + String(sensorData.body_movement) +
- String(",\"breathStatus\":") + String(sensorData.breath_status) +
- String(",\"sleepState\":") + String(sensorData.sleep_state) +
- String(",\"sleepTime\":") + String(sensorData.sleep_time) +
- String(",\"sleepScore\":") + String(sensorData.sleep_score) +
- String(",\"avgHeartRate\":") + String(sensorData.avg_heart_rate) +
- String(",\"avgBreathRate\":") + String(sensorData.avg_breath_rate) +
- String(",\"turnCount\":") + String(sensorData.turn_count) +
- String(",\"largeMoveRatio\":") + String(sensorData.large_move_ratio) +
- String(",\"smallMoveRatio\":") + String(sensorData.small_move_ratio) +
- String("}");
-
- // 使用新的分包发送函数发送JSON数据
- sendJSONDataToBLE(radarDataMsg);
- Serial.println("已发送雷达数据");
- Serial.printf("发送的数据: %s\n", radarDataMsg.c_str());
- } else {
- Serial.println("BLE未连接,无法发送雷达数据");
- }
- return true;
- }
- return false;
-}
-
-// 处理开始持续发送命令
-bool processStartContinuousSend(JsonDocument& doc) {
- const char* command = doc["command"];
- if (command != nullptr && strcmp(command, "startContinuousSend") == 0) {
- // 获取发送间隔参数(可选,默认1000ms)
- if (doc["interval"].is()) {
- continuousSendInterval = doc["interval"].as();
- // 限制最小间隔为100ms,最大间隔为10000ms
- if (continuousSendInterval < 100) continuousSendInterval = 100;
- if (continuousSendInterval > 10000) continuousSendInterval = 10000;
- }
-
- continuousSendEnabled = true;
- bleFlow.reset(); // 重置流量控制器
-
- Serial.printf("⚙️ 启动持续发送模式,间隔: %lu ms\n", continuousSendInterval);
-
- // 发送JSON格式的确认消息
- if (deviceConnected) {
- String confirmMsg = String("{\"type\":\"startContinuousSendResult\",\"success\":true,\"message\":\"已启动持续发送模式\",\"interval\":") +
- String(continuousSendInterval) + "}";
-
- // 使用新的分包发送函数发送JSON数据
- sendJSONDataToBLE(confirmMsg);
- Serial.println("✅ 启动确认消息发送成功");
-
- delay(5); // 减少延迟以提高实时性
- Serial.println("🚀 已启动持续发送模式");
- } else {
- Serial.println("❌ BLE未连接,无法发送确认消息");
- }
- return true;
- }
- return false;
-}
-
-// 处理停止持续发送命令
-bool processStopContinuousSend(JsonDocument& doc) {
- const char* command = doc["command"];
- if (command != nullptr && strcmp(command, "stopContinuousSend") == 0) {
- continuousSendEnabled = false;
-
- Serial.println("🛑 停止持续发送模式");
-
- // 发送JSON格式的确认消息
- if (deviceConnected) {
- String confirmMsg = String("{\"type\":\"stopContinuousSendResult\",\"success\":true,\"message\":\"已停止持续发送模式\"}");
-
- // 使用新的分包发送函数发送JSON数据
- sendJSONDataToBLE(confirmMsg);
- Serial.println("✅ 停止确认消息发送成功");
-
- delay(5); // 减少延迟以提高实时性
- Serial.println("⏹️ 已停止持续发送模式");
- } else {
- Serial.println("❌ BLE未连接,无法发送确认消息");
- }
- return true;
- }
- return false;
-}
-
-// 分包发送函数 - 优化版本,解决分包混乱和数据截断问题
-void sendDataInChunks(const String& data) {
- const int MAX_PACKET_SIZE = 20; // BLE最大包大小
- const int HEADER_SIZE = 6; // 包头大小 "[N/M]"
- const int CHUNK_SIZE = MAX_PACKET_SIZE - HEADER_SIZE; // 实际数据大小
-
- int totalLength = data.length();
- int numChunks = (totalLength + CHUNK_SIZE - 1) / CHUNK_SIZE;
-
- // 只有在数据较长需要分包时才显示分包信息
- Serial.printf("📦 开始分包发送,总长度: %d, 分包数: %d\n", totalLength, numChunks);
-
- for(int i = 0; i < numChunks; i++) {
- int start = i * CHUNK_SIZE;
- int chunkLength = min(CHUNK_SIZE, totalLength - start);
- String chunk = data.substring(start, start + chunkLength);
-
- // 构造简单包头: "[序号/总数]",确保总长度不超过6字符
- // 例如: "[1/5]" = 4字符
- String packetHeader = String("[") + String(i+1) + String("/") + String(numChunks) + String("]");
-
- // 确保总包大小不超过20字节
- int maxDataLength = MAX_PACKET_SIZE - packetHeader.length();
- if (chunk.length() > maxDataLength) {
- chunk = chunk.substring(0, maxDataLength);
- }
-
- String packet = packetHeader + chunk;
-
- Serial.printf("📤 发送分包 %s: %s\n", packetHeader.c_str(), chunk.c_str());
-
- // 检查BLE连接状态
- if (!deviceConnected) {
- Serial.println("❌ BLE未连接,无法发送数据");
- return;
- }
-
- // 发送数据包
- pCharacteristic->setValue(packet.c_str());
- pCharacteristic->notify();
- Serial.println("✅ 分包发送成功");
-
- // 等待一段时间确保接收端处理完成,避免数据丢失
- if (i < numChunks - 1) {
- Serial.printf("⏳ 等待接收端处理第%d个包...\n", i+1);
- delay(20); // 进一步减少等待时间以提高实时性
- }
- }
-
- Serial.println("📦 分包发送完成");
-}
-
-// 新增:发送JSON数据到BLE(使用纯数据分包发送,不带包头)
-void sendJSONDataToBLE(const String& jsonData) {
- Serial.printf("📤 准备发送JSON数据: %s\n", jsonData.c_str());
-
- // 检查BLE连接状态
- if (!deviceConnected) {
- Serial.println("❌ BLE未连接,无法发送JSON数据");
- return;
- }
-
- // 直接发送JSON数据,使用纯数据分包(不带包头)
- const int MAX_PACKET_SIZE = 20; // BLE最大包大小
- int totalLength = jsonData.length();
- int numChunks = (totalLength + MAX_PACKET_SIZE - 1) / MAX_PACKET_SIZE;
-
- Serial.printf("📦 开始分包发送JSON,总长度: %d, 分包数: %d\n", totalLength, numChunks);
-
- for(int i = 0; i < numChunks; i++) {
- int start = i * MAX_PACKET_SIZE;
- int chunkLength = min(MAX_PACKET_SIZE, totalLength - start);
- String chunk = jsonData.substring(start, start + chunkLength);
-
- Serial.printf("📤 发送JSON分包 %d/%d: %s\n", i+1, numChunks, chunk.c_str());
-
- // 发送数据包
- pCharacteristic->setValue(chunk.c_str());
- pCharacteristic->notify();
- Serial.println("✅ JSON分包发送成功");
-
- // 等待一段时间确保接收端处理完成,避免数据丢失
- if (i < numChunks - 1) {
- Serial.printf("⏳ 等待接收端处理第%d个包...\n", i+1);
- delay(10); // 减少等待时间以提高实时性
- }
- }
-
- Serial.println("📦 JSON分包发送完成");
-}
-
-// 新增:发送自定义JSON数据到BLE
-bool sendCustomJSONData(const String& jsonType, const String& jsonString) {
- if (!deviceConnected) {
- Serial.println("❌ BLE未连接,无法发送自定义JSON数据");
- return false;
- }
-
- // 构造完整的JSON数据
- String fullJSON = String("{\"type\":\"") + jsonType + String("\",") + jsonString + String("}");
-
- Serial.printf("📤 发送自定义JSON数据类型 '%s': %s\n", jsonType.c_str(), fullJSON.c_str());
-
- // 使用纯数据分包发送函数发送JSON数据
- sendJSONDataToBLE(fullJSON);
-
- return true;
-}
-
-// 发送雷达数据到BLE(已移至FreeRTOS任务处理)
-void sendRadarDataToBLE() {
- // 此函数已废弃,雷达数据发送现在由FreeRTOS任务处理
- // 保留此函数以防其他代码引用
- Serial.println("ℹ️ 雷达数据发送已移至FreeRTOS任务处理");
-}
-
-// 处理BLE配置和命令
-void processBLEConfig() {
- // 增强超时处理机制
- if (completeData.length() > 0 && (millis() - lastReceiveTime > 3000)) {
- Serial.println("⏰ [超时] 数据接收超时3秒,清空缓冲区");
- completeData = "";
- }
-
- if (receivedData.length() > 0) {
- String bleData = receivedData;
- receivedData = "";
- bleData.trim();
-
- Serial.printf("⚙️ [BLE] 准备解析JSON: %s\n", bleData.c_str());
-
- if (bleData.startsWith("{") && bleData.endsWith("}")) {
- JsonDocument doc;
- DeserializationError error = deserializeJson(doc, bleData);
-
- if (error) {
- String errorMsg = String("❌ [BLE] JSON解析失败: ") + String(error.c_str());
- Serial.println(errorMsg);
- if (deviceConnected) {
- String responseMsg = String("{\"type\":\"error\",\"message\":\"配置格式错误,请使用JSON格式\"}");
-
- // 使用新的分包发送函数发送JSON数据
- sendJSONDataToBLE(responseMsg);
- }
- } else {
- Serial.println("✅ [BLE] JSON解析成功");
-
- // 处理各种命令
- bool processed = false;
-
- // 处理设置设备ID命令
- if (!processed) processed = processSetDeviceId(doc);
-
- // 处理WiFi配置命令
- if (!processed) processed = processWiFiConfigCommand(doc);
-
- // 处理查询状态命令
- if (!processed) processed = processQueryStatus(doc);
-
- // 处理查询雷达数据命令
- if (!processed) processed = processQueryRadarData(doc);
-
- // 处理开始持续发送命令
- if (!processed) processed = processStartContinuousSend(doc);
-
- // 处理停止持续发送命令
- if (!processed) processed = processStopContinuousSend(doc);
-
- // 如果没有处理任何命令,发送错误响应
- if (!processed) {
- Serial.println("❓[BLE] 未知命令");
- if (deviceConnected) {
- String responseMsg = String("{\"type\":\"error\",\"message\":\"未知命令\"}");
-
- // 使用新的分包发送函数发送JSON数据
- sendJSONDataToBLE(responseMsg);
- }
- }
- }
- } else {
- Serial.println("📥 [BLE] 接收到非JSON数据");
- }
- }
-}
-
-// FreeRTOS任务:蓝牙数据发送(精简格式以节省空间)
-//主动发送:这是一个持续运行的FreeRTOS任务,不断地从队列中获取数据并主动发送到蓝牙,用于实时数据传输
-void bleSendTask(void *parameter)
-{
- Serial.println("🔁 R60ABD1蓝牙数据发送任务启动");
-
- while (1) {
- PhaseData phaseData;
-
- // 从队列接收相位数据用于蓝牙发送
- if (xQueueReceive(phaseDataQueue, &phaseData, portMAX_DELAY) == pdTRUE) {
- esp_task_wdt_reset();
-
- // 蓝牙传输 - 构造并发送完整的R60ABD1雷达数据
- if (deviceConnected && continuousSendEnabled) {
- String radarDataCore;
-
- // 检查是否检测到人
- if (sensorData.presence > 0)
- {
- // 检测到人时,发送实际数据,包含R60ABD1扩展功能
- radarDataCore = String(sensorData.heart_rate, 1) + String("|") +
- String(sensorData.breath_rate, 1) + String("|") +
- String((int)sensorData.heart_waveform[0]) + String("|") +
- String((int)sensorData.breath_waveform[0]) + String("|") +
- String(sensorData.presence) + String("|") +
- String(sensorData.motion) + String("|") +
- // String(sensorData.distance) + String("|") + // R60ABD1距离
- String(sensorData.sleep_state); // R60ABD1睡眠状态
- // String(sensorData.sleep_time) + String("|") + // R60ABD1睡眠时长
- // String(sensorData.sleep_score); // R60ABD1睡眠评分
- }
- else {
- // 未检测到人时,发送零数据,但仍然包含实际的RSSI值
- radarDataCore = String("0.0") + String("|") +
- String("0.0") + String("|") +
- String("0") + String("|") +
- String("0") + String("|") +
- String("0") + String("|") +
- String("0") + String("|") +
- // String("0") + String("|") + // 距离
- String("0"); // 睡眠状态
- // String("0") + String("|") + // 睡眠时长
- // String("0"); // 睡眠评分
- }
-
- // 计算CRC校验和
- unsigned int crc = 0xFFFF;
- for (int i = 0; i < radarDataCore.length(); i++) {
- crc ^= (unsigned int)radarDataCore.charAt(i);
- for (int j = 0; j < 8; j++) {
- if (crc & 0x0001) {
- crc >>= 1;
- crc ^= 0xA001;
- } else {
- crc >>= 1;
- }
- }
- }
-
- // 添加校验和到数据末尾
- String radarDataMsg = radarDataCore + String("|") + String(crc, HEX);
-
- Serial.printf("📤 通过蓝牙发送R60ABD1雷达数据: %s\n", radarDataMsg.c_str());
-
- // 检查数据长度决定发送方式
- const int MAX_BLE_PACKET_SIZE = 20; // BLE最大包大小
- if (radarDataMsg.length() <= MAX_BLE_PACKET_SIZE) {
- // 数据较短,直接发送
- pCharacteristic->setValue(radarDataMsg.c_str());
- pCharacteristic->notify();
- Serial.println("✅ R60ABD1雷达数据蓝牙发送成功");
- } else {
- // 数据较长,使用分包发送
- Serial.println("🔄 R60ABD1雷达数据较长,使用分包发送");
- sendDataInChunks(radarDataMsg);
- }
- }
-
- esp_task_wdt_reset();
- }
-
- vTaskDelay(1 / portTICK_PERIOD_MS); // 减少延迟以提高实时性
- // 在BLE任务循环中重置看门狗
- esp_task_wdt_reset();
- }
-}
-// FreeRTOS任务:发送生命体征数据到数据库
-void vitalSendTask(void *parameter) {
- Serial.println("🔁🔁 生命体征数据发送任务启动(WiFi数据库传输)");
-
- // 记录上次发送睡眠数据的时间
- unsigned long lastSleepDataTime = 0;
- const unsigned long SLEEP_DATA_INTERVAL = 5000; // 5秒 = 5000毫秒
-
- while (1) {
- VitalData vitalData;
-
- // 从队列接收生命体征数据用于WiFi数据库发送
- if (xQueueReceive(vitalDataQueue, &vitalData, portMAX_DELAY) == pdTRUE) {
- esp_task_wdt_reset();
-
- // WiFi数据库传输 - 按照Python脚本的双模块架构发送数据
- if (WiFi.status() == WL_CONNECTED) {
- // 发送日常数据(高频实时监测数据)- daily_data
- // 构造日常数据行协议
- String dailyDataLine = "daily_data,deviceId=" + String(currentDeviceId) + ",dataType=daily ";
-
- // 添加各个实时监测字段
- bool firstField = true;
-
- if (vitalData.heart_rate > 0) {
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "heartRate=" + String(vitalData.heart_rate, 1);// 心率
- firstField = false;
- }
-
- if (vitalData.breath_rate > 0) {
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "breathingRate=" + String(vitalData.breath_rate, 1);// 呼吸率
- firstField = false;
- }
-
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "personDetected=" + String(vitalData.presence) + "i";// 人检
- firstField = false;
-
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "humanActivity=" + String(vitalData.motion) + "i";// 活动
- firstField = false;
-
- if (vitalData.distance > 0) {
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "humanDistance=" + String(vitalData.distance) + "i";// 距离
- firstField = false;
- }
-
- if (vitalData.sleep_state >= 0) {
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "sleepState=" + String(vitalData.sleep_state) + "i";// 睡眠状态
- firstField = false;
- }
-
- // 添加人体坐标数据 (DP5) - X坐标
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "humanPositionX=" + String(vitalData.pos_x) + "i";// X坐标
- firstField = false;
-
- // 添加人体坐标数据 (DP5) - Y坐标
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "humanPositionY=" + String(vitalData.pos_y) + "i";// Y坐标
- firstField = false;
-
- // 添加人体坐标数据 (DP5) - Z坐标
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "humanPositionZ=" + String(vitalData.pos_z) + "i";// Z坐标
- firstField = false;
-
- // 添加波形数据
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "heartbeatWaveform=" + String((int)sensorData.heart_waveform[0]) + "i";// 心跳波形
- firstField = false;
-
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "breathingWaveform=" + String((int)sensorData.breath_waveform[0]) + "i";// 呼吸波形
- firstField = false;
-
- // 添加异常状态
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "abnormalState=" + String(vitalData.abnormal_state) + "i";// 异常状态
- firstField = false;
-
- // 添加入床状态
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "bedStatus=" + String(vitalData.bed_status) + "i";// 入床状态
- firstField = false;
-
- // 挣扎警报和无人警报
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "struggleAlert=" + String(vitalData.struggle_alert) + "i";// 挣扎警报
- firstField = false;
-
- if (!firstField) dailyDataLine += ",";
- dailyDataLine += "noOneAlert=" + String(vitalData.no_one_alert) + "i";// 无人警报
- firstField = false;
-
- // 发送日常数据到数据库
- if (!dailyDataLine.endsWith(" ")) { // 确保有数据要发送
- sendDailyDataToInfluxDB(dailyDataLine);
- esp_task_wdt_reset();
- delay(10);
- }
-
- // 检查是否需要发送睡眠数据(低频汇总数据)- sleep_data
- unsigned long currentTime = millis();
- if (currentTime - lastSleepDataTime >= SLEEP_DATA_INTERVAL) {
- sendSleepDataToInfluxDB();
- lastSleepDataTime = currentTime;
- Serial.println("⏰ 睡眠数据定时发送完成");
- }
- }
- } else {
- Serial.println("❌❌ WiFi未连接,无法发送雷达数据到数据库");
-
- // 定期打印WiFi状态以便调试
- static unsigned long lastWifiCheck = 0;
- if (millis() - lastWifiCheck > 10000) {
- Serial.printf("📶📶 WiFi状态: %d, 已配置: %s\n",
- WiFi.status(), wifiConfigured ? "是" : "否");
- lastWifiCheck = millis();
- }
- }
-
- esp_task_wdt_reset();
- }
-
- vTaskDelay(10 / portTICK_PERIOD_MS); // 适当延迟
- }
-
-
-// 发送日常数据到InfluxDB - 高频实时监测数据
-void sendDailyDataToInfluxDB(String dailyDataLine) {
- if (WiFi.status() != WL_CONNECTED) {
- Serial.println("❌ WiFi未连接,无法发送日常数据到数据库");
- return;
- }
-
- HTTPClient http;
- http.setTimeout(2000);
-
- String url = String("http://") + String(influxDBHost) + ":" + String(influxDBPort) + "/api/v2/write?org=" + String(influxDBOrg) + "&bucket=" + String(influxDBBucket);
-
- http.begin(url);
- http.addHeader("Authorization", String("Token ") + String(influxDBToken));
- http.addHeader("Content-Type", "text/plain; charset=utf-8");
- http.setReuse(true);
-
- Serial.println(String("📊 发送日常数据到InfluxDB: ") + dailyDataLine);
-
- int httpResponseCode = http.POST(dailyDataLine);// 发送日常数据到InfluxDB
-
- if (httpResponseCode == 204) {
- Serial.println("✅ 日常数据发送成功");
- } else {
- Serial.println(String("❌ 发送日常数据失败: ") + String(httpResponseCode) + " - " + http.getString());
- }
-
- http.end();
-}
-
-// 发送睡眠数据到InfluxDB - 模仿Python脚本的低频发送模式
-//这个函数是专门用于发送睡眠数据到InfluxDB数据库的
-void sendSleepDataToInfluxDB() {
- if (WiFi.status() != WL_CONNECTED) {
- Serial.println("❌ WiFi未连接,无法发送睡眠数据到数据库");
- return;
- }
-
- // 检查总睡眠时长是否为0,如果为0则不上传数据
- if (sensorData.sleep_total_time == 0) {
- Serial.println("😴 总睡眠时长为0,跳过上传睡眠数据");
- return;
- }
-
- HTTPClient http;
- http.setTimeout(2000);
-
- String url = String("http://") + String(influxDBHost) + ":" + String(influxDBPort) + "/api/v2/write?org=" + String(influxDBOrg) + "&bucket=" + String(influxDBBucket);
-
- http.begin(url);
- http.addHeader("Authorization", String("Token ") + String(influxDBToken));
- http.addHeader("Content-Type", "text/plain; charset=utf-8");
- http.setReuse(true);
-
- // 构造睡眠数据的行协议格式
- String lineProtocol = String("sleep_data,deviceId=") + String(currentDeviceId) + ",dataType=sleep ";
-
- // 按顺序添加各个字段 - 构建睡眠分析数据
- String fields = "";
- fields += String("sleepQualityScore=") + String((int)sensorData.sleep_score) + "i";//睡眠评分
- fields += ",sleepQualityGrade=" + String((int)sensorData.sleep_grade) + "i";//睡眠质量评级
- fields += ",totalSleepDuration=" + String((int)sensorData.sleep_total_time) + "i";// 总睡眠时长
- fields += ",awakeDurationRatio=" + String((int)sensorData.awake_ratio) + "i";// 清醒时长占比
- fields += ",lightSleepRatio=" + String((int)sensorData.light_sleep_ratio) + "i";// 浅睡时长占比
- fields += ",deepSleepRatio=" + String((int)sensorData.deep_sleep_ratio) + "i";// 深睡时长占比
- fields += ",outOfBedDuration=" + String((int)sensorData.bed_Out_Time) + "i";// 离床时长
- fields += ",outOfBedCount=" + String((int)sensorData.turn_count) + "i";// 离床次数
- fields += ",turnCount=" + String((int)sensorData.turnover_count) + "i";// 转身次数
- fields += ",avgBreathingRate=" + String((int)sensorData.avg_breath_rate) + "i";// 平均呼吸率
- fields += ",avgHeartRate=" + String((int)sensorData.avg_heart_rate) + "i";// 平均心率
- fields += ",apneaCount=" + String((int)sensorData.apnea_count) + "i";// 呼吸暂停次数
- fields += ",abnormalState=" + String((int)sensorData.abnormal_state) + "i";// 睡眠异常状态
- fields += ",bodyMovement=" + String((int)sensorData.body_movement) + "i";// 体动幅度
- fields += ",breathStatus=" + String((int)sensorData.breath_status) + "i";// 呼吸状态
- fields += ",sleepState=" + String((int)sensorData.sleep_state) + "i";// 睡眠状态
- fields += ",largeMoveRatio=" + String((int)sensorData.large_move_ratio) + "i";// 大幅度体动占比
- fields += ",smallMoveRatio=" + String((int)sensorData.small_move_ratio) + "i";// 小幅度体动占比
- fields += ",struggleAlert=" + String((int)sensorData.struggle_alert) + "i";// 挣扎警报
- fields += ",noOneAlert=" + String((int)sensorData.no_one_alert) + "i";// 无人警报
- fields += ",awakeDuration=" + String((int)sensorData.awake_time) + "i";// 清醒时长
- fields += ",lightSleepDuration=" + String((int)sensorData.light_sleep_time) + "i";// 浅睡时长
- fields += ",deepSleepDuration=" + String((int)sensorData.deep_sleep_time) + "i";// 深睡时长
- // // 添加波形数组数据(前3个点)
- // fields += ",breathWaveform1=" + String((int)sensorData.breath_waveform[0]) + "i";// 呼吸波形点1
- // fields += ",breathWaveform2=" + String((int)sensorData.breath_waveform[1]) + "i";// 呼吸波形点2
- // fields += ",breathWaveform3=" + String((int)sensorData.breath_waveform[2]) + "i";// 呼吸波形点3
- // fields += ",heartWaveform1=" + String((int)sensorData.heart_waveform[0]) + "i";// 心率波形点1
- // fields += ",heartWaveform2=" + String((int)sensorData.heart_waveform[1]) + "i";// 心率波形点2
- // fields += ",heartWaveform3=" + String((int)sensorData.heart_waveform[2]) + "i";// 心率波形点3
-
- lineProtocol += fields;
-
- Serial.println(String("🌙 发送睡眠数据到InfluxDB: ") + lineProtocol);
-
- int httpResponseCode = http.POST(lineProtocol);// 发送睡眠数据到InfluxDB
-
- if (httpResponseCode == 204) {
- Serial.println(String("✅ 睡眠数据已保存到InfluxDB设备") + String(currentDeviceId) + "上");
- } else {
- Serial.println(String("❌ 保存睡眠数据到InfluxDB失败: ") + String(httpResponseCode) + " - " + http.getString());
- }
-
- http.end();
-}
-
-// 雷达数据处理任务 - 高优先级
-void radarDataTask(void *parameter) {
- Serial.println("🔁 雷达数据处理任务启动(最高优先级)");
-
- while (1) {
- // 雷达数据处理已移至专用任务处理,这里不需要做任何事情
- // 串口数据现在由uartProcessTask处理
-
- // 短暂延迟以允许其他任务运行
- vTaskDelay(100 / portTICK_PERIOD_MS);
- }
-}
-
-
-// 修改串口处理任务以支持二进制协议
-//存储数据:将解析后的数据分别存储到两个结构体中:vitalData,phaseData
-void uartProcessTask(void *parameter) {
- uint8_t buffer[256]; // 接收缓冲区
- int bufferIndex = 0;
- bool inFrame = false;
- uint8_t prevByte = 0;
-
- Serial.println("✅ R60ABD1串口数据处理任务启动");
-
- while(1) {
- uint8_t c;
- // 从队列接收字节(最多等待10ms)
- if(xQueueReceive(uartQueue, &c, 10 / portTICK_PERIOD_MS) == pdTRUE) {
- // 重置看门狗,防止超时
- esp_task_wdt_reset();
- if(!inFrame) {
- // 寻找帧头
- if(prevByte == FRAME_HEADER1 && c == FRAME_HEADER2) {
- // 找到帧头,开始接收帧
- buffer[0] = FRAME_HEADER1;
- buffer[1] = FRAME_HEADER2;
- bufferIndex = 2;
- inFrame = true;
- Serial.println("🔍 检测到R60ABD1帧头");
- }
- } else {
- // 在帧中接收数据
- if(bufferIndex < sizeof(buffer)) {
- buffer[bufferIndex++] = c;
-
- // 检查是否到达帧尾
- if(bufferIndex >= 8 && // 至少包含基本帧头信息
- buffer[bufferIndex-2] == FRAME_TAIL1 &&
- buffer[bufferIndex-1] == FRAME_TAIL2) {
- // 完整帧接收完成,进行解析
- if(parseR60ABD1Frame(buffer, bufferIndex)) {
- // 数据解析成功,更新统计
- static uint32_t frameCounter = 0;
- frameCounter++;
-
- // 更新全局传感器数据
- lastSensorUpdate = millis();
-
- // 使用FreeRTOS队列发送数据到任务
- static uint32_t phasePacketCounter = 0;
- static uint32_t vitalPacketCounter = 0;
-
- phasePacketCounter++;
- if (phasePacketCounter >= PHASE_SEND_INTERVAL) {
- PhaseData phaseData;
- phaseData.heartbeat_waveform = sensorData.heartbeat_waveform;// 心率波形数据
- phaseData.breathing_waveform = sensorData.breathing_waveform;// 呼吸波形数据
-
- if (xQueueSend(phaseDataQueue, &phaseData, 0) == pdTRUE) {
- // 成功发送到队列
- } else {
- Serial.println("❌ 相位数据队列已满,数据丢失");
- }
- phasePacketCounter = 0;
- }
-
- vitalPacketCounter++;
- if (vitalPacketCounter >= VITAL_SEND_INTERVAL) {
- VitalData vitalData;
- vitalData.heart_rate = sensorData.heart_rate;// 心率数据
- vitalData.breath_rate = sensorData.breath_rate;// 呼吸率数据
- vitalData.presence = sensorData.presence;// 人员检测数据
- vitalData.motion = sensorData.motion;// 运动数据
- vitalData.distance = sensorData.distance;// 距离数据
- vitalData.sleep_state = sensorData.sleep_state;// 睡眠状态数据
- vitalData.sleep_score = sensorData.sleep_score;// 睡眠评分数据(无)
- vitalData.body_movement = sensorData.body_movement;// 体动数据
- vitalData.breath_status = sensorData.breath_status;// 呼吸状态数据(去)
- vitalData.sleep_time = sensorData.sleep_time;// 睡眠时间数据
- vitalData.bed_status = sensorData.bed_status;//入床状态数据
- vitalData.abnormal_state = sensorData.abnormal_state;// 异常状态数据
- vitalData.avg_heart_rate = sensorData.avg_heart_rate;// 平均心率数据
- vitalData.avg_breath_rate = sensorData.avg_breath_rate;// 平均呼吸率数据
- vitalData.turn_count = sensorData.turn_count;// 离床次数数据
- vitalData.large_move_ratio = sensorData.large_move_ratio;// 大动作比例数据
- vitalData.small_move_ratio = sensorData.small_move_ratio;// 小动作比例数据
- vitalData.pos_x = sensorData.pos_x;// X坐标数据
- vitalData.pos_y = sensorData.pos_y;// Y坐标数据
- vitalData.pos_z = sensorData.pos_z;// Z坐标数据
- vitalData.deep_sleep_time = sensorData.deep_sleep_time;// 深睡时间数据
- vitalData.light_sleep_time = sensorData.light_sleep_time;// 浅睡时间数据
- vitalData.awake_time = sensorData.awake_time;// 唤醒时间数据
- vitalData.sleep_total_time = sensorData.sleep_total_time;// 睡眠总时间数据(去)
- vitalData.deep_sleep_ratio = sensorData.deep_sleep_ratio;// 深睡比例数据(去)
- vitalData.light_sleep_ratio = sensorData.light_sleep_ratio;// 浅睡比例数据(去)
- vitalData.awake_ratio = sensorData.awake_ratio;// 清醒比例数据(去)
- vitalData.turnover_count = sensorData.turnover_count;// 离床次数数据(去)
- vitalData.struggle_alert = sensorData.struggle_alert;// 挤压警报数据
- vitalData.no_one_alert = sensorData.no_one_alert;// 无人警报数据
- vitalData.apnea_count = sensorData.apnea_count;// 呼吸暂停次数数据(去)
- vitalData.heartbeat_waveform = sensorData.heartbeat_waveform;// 心率波形数据
- vitalData.breathing_waveform = sensorData.breathing_waveform;// 呼吸波形数据
-
- if (xQueueSend(vitalDataQueue, &vitalData, 0) == pdTRUE) {
- Serial.println("📤 生命体征数据已加入发送队列");
- } else {
- Serial.println("❌ 生命体征数据队列已满,数据丢失");
- }
- vitalPacketCounter = 0;
- }
-
- if(frameCounter % 100 == 0) {
- Serial.printf("📈 已处理 %d 个R60ABD1数据帧\n", frameCounter);
- }
-
- }
-
- // 重置状态,准备接收下一帧
- inFrame = false;
- }
- } else {
- // 缓冲区溢出,重置状态
- Serial.println("⚠️ R60ABD1帧缓冲区溢出,重置接收状态");
- inFrame = false;
- }
- }
-
- prevByte = c;
- }
-
- // 允许其他任务运行
- vTaskDelay(1 / portTICK_PERIOD_MS);
- // 在循环中定期重置看门狗
- esp_task_wdt_reset();
- }
-}
-
-// 替换现有的parseSensorLine函数
-bool parseR60ABD1Frame(uint8_t *frame, uint16_t frameLen) {
- if(frameLen < 8) return false; // 最小帧长度检查
-
- // 验证帧头和帧尾
- if(frame[0] != FRAME_HEADER1 || frame[1] != FRAME_HEADER2 ||
- frame[frameLen-2] != FRAME_TAIL1 || frame[frameLen-1] != FRAME_TAIL2) {
- return false;
- }
-
- // 验证校验和
- uint8_t checksum = 0;
- for(int i = 0; i < frameLen-3; i++) { // 不包括校验和字节和帧尾
- checksum += frame[i];
- // 在长循环中重置看门狗,防止超时
- if(i % 50 == 0) {
- esp_task_wdt_reset();
- }
- }
- if(checksum != frame[frameLen-3]) { // 校验和在倒数第三个字节
- Serial.println("❌ R60ABD1帧校验和错误");
- return false;
- }
-
- for(int i = 0; i < frameLen && i < 20; i++) { // 显示前20个字节
- Serial.printf("%02X ", frame[i]);
- }
- if(frameLen > 20) Serial.print("... ");
- Serial.printf("| 校验:0x%02X\n", frame[frameLen-3]);
-
- // 提取帧内容
- uint8_t ctrlByte = frame[2]; // 控制字
- uint8_t cmdByte = frame[3]; // 命令字
- uint16_t dataLen = (frame[4] << 8) | frame[5]; // 数据长度
-
- // 根据控制字和命令字处理不同类型的数据
- switch(ctrlByte)
- {
- case CTRL_PRESENCE:
- // 根据技术文档,0x80控制字的不同命令字对应不同的数据点
- switch(cmdByte)
- {
- // 开关人体存在监测
- case 0x00:
- case 0x80:
- if(dataLen >= 1) {
- if(frame[6] == 0x01) {
- Serial.println("🔄 人体存在监测功能已开启");
- sendRadarCommand(0x84, 0x00, 0x01); // 睡眠监测
- delay(50);
- } else {
- Serial.println("🔄 人体存在监测功能已关闭");
- }
- }
- break;
-
- // DP1: 存在信息主动上报
- case 0x01:
- if(dataLen >= 1) {
- sensorData.presence = frame[6]; // 0:无人, 1:有人
- Serial.printf("👤 人体存在: %s\n", sensorData.presence ? "有人" : "无人");
- }
- break;
-
- // DP2: 运动信息上报
- case 0x02:
- if(dataLen >= 1) {
- sensorData.motion = frame[6]; // 0:无, 1:静止, 2:活跃
- const char* states[] = {"无", "静止", "活跃"};
- Serial.printf("🏃 运动状态: %s\n", states[sensorData.motion]);
- }
- break;
-
- // DP3: 体动参数
- case 0x03:
- if(dataLen >= 1) {
- sensorData.body_movement = frame[6]; // 0-100
- Serial.printf("📊体动参数: %d\n", sensorData.body_movement);
- }
- break;
-
- // DP4: 人体距离
- case 0x04:
- if(dataLen >= 2) {
- sensorData.distance = ((uint16_t)frame[6] << 8) | frame[7]; // 单位:厘米
- Serial.printf("📏人体距离: %d cm\n", sensorData.distance);
- }
- break;
-
- // DP5: 人体方位坐标 - 修正版
- case 0x05:
- if(dataLen >= 6) {
- // 6字节: X(2B), Y(2B), Z(2B)
- // 从frame[6]开始是数据域
-
- // 解析X坐标
- uint16_t x_raw = ((uint16_t)frame[6] << 8) | frame[7];
- sensorData.pos_x = parseSignedCoordinate(x_raw);
-
- // 解析Y坐标
- uint16_t y_raw = ((uint16_t)frame[8] << 8) | frame[9];
- sensorData.pos_y = parseSignedCoordinate(y_raw);
-
- // 解析Z坐标
- uint16_t z_raw = ((uint16_t)frame[10] << 8) | frame[11];
- sensorData.pos_z = parseSignedCoordinate(z_raw);
-
- // 调试输出
- Serial.printf("📍方位坐标 - 原始: X=0x%04X, Y=0x%04X, Z=0x%04X\n",
- x_raw, y_raw, z_raw);
- Serial.printf(" 解析后: X=%d, Y=%d, Z=%d cm\n",
- sensorData.pos_x, sensorData.pos_y, sensorData.pos_z);
- }
- break;
-
- default:
- Serial.printf("❓未知的0x80命令字: 0x%02X\n", cmdByte);
- break;
- }
- break;
-
- case CTRL_BREATH:
- // 根据技术文档,0x81控制字的不同命令字对应不同的呼吸相关数据点
- switch(cmdByte) {
- // 开关呼吸监测功能
- case 0x00:
- case 0x80:
- if(dataLen >= 1) {
- if(frame[6] == 0x01)
- Serial.println("🔄 呼吸监测功能已开启");
- else
- Serial.println("🔄 呼吸监测功能已关闭");
- }
- break;
-
- // DP9: 呼吸信息
- case 0x01:
- if(dataLen >= 1) {
- sensorData.breath_status = frame[6];
- const char* info_str[] = {"", "正常", "呼吸过高(>25)", "呼吸过低(<10)", "无"};
- if(sensorData.breath_status >= 1 && sensorData.breath_status <= 4) {
- Serial.printf("🔍 呼吸信息: %s\n", info_str[sensorData.breath_status]);
- } else {
- Serial.printf("🔍 呼吸信息: 未知状态(0x%02X)\n", sensorData.breath_status);
- }
- }
- break;
-
- // DP8: 呼吸数值上报
- case 0x02:
- if(dataLen >= 1) {
- sensorData.breath_rate = (float)frame[6]; // 0-35次/分钟
- sensorData.breath_valid = (sensorData.breath_rate >= 0.0f &&
- sensorData.breath_rate <= 35.0f);
- Serial.printf("💨 呼吸率: %.1f 次/分\n", sensorData.breath_rate);
- }
- break;
-
- // DP10: 呼吸波形
- case 0x05:
- if(dataLen >= 5) {
- // 1秒上报5个点,每个点需要减去128
- for(int i = 0; i < 5 && i < dataLen; i++) {
- sensorData.breath_waveform[i] = (int8_t)(frame[6+i] - 128);
- }
- // 示例:打印第一个点
- Serial.printf("📈 呼吸波形: %d\n", sensorData.breath_waveform[0]);
- }
- break;
-
- default:
- Serial.printf("❓未知的0x81命令字: 0x%02X\n", cmdByte);
- break;
- }
- break;
-
- case CTRL_HEARTRATE:
- // 根据技术文档,0x85控制字的不同命令字对应不同的心率相关数据点
- switch(cmdByte) {
- // 开关心率监测功能
- case 0x00:
- case 0x80:
- if(dataLen >= 1) {
- if(frame[6] == 0x01)
- Serial.println("🔄 心率监测功能已开启");
- else
- Serial.println("🔄 心率监测功能已关闭");
- }
- break;
- // DP6: 心跳数值
- case 0x02:
- if(dataLen >= 1) {
- sensorData.heart_rate = (float)frame[6]; // 60-120次/分钟
- sensorData.heart_valid = (sensorData.heart_rate >= 60.0f &&
- sensorData.heart_rate <= 120.0f);
- Serial.printf("❤️ 心率: %.1f 次/分\n", sensorData.heart_rate);
- }
- break;
-
- // DP7: 心率波形
- case 0x05:
- if(dataLen >= 5) {
- // 1秒上报5个点,每个点需要减去128
- for(int i = 0; i < 5 && i < dataLen; i++) {
- sensorData.heart_waveform[i] = (int8_t)(frame[6+i] - 128);
- }
- Serial.printf("📈 心率波形: %d\n", sensorData.heart_waveform[0]);
- }
- break;
-
- default:
- Serial.printf("❓未知的0x85命令字: 0x%02X\n", cmdByte);
- break;
- }
- break;
-
- case CTRL_SLEEP:
- // 根据技术文档,0x84控制字的不同命令字对应不同的睡眠相关数据点
- switch(cmdByte) {
- // DP10: 开关睡眠监测功能
- case 0x00:
- case 0x80:
- if(dataLen >= 1) {
- if(frame[6] == 0x01)
- Serial.println("🔄 睡眠监测功能已开启");
- else
- Serial.println("🔄 睡眠监测功能已关闭");
- }
- break;
-
- // DP11: 入床/离床状态
- case 0x01:
- case 0x81:
- if(dataLen >= 1) {
- sensorData.bed_status = frame[6]; // 0x00:离床, 0x01:入床, 0x02:无
- const char* status_str[] = {"离床", "入床", "无"};
- Serial.printf("🛏️ 床状态: %s\n", status_str[sensorData.bed_status]);
- }
- break;
-
- // DP13: 清醒时长 (2字节,单位:分钟)
- case 0x03:
- case 0x83:
- if(dataLen >= 2) {
- sensorData.awake_time = ((uint16_t)frame[6] << 8) | (uint16_t)frame[7];
- Serial.printf("⏰ 清醒时长: %d 分钟\n", sensorData.awake_time);
- }
- break;
-
- // DP14: 浅睡时长 (2字节,单位:分钟)
- case 0x04:
- case 0x84:
- if(dataLen >= 2) {
- sensorData.light_sleep_time = ((uint16_t)frame[6] << 8) | (uint16_t)frame[7];
- Serial.printf("😪 浅睡时长: %d 分钟\n", sensorData.light_sleep_time);
- }
- break;
-
- // DP15: 深睡时长 (2字节,单位:分钟)
- case 0x05:
- case 0x85:
- if(dataLen >= 2) {
- sensorData.deep_sleep_time = ((uint16_t)frame[6] << 8) | (uint16_t)frame[7];
- Serial.printf("💤 深睡时长: %d 分钟\n", sensorData.deep_sleep_time);
- }
- break;
-
- // DP16: 睡眠质量评分 (1字节,0-100分)
- case 0x06:
- //case 0x86:
- if(dataLen >= 1) {
- sensorData.sleep_score = frame[6];
- Serial.printf("⭐ 睡眠质量评分: %d 分\n", sensorData.sleep_score);
- }
- break;
-
- case 0x86:
- if(dataLen >= 2) {
- //sensorData.sleep_score = ((uint16_t)frame[6] << 8) | (uint16_t)frame[7];
- sensorData.sleep_score = frame[6];
- Serial.printf("⭐ 睡眠质量评分: %d 分\n", sensorData.sleep_score);
- }
- break;
-
-
-
- // DP17: 睡眠综合状态 (8字节)
- case 0x0C:
- case 0x8D:
- if(dataLen >= 8) {
- sensorData.presence = frame[6]; // 存在状态: 1有人, 0无人
- sensorData.sleep_state = frame[7]; // 睡眠状态: 3离床, 2清醒, 1浅睡, 0深睡
- sensorData.avg_breath_rate = frame[8]; // 平均呼吸率
- sensorData.avg_heart_rate = frame[9]; // 平均心率
- sensorData.turnover_count = frame[10]; // 翻身次数
- sensorData.large_move_ratio = frame[11]; // 大幅度体动占比(0-100)
- sensorData.small_move_ratio = frame[12]; // 小幅度体动占比(0-100)
- sensorData.apnea_count = frame[13]; // 呼吸暂停次数
-
- Serial.printf("📊 睡眠综合状态 - 存在:%d, 睡眠状态:%d, 睡眠平均呼吸:%d, 睡眠平均心率:%d, 翻身次数:%d, 大动占比:%d%%, 小动占比:%d%%, 呼吸暂停次数:%d\n",
- sensorData.presence, sensorData.sleep_state,
- sensorData.avg_breath_rate, sensorData.avg_heart_rate,
- sensorData.turnover_count, sensorData.large_move_ratio,
- sensorData.small_move_ratio, sensorData.apnea_count);
- }
- break;
-
- // DP18: 睡眠质量分析报告 (12字节)
- case 0x0D:
- case 0x8F:
- if(dataLen >= 12) {
- sensorData.sleep_score = frame[6]; // 1B 睡眠评分
- sensorData.sleep_total_time = ((uint16_t)frame[7] << 8) | (uint16_t)frame[8]; // 2B 总时长(分钟)
-
- // 修正:以下三项为1字节的百分比,非2字节的绝对时长
- sensorData.awake_ratio = frame[9]; // 1B 清醒时长占比
- sensorData.light_sleep_ratio = frame[10]; // 1B 浅睡时长占比
- sensorData.deep_sleep_ratio = frame[11]; // 1B 深睡时长占比
-
- sensorData.bed_Out_Time = frame[12]; // 1B 离床时长
- sensorData.turn_count = frame[13]; // 1B 离床次数
- sensorData.turnover_count = frame[14]; // 1B 翻身次数
- sensorData.avg_breath_rate = frame[15]; // 1B 平均呼吸率
- sensorData.avg_heart_rate = frame[16]; // 1B 平均心跳
- sensorData.apnea_count = frame[17]; // 1B 呼吸暂停次数
-
- // 打印日志也应相应修改
- Serial.printf("📈 睡眠分析报告 - 评分:%d, 总时长:%d分, 清醒占比:%d%%, 浅睡占比:%d%%, 深睡占比:%d%%, 离床时长:%d, 离床次数:%d, 翻身:%d次, 平均呼吸:%d, 平均心跳:%d\n",
- sensorData.sleep_score,
- sensorData.sleep_total_time,
- sensorData.awake_ratio,
- sensorData.light_sleep_ratio,
- sensorData.deep_sleep_ratio,
- sensorData.bed_Out_Time,
- sensorData.turn_count,
- sensorData.turnover_count,
- sensorData.avg_breath_rate,
- sensorData.avg_heart_rate);
- }
- break;
-
- // DP19: 睡眠异常上报
- case 0x0E:
- case 0x8E:
- if(dataLen >= 1) {
- sensorData.abnormal_state = frame[6];
- const char* abnormal_str[] = {
- "睡眠时长不足4小时", "睡眠时长大于12小时", "长时间异常无人"
- };
- if(sensorData.abnormal_state < 3) {
- Serial.printf("⚠️ 睡眠异常: %s\n", abnormal_str[sensorData.abnormal_state]);
- }
- }
- break;
-
- // DP20: 睡眠质量评级
- case 0x10:
- case 0x90:
- if(dataLen >= 1) {
- sensorData.sleep_grade = frame[6];
- const char* rating_str[] = {"无", "睡眠质量良好", "睡眠质量一般", "睡眠质量较差"};
- if(sensorData.sleep_grade < 4) {
- Serial.printf("🏆 睡眠质量评级: %s\n", rating_str[sensorData.sleep_grade]);
- }
- }
- break;
-
- // DP21: 异常挣扎状态
- case 0x11:
- case 0x91:
- if(dataLen >= 1) {
- sensorData.struggle_alert = frame[6]; // 0x00:无, 0x01:正常, 0x02:异常挣扎
- const char* struggle_str[] = {"无", "正常", "异常挣扎"};
- if(sensorData.struggle_alert < 3) {
- Serial.printf("⚠️ 挣扎状态: %s\n", struggle_str[sensorData.struggle_alert]);
- }
- }
- break;
-
- // DP22: 无人计时状态
- case 0x12:
- case 0x92:
- if(dataLen >= 1) {
- sensorData.no_one_alert = frame[6]; // 0x00:无, 0x01:正常, 0x02:异常
- const char* no_one_str[] = {"无", "正常", "异常"};
- if(sensorData.no_one_alert < 3) {
- Serial.printf("⏰ 无人计时状态: %s\n", no_one_str[sensorData.no_one_alert]);
- }
- }
- break;
-
- default:
- Serial.printf("❓未知的0x84命令字: 0x%02X\n", cmdByte);
- break;
- }
- break;
-
- // case 0x01: // 心跳包标识
- // Serial.println("💓 心跳包");
- // break;
-
- case 0x07: // 雷达探测范围信息
- if(dataLen >= 1) {
- if(frame[6] == 0x00)
- Serial.println("雷达探测范围外");
- else
- Serial.println("雷达探测范围内");
- }
- break;
-
- default:
- Serial.printf("❓未知控制字: 0x%02X\n", ctrlByte);
- break;
- }
-
-
-
-
- // 更新传感器时间戳
- lastSensorUpdate = millis();
-
- // 验证数据有效性
- sensorData.heart_valid = (sensorData.heart_rate > 0 && sensorData.heart_rate < 200);
- sensorData.breath_valid = (sensorData.breath_rate >= 0.1f && sensorData.breath_rate <= 60.0f);
-
- if( sensorData.heart_valid ==1 && sensorData.heart_valid == 1 && presence_Bit == 1 )
- {
- sensorData.presence = 1;
- presence_Bit = 0;
- }
-
- return true;
-}
-
-// 解析有符号坐标值
-int16_t parseSignedCoordinate(uint16_t raw_value) {
- // 方法1:手动解析符号位
- bool is_negative = (raw_value & 0x8000) != 0; // 检查最高位
- uint16_t magnitude = raw_value & 0x7FFF; // 取低15位数值
-
- int16_t result = (int16_t)magnitude;
- if (is_negative) {
- result = -result; // 如果是负数,加上负号
- }
-
- return result;
-}
-
-void saveWiFiConfig() {
- preferences.putString("ssid", ssid);// 保存SSID到Flash
- preferences.putString("password", password);// 保存密码到Flash
- preferences.putBool("configured", true);// 保存配置标志到Flash
- Serial.println("WiFi配置已保存到Flash");
-}
-
-void loadWiFiConfig() {
- wifiConfigured = preferences.getBool("configured", false);// 从Flash加载配置标志
- if (wifiConfigured)
- {
- preferences.getString("ssid", ssid, sizeof(ssid));// 从Flash加载SSID
- preferences.getString("password", password, sizeof(password));// 从Flash加载密码
- Serial.printf("从Flash加载WiFi配置 - SSID: %s\n", ssid);
- }
-}
-
-bool connectWiFi() {
- Serial.println("🌐 [WiFi] 开始连接到网络...");
- Serial.printf("🌐 [WiFi] 尝试连接到 SSID: %s\n", ssid);
-
- // 设置网络状态为连接中
- setNetworkStatus(NET_CONNECTING);
-
- WiFi.mode(WIFI_STA);
- WiFi.begin(ssid, password);
-
- Serial.println("[WiFi] 正在尝试连接,超时时间: 15秒...");
-
- // 记录开始时间,用于15秒超时检测
- unsigned long startTime = millis();
- const unsigned long WIFI_CONNECT_TIMEOUT = 15000; // 15秒超时
-
- while (WiFi.status() != WL_CONNECTED && (millis() - startTime) < WIFI_CONNECT_TIMEOUT) {
- delay(500);
- Serial.printf("[WiFi] 尝试连接中,当前状态: %d\n", WiFi.status());
- // 在WiFi连接过程中重置看门狗
- esp_task_wdt_reset();
- }
-
- // 检查连接结果
- if (WiFi.status() == WL_CONNECTED) {
- Serial.println("✅ [WiFi] 连接成功!");
- Serial.printf("🌐 [WiFi] 分配的IP地址: %s\n", WiFi.localIP().toString().c_str());
- Serial.printf("🔒 [WiFi] 信号强度: %d dBm\n", WiFi.RSSI());
-
- // 设置网络状态为已连接
- setNetworkStatus(NET_CONNECTED);
-
- // 将首次连接标志置0并保存到Flash,以后开机不再阻塞等待连接
- WiFi_Connect_First_bit = 0;
- preferences.putBytes("wifi_first", &WiFi_Connect_First_bit, sizeof(WiFi_Connect_First_bit));
- Serial.println("[WiFi] 已将 WiFi_Connect_First_bit 置零并保存到Flash");
-
- return true;
- } else {
- Serial.println("❌ [WiFi] 连接超时,未能成功连接到WiFi。");
- Serial.printf("⚠️ [WiFi] 最终状态码: %d\n", WiFi.status());
-
- // 检查是否是因为超时导致的连接失败,如果是,发送蓝牙通知
- if ((millis() - startTime) >= WIFI_CONNECT_TIMEOUT) {
- Serial.println("⏰ [WiFi] 连接超时,15秒内未能连接成功");
- bool connected = connectWiFi();
- if (deviceConnected) {
- String resultMsg = String("{\"type\":\"wifiConfigResult\",\"success\":") +
- String(connected ? "true" : "false") +
- String(",\"message\":\"") +
- String(connected ? "WiFi配置成功" : "WiFi配置失败") +
- String("\"}");
- // 使用新的分包发送函数发送JSON数据
- sendJSONDataToBLE(resultMsg);
- }
- }
-
- // 设置网络状态为断开
- setNetworkStatus(NET_DISCONNECTED);
-
- return false;
- }
-}
-
-// 修正后的发送雷达命令函数
-void sendRadarCommand(uint8_t ctrl, uint8_t cmd, uint8_t value) {
- uint8_t command[10];
- command[0] = 0x53; // 帧头1
- command[1] = 0x59; // 帧头2
- command[2] = ctrl; // 控制字
- command[3] = cmd; // 命令字
- command[4] = 0x00; // 长度高字节
- command[5] = 0x01; // 长度低字节
- command[6] = value; // 数据
-
- // 修正校验和计算:帧头(2) + 控制字(1) + 命令字(1) + 长度(2) + 数据(1) = 前7个字节
- uint8_t checksum = 0;
- for(int i = 0; i < 7; i++) { // 计算前7个字节的和
- checksum += command[i];
- }
- command[7] = checksum; // 校验码
-
- // 帧尾
- command[8] = 0x54; // 帧尾1
- command[9] = 0x43; // 帧尾2
-
- // 发送命令
- mySerial1.write(command, 10);
-
- // 调试输出
- Serial.printf("📤 发送: ");
- for(int i = 0; i < 10; i++) {
- Serial.printf("%02X ", command[i]);
- }
- Serial.println();
-
- Serial.printf(" 控制字=0x%02X, 命令字=0x%02X, 值=0x%02X, 校验和=0x%02X\n",
- ctrl, cmd, value, checksum);
-}
-
-// 处理设置设备ID命令
-bool processSetDeviceId(JsonDocument& doc) {
- const char* command = doc["command"];
- if (command != nullptr && strcmp(command, "setDeviceId") == 0) {
- // 处理设置设备ID的命令
- uint16_t newDeviceId = doc["newDeviceId"];
-
- //验证设备ID范围
- if (newDeviceId < MIN_DEVICE_ID || newDeviceId > MAX_DEVICE_ID){
- Serial.printf("[错误] 设备ID超出范围,有效范围: %d-%d\n", MIN_DEVICE_ID, MAX_DEVICE_ID);
- if (deviceConnected) {
- String errorMsg = String("{\"type\":\"error\",\"message\":\"设备ID超出范围,有效范围: ") +
- String(MIN_DEVICE_ID) + "-" + String(MAX_DEVICE_ID) + "\"}";
-
- // 使用新的分包发送函数发送JSON数据
- sendJSONDataToBLE(errorMsg);
- }
- return true; // 已处理该命令
- }
-
- currentDeviceId = newDeviceId;
-
- Serial.printf("[设备ID] 已设置新的设备ID: %u\n", currentDeviceId);
-
- // 保存设备ID到Preferences
- saveDeviceId();
-
- // 发送确认消息
- if (deviceConnected) {
- String confirmMsg = String("{\"type\":\"setDeviceIdResult\",\"success\":true,\"message\":\"设备ID设置成功\",\"newDeviceId\":") +
- String(newDeviceId) + "}";
-
- // 使用新的分包发送函数发送JSON数据
- sendJSONDataToBLE(confirmMsg);
-
- // 发送更新后的状态信息
- sendStatusToBLE();
- }
- return true;
- }
- return false;
-}
-
-// 加载设备ID
-void loadDeviceId() {
- currentDeviceId = preferences.getUShort("deviceId", 1001);// 从Flash加载设备ID
- Serial.printf("从Flash加载设备ID: %u\n", currentDeviceId);
-}
-
-// 保存设备ID
-void saveDeviceId() {
- preferences.putUShort("deviceId", currentDeviceId);// 保存设备ID到Flash
- Serial.printf("设备ID已保存到Flash: %u\n", currentDeviceId);
-}
-
-// 发送状态信息到BLE
-void sendStatusToBLE() {
- if (deviceConnected) {
- String statusMsg = String("{\"type\":\"status\",\"wifiConfigured\":") +
- String(wifiConfigured ? "true" : "false") +
- String(",\"wifiConnected\":") +
- String(WiFi.status() == WL_CONNECTED ? "true" : "false") +
- String(",\"ipAddress\":\"") +
- (WiFi.status() == WL_CONNECTED ? WiFi.localIP().toString() : "") + "\"" +
- String(",\"deviceId\":") +
- String(currentDeviceId) + "}";
-
- // 使用新的分包发送函数发送JSON数据
- sendJSONDataToBLE(statusMsg);
- Serial.println("已发送连接状态信息");
- }
-}
-
-// 处理WiFi配置命令
-bool processWiFiConfigCommand(JsonDocument& doc) {
- const char* command = doc["command"];
- if (command != nullptr && strcmp(command, "setWiFiConfig") == 0) {
- Serial.println("📱 [BLE-WiFi] 收到WiFi配置命令");
- // 获取WiFi配置参数
- const char* newSSID = doc["ssid"];
- const char* newPassword = doc["password"];
-
- if (newSSID != nullptr && newPassword != nullptr) {
- Serial.printf("📱 [BLE-WiFi] 接收到新的WiFi配置: SSID='%s', Password='[HIDDEN]'", newSSID);
-
- // 更新WiFi配置
- strncpy(ssid, newSSID, sizeof(ssid)-1);
- ssid[sizeof(ssid)-1] = '\0'; // 确保字符串结束符
- strncpy(password, newPassword, sizeof(password)-1);
- password[sizeof(password)-1] = '\0'; // 确保字符串结束符
-
- // 保存WiFi配置到Flash
- saveWiFiConfig();
-
- // 尝试连接WiFi
- Serial.println("🔄 [WiFi] 尝试连接到新配置的网络...");
- bool connected = connectWiFi();
-
- // 发送配置结果
- if (deviceConnected) {
- String resultMsg = String("{\"type\":\"wifiConfigResult\",\"success\":") +
- String(connected ? "true" : "false") +
- String(",\"message\":\"") +
- String(connected ? "WiFi配置成功" : "WiFi配置失败") +
- String("\"}");
- // 使用新的分包发送函数发送JSON数据
- sendJSONDataToBLE(resultMsg);
- Serial.printf("✅ [BLE] 发送WiFi配置结果: %s\n", connected ? "成功" : "失败");
- }
-
- return true; // 已处理该命令
- } else {
- // 参数不完整
- Serial.println("❌ [BLE-WiFi] WiFi配置参数不完整");
- if (deviceConnected) {
- String errorMsg = String("{\"type\":\"error\",\"message\":\"WiFi配置参数不完整,需要ssid和password字段\"}");
- // 使用新的分包发送函数发送JSON数据
- sendJSONDataToBLE(errorMsg);
- Serial.println("📱 [BLE] 发送WiFi配置错误信息");
- }
- return true; // 已处理该命令(虽然失败了)
- }
- }
- return false; // 未处理该命令
-}
-
-// 在setup()函数中添加R60ABD1初始化
-void initR60ABD1() {
- Serial.println("🔧 初始化R60ABD1雷达模组...");
-
- // 发送查询指令以激活数据上报
- Serial.println("📡 发送查询指令以激活数据上报...");
- // 查询存在信息: 53 59 80 81 00 01 00 7D 54 43
- uint8_t queryPresenceCmd[] = {0x53, 0x59, 0x80, 0x81, 0x00, 0x01, 0x00, 0x7D, 0x54, 0x43};
- mySerial1.write(queryPresenceCmd, sizeof(queryPresenceCmd));
-
- // 1. 确认开启核心功能
- Serial.println("📡 开启核心监测功能...");
- // 发送多次人体存在监测开启命令以确保生效
-
- sendRadarCommand(0x80, 0x00, 0x01); // 人体存在
- delay(50);
- sendRadarCommand(0x81, 0x00, 0x01); // 呼吸监测
- delay(50);
- sendRadarCommand(0x85, 0x00, 0x01); // 心率监测
- delay(50);
- sendRadarCommand(0x84, 0x00, 0x01); // 睡眠监测
- delay(50);
-
- // 2. 波形数据开启(需要确认命令字)
- Serial.println("📡 尝试开启波形数据...");
- sendRadarCommand(0x81, 0x0C, 0x01); // 呼吸波形
- delay(50);
- sendRadarCommand(0x85, 0x0A, 0x01); // 心率波形
- delay(50);
-
- //确认开启特殊功能
-
- sendRadarCommand(0x84, 0x13, 0x01); // 异常挣扎状态开关设置
- delay(50);
- sendRadarCommand(0x84, 0x14, 0x01); // 无人计时功能开关设置
- delay(50);
-
- // 3. 验证初始化结果
- Serial.println("🔍 查询当前状态...");
- sendRadarCommand(0x80, 0x80, 0x0F); // 查询人体存在状态
- delay(50);
- sendRadarCommand(0x81, 0x80, 0x0F); // 查询呼吸监测状态
- delay(50);
- sendRadarCommand(0x85, 0x80, 0x0F); // 查询心率监测状态
- delay(50);
- sendRadarCommand(0x84, 0x80, 0x0F); // 查询睡眠监测状态
-
- Serial.println("✅ R60ABD1雷达初始化完成");
- Serial.println("📋 串口将输出解析后的雷达数据,包括:\n - 生命体征数据(心率、呼吸率)\n - 睡眠监测数据(状态、评分、时长)\n - 人体检测数据(存在、距离、运动)\n - 活动监测数据(体动幅度、翻身次数)");
-}
-
-// 检查Boot按钮状态
-void checkBootButton() {
- Serial.println("🔍 检查Boot按钮状态...");
-
- // 配置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) {
- // 检测到Boot按钮按下,设置指示灯为准备清除状态
- currentConfigClearStatus = CONFIG_PREPARING;
- Serial.println("⚠️ 检测到Boot按钮按下,长按3秒将清除配置");
- Serial.println("⏰ 倒计时开始...");
-
- // 检测长按3秒
- bootButtonPressTime = millis();
- while (digitalRead(BOOT_BUTTON_PIN) == LOW) {
- // 打印倒计时
- unsigned long pressedTime = millis() - bootButtonPressTime;
- unsigned long remaining = (CLEAR_CONFIG_DURATION - pressedTime) / 1000;
-
- if (remaining <= 3 && remaining > 0) {
- Serial.printf("⏳ 继续按住 %lu 秒将清除配置...\n", remaining);
- }
-
- if (pressedTime >= CLEAR_CONFIG_DURATION) {
- clearConfigRequested = true;
- Serial.println("✅ 长按3秒确认,将清除配置");
- // 设置指示灯为清除过程中状态(呼吸灯)
- currentConfigClearStatus = CONFIG_CLEARING;
- break;
- }
-
- delay(1000); // 每秒检查一次
- }
-
- if (!clearConfigRequested) {
- // 按钮释放,恢复正常状态
- currentConfigClearStatus = CONFIG_NORMAL;
- Serial.println("❌ 按钮释放,取消清除操作");
- }
- } else {
- Serial.println("✅ Boot按钮未按下,正常启动");
- }
-}
-
-// 清除所有存储的配置(Flash 与内存副本)
-void clearStoredConfig() {
- Serial.println("🧹 开始清除存储的配置...");
-
- // 打开Preferences命名空间
- preferences.begin("radar_data", false);
-
- // 记录当前配置(用于显示)
- uint16_t oldDeviceId = preferences.getUShort("deviceId", 0);
- String oldSSID = preferences.getString("ssid", "");
-
- // 清除所有配置项(从Flash中移除)
- preferences.remove("deviceId");
- preferences.remove("ssid");
- preferences.remove("password");
- preferences.remove("configured");
- preferences.remove("wifi_first");// 清除首次连接标志位
-
- preferences.end();
-
- // 显示清除结果
- Serial.println("✅ 配置已清除完成");
- Serial.printf("🗑️ 被清除的设备ID: %u\n", oldDeviceId);
- Serial.printf("🗑️ 被清除的WiFi SSID: %s\n", oldSSID.c_str());
- Serial.println("🎯 系统将恢复出厂设置");
-
- // 重置全局变量(内存副本)
- currentDeviceId = 1001; // 恢复默认设备ID
- ssid[0] = '\0';
- password[0] = '\0';
- wifiConfigured = false;
- WiFi_Connect_First_bit = 1; // 标记首次连接标志位
-
- // 立即断开WiFi并重置连接相关计数/状态,确保清除立即生效
- WiFi.disconnect(true); // 立即断开并清除驱动层缓存的凭证
- wifiReconnectAttempts = 0;
- setNetworkStatus(NET_DISCONNECTED);
-
- Serial.println("🔄 已清除Flash与内存中的配置,请重新配置WiFi和设备ID");
-
- // 若有BLE连接,通知客户端当前状态
- if (deviceConnected) {
- sendStatusToBLE();
- }
-}
-
-
-// 配置清除指示灯控制任务
-void configClearLedTask(void *parameter) {
- while (1) {
- switch (currentConfigClearStatus) {
- case CONFIG_NORMAL: // 正常运行 - LOW
- analogWrite(CONFIG_CLEAR_PIN, 0); // 关闭LED
- break;
-
- case CONFIG_PREPARING: // 准备清除 - HIGH
- analogWrite(CONFIG_CLEAR_PIN, 255); // 开启LED
- 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: // 清除完成 - 快速闪烁3次
- 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) { // 闪烁3次 (HIGH-LOW为1次)
- blinkCount = 0;
- currentConfigClearStatus = CONFIG_NORMAL;
- digitalWrite(CONFIG_CLEAR_PIN, LOW); // 确保回到LOW状态
- }
- }
- break;
- }
-
- vTaskDelay(10 / portTICK_PERIOD_MS);
- }
-}
-
-// BOOT按钮监控任务
-void bootButtonMonitorTask(void *parameter) {
- Serial.println("🔍 启动BOOT按钮监控任务...");
-
- // 配置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;
- }
-
- // 检查是否长按了3秒
- if (buttonPressed && (millis() - buttonPressStartTime >= CLEAR_CONFIG_DURATION)) {
- if (!clearConfigRequested) {
- clearConfigRequested = true;
- analogWrite(NETWORK_LED_PIN, 0); // 关闭网络状态LED
- Serial.println("✅ 长按3秒确认,将清除配置");
-
- // 设置指示灯为清除过程中状态(呼吸灯)
- //currentConfigClearStatus = CONFIG_CLEARING;
-
- // 清除配置
- clearStoredConfig();
-
- Serial.println("🔄 配置清除完成,LED将闪烁3次表示完成...");
-
- // 设置指示灯为清除完成状态(快速闪烁3次)
- // currentConfigClearStatus = CONFIG_COMPLETED;
-
- // 等待闪烁完成
- // while(currentConfigClearStatus == CONFIG_COMPLETED) {
- // vTaskDelay(100 / portTICK_PERIOD_MS);
- // esp_task_wdt_reset();
- // }
- analogWrite(CONFIG_CLEAR_PIN, 0); // 关闭LED
- // analogWrite(NETWORK_LED_PIN, 0); // 关闭网络状态LED
- Serial.println("🔄 系统即将重启...");
-
- // 短暂延迟后重启
- vTaskDelay(1000 / portTICK_PERIOD_MS);
- ESP.restart();
- }
- }
-
- vTaskDelay(50 / portTICK_PERIOD_MS); // 每50ms检查一次
-
- // 重置看门狗
- esp_task_wdt_reset();
- }
-}
-
-// LED控制任务
-void ledControlTask(void *parameter) {
- while (1) {
- 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); // 关闭LED
- }
- lastBlinkTime = millis();
- }
- break;
-
- case NET_CONNECTING: // 连接中 - 快闪
- if (millis() - lastBlinkTime >= FAST_BLINK_INTERVAL) {
- ledState = !ledState;
- if(ledState) {
- ledcWrite(0, 255); // 设置为最大亮度
- } else {
- ledcWrite(0, 0); // 关闭LED
- }
- lastBlinkTime = millis();
- }
- break;
-
- case NET_CONNECTED: // 已连接 - 呼吸灯效果
- if (millis() - lastBlinkTime >= BREATHE_INTERVAL) {
- // 呼吸灯效果
- ledcWrite(0, breatheValue); // 使用ledcWrite替代analogWrite
-
- // 更新呼吸灯值
- 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);
- }
-}
-
-// 设置网络状态
-void setNetworkStatus(NetworkStatus status) {
- currentNetworkStatus = status;
-
- // 切换到呼吸灯模式时,重置呼吸灯参数
- if (status == NET_CONNECTED) {
- breatheValue = BREATHE_MIN;
- breatheIncreasing = true;
- }
-}
-
-// 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;
- }
-}
-
-// WiFi监控任务
-void wifiMonitorTask(void *parameter) {
- Serial.println("📡 WiFi监控任务启动");
-
- while(1) {
- // 定期检查WiFi连接状态
- if(millis() - lastWiFiCheckTime >= WIFI_CHECK_INTERVAL) {
- if(WiFi.status() != WL_CONNECTED) {
- // WiFi已断开,尝试重连
- Serial.println("⚠️ 检测到WiFi连接断开,尝试重连...");
- loadWiFiConfig(); // 加载WiFi配置
- // 如果重连尝试次数超过限制,稍等更长时间再试
- if(wifiReconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
- Serial.println("🔄 达到最大重连尝试次数,等待更长时间再试...");
- delay(250);
- wifiReconnectAttempts = 0; // 重置计数器
- }
-
- if(connectWiFi()) {
- Serial.println("✅ WiFi重连成功!");
- wifiReconnectAttempts = 0; // 重置重连尝试计数
- } else {
- Serial.println("❌ WiFi重连失败");
- wifiReconnectAttempts++;
-
- // 指数退避:每次失败后等待更长时间
- int delayTime = (int)(pow(2, wifiReconnectAttempts) * 1000);
- if(delayTime > 2000) delayTime = 2000; // 限制最大延迟时间为2秒
- Serial.printf("⏳ 等待 %d 秒后再次尝试重连...\n", delayTime/1000);
- delay(delayTime);
- }
- } else {
- // WiFi连接正常,重置重连尝试计数
- if(wifiReconnectAttempts > 0) {
- wifiReconnectAttempts = 0;
- }
- }
-
- lastWiFiCheckTime = millis();
- }
-
- vTaskDelay(500 / portTICK_PERIOD_MS); // 每500ms检查一次
- }
-}
\ No newline at end of file
diff --git a/src/main_backup.cpp.bak b/src/main_backup.cpp.bak
new file mode 100644
index 0000000..b493864
--- /dev/null
+++ b/src/main_backup.cpp.bak
@@ -0,0 +1,2582 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "wifi_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 presence_Bit = 1;//标志位
+uint8_t WiFi_Connect_First_bit = 1; //WiFi第一次连接标志位
+
+// 网络状态枚举
+enum NetworkStatus {
+ NET_INITIAL, // 初始化/未连接 - 慢闪
+ NET_CONNECTING, // 连接中 - 快闪
+ NET_CONNECTED, // 已连接 - 呼吸灯
+ NET_DISCONNECTED // 断开连接 - 慢闪
+};
+
+// 配置清除指示灯状态枚举
+enum ConfigClearStatus {
+ CONFIG_NORMAL, // 正常运行 - LOW
+ CONFIG_PREPARING, // 准备清除 - HIGH
+ CONFIG_CLEARING, // 清除过程中 - 呼吸灯
+ CONFIG_COMPLETED // 清除完成 - 快速闪烁3次
+};
+
+// 网络状态全局变量
+NetworkStatus currentNetworkStatus = NET_INITIAL;
+unsigned long lastBlinkTime = 0;
+bool ledState = false;
+int breatheValue = 0;
+bool breatheIncreasing = true;
+
+// 配置清除指示灯状态变量
+ConfigClearStatus currentConfigClearStatus = CONFIG_NORMAL;
+unsigned long lastConfigBlinkTime = 0;
+bool configLedState = false;
+int configBreatheValue = 0;
+bool configBreatheIncreasing = true;
+
+// LED控制相关参数
+const int SLOW_BLINK_INTERVAL = 1000; // 慢闪间隔 1秒
+const int FAST_BLINK_INTERVAL = 200; // 快闪间隔 0.2秒
+const int BREATHE_INTERVAL = 20; // 呼吸灯更新间隔
+const int BREATHE_MIN = 0; // 呼吸灯最小值
+const int BREATHE_MAX = 155; // 呼吸灯最大值
+const int BREATHE_STEP = 5; // 呼吸灯步进值
+
+// 协议ID映射表 - 对应Python脚本中的field_mapping
+const int PROTOCOL_HEART_RATE = 1; // 心率
+const int PROTOCOL_BREATH_RATE = 2; // 呼吸率
+const int PROTOCOL_PERSON_DETECTED = 13; // 人检
+const int PROTOCOL_HUMAN_ACTIVITY = 14; // 人体活动
+const int PROTOCOL_HUMAN_DISTANCE = 15; // 人体距离
+const int PROTOCOL_HUMAN_POSITION = 16; // 人体方位
+const int PROTOCOL_SLEEP_STATE = 17; // 睡眠状态
+
+// R60ABD1新增协议ID
+const int PROTOCOL_BODY_MOVEMENT = 18; // 体动幅度
+const int PROTOCOL_BREATH_STATUS = 19; // 呼吸信息
+const int PROTOCOL_SLEEP_TIME = 20; // 睡眠时长
+const int PROTOCOL_SLEEP_SCORE = 21; // 睡眠质量评分
+const int PROTOCOL_BED_ENTRY = 22; // 入床/离床状态
+const int PROTOCOL_ABNORMAL_STATE = 23; // 异常状态
+const int PROTOCOL_AVG_HEART_RATE = 24; // 平均心率
+const int PROTOCOL_AVG_BREATH_RATE = 25; // 平均呼吸率
+const int PROTOCOL_TURN_COUNT = 26; // 翻身次数
+const int PROTOCOL_LARGE_MOVE_RATIO = 27; // 大幅度体动占比
+const int PROTOCOL_SMALL_MOVE_RATIO = 28; // 小幅度体动占比
+const int PROTOCOL_POS_X = 29; // 人体X坐标
+const int PROTOCOL_POS_Y = 30; // 人体Y坐标
+const int PROTOCOL_POS_Z = 31; // 人体Z坐标
+const int PROTOCOL_DEEP_SLEEP_TIME = 32; // 深睡时长
+const int PROTOCOL_LIGHT_SLEEP_TIME = 33; // 浅睡时长
+const int PROTOCOL_AWAKE_TIME = 34; // 清醒时长
+const int PROTOCOL_SLEEP_TOTAL_TIME = 35; // 睡眠总时长
+const int PROTOCOL_DEEP_SLEEP_RATIO = 36; // 深睡占比
+const int PROTOCOL_LIGHT_SLEEP_RATIO = 37; // 浅睡占比
+const int PROTOCOL_AWAKE_RATIO = 38; // 清醒占比
+const int PROTOCOL_TURNOVER_COUNT = 39; // 翻身次数
+const int PROTOCOL_STRUGGLE_ALERT = 40; // 挣扎报警
+const int PROTOCOL_NO_ONE_ALERT = 41; // 无人计时报警
+const int PROTOCOL_BED_STATUS = 42; // 入床状态
+const int PROTOCOL_APNEA_COUNT = 43; // 呼吸暂停次数
+
+bool clearConfigRequested = false;
+
+unsigned long bootButtonPressTime = 0;
+
+const unsigned long CLEAR_CONFIG_DURATION = 3000; // 长按3秒清除配置
+
+#define BUFFER_SIZE 2000 // 固定存储2000个数据点
+// 环形缓冲区结构
+struct CircularBuffer {
+ float data[BUFFER_SIZE];
+ unsigned long timestamps[BUFFER_SIZE]; // 存储时间戳
+ int head; // 最新数据位置
+ int tail; // 最旧数据位置
+ int count; // 当前有效数据数量
+ bool isFull; // 缓冲区是否已满
+ float sum; // 当前数据总和
+};
+
+
+
+Preferences preferences;
+
+WiFiManager wifiManager;
+
+uint16_t currentDeviceId = 0000; // 默认设备ID为0000
+const uint16_t MIN_DEVICE_ID = 1000; // 设备ID最小值
+const uint16_t MAX_DEVICE_ID = 1999; // 设备ID最大值
+
+const uint32_t PHASE_SEND_INTERVAL = 1;// 每1毫秒发送一次相位数据
+const uint32_t VITAL_SEND_INTERVAL = 10;// 每10毫秒发送一次生命体征数据
+
+#define SERVICE_UUID "a8c1e5c0-3d5d-4a9d-8d5e-7c8b6a4e2f1a"
+// #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
+#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
+
+#define UART_RX_BUFFER_SIZE 4096
+
+BLEServer* pServer = NULL;
+BLECharacteristic* pCharacteristic = NULL;
+
+
+bool deviceConnected = false;
+bool oldDeviceConnected = false;
+String receivedData = "";
+String completeData = "";
+unsigned long lastReceiveTime = 0;
+
+const char* influxDBHost = "8.134.11.76";
+const int influxDBPort = 8086;
+const char* influxDBToken = "KuTa5ZsqoHIhi2IglOO06zExUYw1_mJ6K0mcA9X1y6O6CJDog3_Cgr8mUw1SwpuCCKRElqxa6wAhrrhsYPytkg==";
+const char* influxDBOrg = "gzlg";
+const char* influxDBBucket = "gzlg";
+
+const unsigned long SENSOR_TIMEOUT = 40000;
+static uint32_t packetCounter = 0;
+static bool shouldSendOtherData = false;
+
+unsigned long lastSensorUpdate = 0;
+
+// 睡眠数据发送相关
+unsigned long lastSleepDataTime = 0;
+const unsigned long SLEEP_DATA_INTERVAL = 5000; // 5秒 = 5000毫秒
+
+// 更新SensorData结构体以支持R60ABD1的全部功能点
+typedef struct {
+ float breath_rate; // 呼吸率 (DP8) 单位:次/分钟
+ float heart_rate; // 心率 (DP6) 单位:BPM
+ uint8_t breath_valid; // 呼吸率有效性
+ uint8_t heart_valid; // 心率有效性
+ uint8_t presence ; // 存在检测 (DP1) 0=无人, 1=有人
+ uint8_t motion = 0; // 运动状态 (DP2) 0=无, 1=静止, 2=活跃
+ int heartbeat_waveform; // 心跳波形 (DP7) 数值+128
+ int breathing_waveform; // 呼吸波形 (DP10) 数值+128
+
+ // R60ABD1新增字段
+ uint16_t distance; // 人体距离 (DP3) 单位:cm
+ uint8_t body_movement; // 体动幅度 (DP4) 0-100
+ uint8_t breath_status = 04; // 呼吸信息 (DP9) 01=正常, 02=呼吸过高, 03=呼吸过低, 04=无
+ uint8_t sleep_state = 3; // 睡眠状态 (DP12) 0=深睡, 1=浅睡, 2=清醒, 3=无睡眠
+ uint32_t sleep_time; // 睡眠时长 (DP13) 单位:秒
+ uint8_t sleep_score; // 睡眠质量评分 (DP14) 0-100
+ uint8_t sleep_grade; //睡眠质量评级
+ uint8_t bed_entry; // 入床/离床状态 (DP11) 0=离床, 1=入床
+ uint8_t abnormal_state; // 异常状态 (DP18) 0=正常, 1=异常
+ uint8_t avg_heart_rate; // 平均心率 (DP15)
+ uint8_t avg_breath_rate; // 平均呼吸率 (DP15)
+ uint8_t turn_count; // 翻身次数 (DP15)
+ uint8_t large_move_ratio;// 大幅度体动占比 (DP15)
+ uint8_t small_move_ratio;// 小幅度体动占比 (DP15)
+
+ // 根据技术文档新增字段
+ int16_t pos_x; // 人体X坐标 (DP5)
+ int16_t pos_y; // 人体Y坐标 (DP5)
+ int16_t pos_z; // 人体Z坐标 (DP5)
+ int8_t breath_waveform[5]; // 呼吸波形数据 (DP10)
+ int8_t heart_waveform[5]; // 心率波形数据 (DP7)
+ uint16_t deep_sleep_time; // 深睡时长 (DP15) 单位:分钟
+ uint16_t light_sleep_time; // 浅睡时长 (DP14) 单位:分钟
+ uint16_t awake_time; // 清醒时长 (DP13) 单位:分钟
+ uint16_t sleep_total_time; // 睡眠总时长 (DP18) 单位:分钟
+ uint8_t deep_sleep_ratio; // 深睡占比 (DP16)
+ uint8_t light_sleep_ratio; // 浅睡占比 (DP16)
+ uint8_t awake_ratio; // 清醒占比 (DP16)
+ uint8_t turnover_count; // 翻身次数 (DP16)
+ uint8_t struggle_alert; // 挣扎报警 (DP21)
+ uint8_t no_one_alert; // 无人计时报警 (DP22)
+ uint8_t bed_status = 02; //入床状态 (DP11)
+ uint8_t bed_Out_Time; // 离床时间 (DP11)
+ uint8_t apnea_count; // 呼吸暂停次数
+} SensorData;
+
+SensorData sensorData ;
+
+HardwareSerial mySerial1(1); // 使用UART1
+const int BAUD_RATE = 115200;
+const int UART1_RX = 3; // UART1的RX引脚,配合原理图
+const int UART1_TX = 2; // UART1的TX引脚,配合原理图
+
+// FreeRTOS任务和队列定义
+QueueHandle_t phaseDataQueue;
+QueueHandle_t vitalDataQueue;
+QueueHandle_t uartQueue = NULL; // 添加串口数据队列
+TaskHandle_t bleSendTaskHandle = NULL;
+TaskHandle_t vitalSendTaskHandle = NULL;
+TaskHandle_t uartProcessTaskHandle = NULL; // 添加串口处理任务句柄
+
+#define QUEUE_SIZE 50
+#define TASK_STACK_SIZE 8192
+#define TASK_PRIORITY 1
+
+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 = 3; // 睡眠状态
+ 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;
+
+// 睡眠数据结构 - 对应Python脚本中的sleep_data
+typedef struct {
+ uint8_t sleepQualityScore; // 睡眠质量评分 (0~100)
+ uint16_t totalSleepDuration; // 睡眠总时长 (0~65535 分钟)
+ uint8_t awakeDurationRatio; // 清醒时长占比 (0~100)
+ uint8_t lightSleepRatio; // 浅睡时长占比 (0~100)
+ uint8_t deepSleepRatio; // 深睡时长占比 (0~100)
+ uint8_t outOfBedDuration; // 离床时长 (0~255)
+ uint8_t outOfBedCount; // 离床次数 (0~255)
+ uint8_t turnCount; // 翻身次数 (0~255)
+ uint8_t avgBreathingRate; // 平均呼吸 (0~25)
+ uint8_t avgHeartRate; // 平均心跳 (0~100)
+ uint8_t apneaCount; // 呼吸暂停次数 (0~10)
+} SleepData;
+
+// 添加流量控制类
+class BLEFlowController {
+private:
+ size_t maxBytesPerSecond;
+ size_t bytesSent;
+ unsigned long lastResetTime;
+ unsigned long lastSendTime;
+
+public:
+ BLEFlowController(size_t maxBps) : maxBytesPerSecond(maxBps), bytesSent(0) {
+ lastResetTime = millis();
+ lastSendTime = 0;
+ }
+
+ bool canSend(size_t dataSize) {
+ unsigned long currentTime = millis();
+
+ // 每秒重置计数器
+ if(currentTime - lastResetTime >= 1000) {
+ bytesSent = 0;
+ lastResetTime = currentTime;
+ }
+
+ // 检查速率限制(放宽限制)
+ if((bytesSent + dataSize) > maxBytesPerSecond) {
+ return false;
+ }
+
+ // 最小发送间隔控制(重要!)
+ if(currentTime - lastSendTime < 5) { // 进一步减小最小间隔到5ms
+ return false;
+ }
+
+ return true;
+ }
+
+ bool check() {
+ // 这是一个简化版本的检查方法,只检查最小发送间隔
+ unsigned long currentTime = millis();
+ if(currentTime - lastSendTime < 5) { // 最小间隔5ms
+ return false;
+ }
+ return true;
+ }
+
+ void recordSend(size_t dataSize) {
+ bytesSent += dataSize;
+ lastSendTime = millis();
+ }
+
+ void reset() {
+ bytesSent = 0;
+ lastResetTime = millis();
+ lastSendTime = 0;
+ }
+};
+
+// 全局变量用于控制持续发送
+bool continuousSendEnabled = false;
+unsigned long continuousSendInterval = 500; // 默认1秒发送一次
+BLEFlowController bleFlow(500); // 提高到500 B/s限制,进一步提高数据传输速率
+
+void configClearLedTask(void *parameter); // 配置清除指示灯控制任务
+void bootButtonMonitorTask(void *parameter); // BOOT按钮监控任务
+void checkBootButton(); // 检查Boot按钮状态
+void clearStoredConfig(); // 清除所有存储的配置
+void ledControlTask(void *parameter); // LED控制任务
+void setNetworkStatus(NetworkStatus status); // 设置网络状态
+
+void processBLEConfig();
+bool processWiFiConfigCommand(JsonDocument& doc);
+bool processScanWiFi(JsonDocument& doc);
+bool processEchoRequest(JsonDocument& doc);
+void sendRawEchoResponse(const String& rawData);
+
+bool parseR60ABD1Frame(uint8_t *frame, uint16_t frameLen); // 解析传感器数据行
+int16_t parseSignedCoordinate(uint16_t raw_value); // 解析有符号坐标值
+void initR60ABD1(); // 初始化R60ABD1雷达
+void sendRadarCommand(uint8_t ctrl, uint8_t cmd, uint8_t value); // 发送雷达命令
+
+void wifiMonitorTask(void *parameter); // WiFi监控任务
+void WiFiEvent(WiFiEvent_t event); // WiFi事件处理
+void loadDeviceId(); // 加载设备ID
+void saveDeviceId(); // 保存设备ID
+bool processSetDeviceId(JsonDocument& doc); // 处理设置设备ID命令
+void sendStatusToBLE(); // 发送状态信息到BLE
+bool processQueryStatus(JsonDocument& doc); // 处理查询状态命令
+
+// 发送日常数据到InfluxDB - 高频实时监测数据
+void sendDailyDataToInfluxDB(String dailyDataLine);
+
+// 发送睡眠数据到InfluxDB - 低频汇总数据
+void sendSleepDataToInfluxDB();
+
+bool processQueryRadarData(JsonDocument& doc); // 处理查询雷达数据命令
+bool processStartContinuousSend(JsonDocument& doc); // 处理开始持续发送命令
+bool processStopContinuousSend(JsonDocument& doc); // 处理停止持续发送命令
+void sendRadarDataToBLE(); // 发送雷达数据到BLE
+void sendDataInChunks(const String& data); // 分包发送函数
+void sendJSONDataToBLE(const String& jsonData); // 发送JSON数据到BLE(使用纯数据分包发送,不带包头)
+bool sendCustomJSONData(const String& jsonType, const String& jsonString); // 发送自定义JSON数据到BLE
+
+// 根据协议ID获取对应的字段名
+String getFieldNameByProtocolId(int protocolId);
+// 根据协议ID获取对应的字段名
+String getFieldNameByProtocolId(int protocolId) {
+ switch(protocolId) {
+ case PROTOCOL_HEART_RATE:
+ return "heartRate";
+ case PROTOCOL_BREATH_RATE:
+ return "breathingRate";
+ case PROTOCOL_PERSON_DETECTED:
+ return "personDetected";
+ case PROTOCOL_HUMAN_ACTIVITY:
+ return "humanActivity";
+ case PROTOCOL_HUMAN_DISTANCE:
+ return "humanDistance";
+ case PROTOCOL_HUMAN_POSITION:
+ return "humanPosition";
+ case PROTOCOL_SLEEP_STATE:
+ return "sleepState";
+ default:
+ return "unknown";
+ }
+}
+
+// FreeRTOS任务函数声明
+void bleSendTask(void *parameter);
+void vitalSendTask(void *parameter);
+void radarDataTask(void *parameter);
+void uartProcessTask(void *parameter); // 添加串口处理任务声明
+
+
+// 使用正确的函数签名 - 不带参数的回调函数
+void IRAM_ATTR serialRxCallback() {
+ // 直接使用全局变量mySerial1和uartQueue
+ if (uartQueue != NULL) {
+ // 读取所有可用数据
+ while(mySerial1.available()) {
+ char c = mySerial1.read();
+
+ // 将字符放入队列(从中断安全函数)
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ xQueueSendFromISR(uartQueue, &c, &xHigherPriorityTaskWoken);
+
+ if(xHigherPriorityTaskWoken) {
+ portYIELD_FROM_ISR();
+ }
+ }
+ }
+}
+
+class MyServerCallbacks: public BLEServerCallbacks {
+ void onConnect(BLEServer* pServer) {
+ deviceConnected = true;
+ Serial.println("✅ [BLE] 客户端已连接");
+
+ // 发送连接状态信息
+ sendStatusToBLE();
+ };
+
+ void onDisconnect(BLEServer* pServer) {
+ deviceConnected = false;
+ Serial.println("🔴 [BLE] 客户端已断开");
+
+ // 重置持续发送状态
+ continuousSendEnabled = false;
+ Serial.println("🔄 重置持续发送状态");
+ }
+};
+
+class MyCallbacks: public BLECharacteristicCallbacks {
+ void onWrite(BLECharacteristic *pCharacteristic) {
+ std::string value = pCharacteristic->getValue();
+ Serial.printf("🔵 [BLE] 收到写入数据,长度: %d 字节\n", value.length());
+
+ if (value.length() > 0) {
+ String fragment = "";
+ for (int i = 0; i < value.length(); i++)
+ fragment += value[i];
+
+ Serial.printf("📄 [BLE] 接收数据片段: %s\n", fragment.c_str());
+
+ completeData += fragment;
+ lastReceiveTime = millis();
+
+ // 检查是否收到完整的JSON数据
+ int openBrace = completeData.indexOf('{');
+ int closeBrace = completeData.lastIndexOf('}');
+
+ if (openBrace >= 0 && closeBrace > openBrace) {
+ String jsonData = completeData.substring(openBrace, closeBrace + 1);
+ completeData = completeData.substring(closeBrace + 1);
+
+ Serial.printf("📥 [BLE] 完整JSON数据: %s\n", jsonData.c_str());
+
+ // 将JSON数据存储到receivedData变量中供后续处理
+ receivedData = jsonData;
+ }
+ }
+ }
+};
+
+
+
+bool processScanWiFi(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "scanWiFi") == 0) {
+ Serial.println("📱 [BLE-WiFi] 收到WiFi扫描命令");
+ wifiManager.scanAndSendResults();
+ return true;
+ }
+ return false;
+}
+
+// 处理回显命令 - 将收到的任何数据原样返回
+bool processEchoRequest(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "echo") == 0) {
+ Serial.println("📱 [BLE] 收到回显请求");
+
+ // 获取要回显的内容
+ const char* echoContent = doc["content"];
+ if (echoContent != nullptr) {
+ String echoResponse = String("{\"type\":\"echoResponse\",\"originalContent\":\"") +
+ echoContent + String("\",\"receivedSuccessfully\":true}");
+
+ if (deviceConnected) {
+ sendJSONDataToBLE(echoResponse);
+ Serial.printf("📤 [BLE] 回显响应已发送: %s\n", echoContent);
+ }
+ } else {
+ // 如果没有内容参数,至少回应已收到
+ String echoResponse = String("{\"type\":\"echoResponse\",\"receivedSuccessfully\":true,\"message\":\"Echo command received\"}");
+
+ if (deviceConnected) {
+ sendJSONDataToBLE(echoResponse);
+ Serial.println("📤 [BLE] 简单回显响应已发送");
+ }
+ }
+
+ return true;
+ }
+ return false;
+}
+
+// 通用回显函数 - 将收到的任何原始数据返回
+void sendRawEchoResponse(const String& rawData) {
+ if (deviceConnected) {
+ String echoResponse = String("{\"type\":\"rawEchoResponse\",\"originalData\":\"") +
+ rawData + String("\",\"received\":true}");
+
+ sendJSONDataToBLE(echoResponse);
+ Serial.printf("📤 [BLE] 原始数据回显已发送: %s\n", rawData.c_str());
+ }
+}
+
+// 处理查询状态命令
+bool processQueryStatus(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "queryStatus") == 0) {
+ if (deviceConnected) {
+ String statusMsg = String("{\"type\":\"deviceStatus\",\"success\":true,\"deviceId\":") +
+ String(currentDeviceId) +
+ String(",\"wifiConfigured\":") +
+ String(wifiManager.getSavedNetworkCount() > 0 ? "true" : "false") +
+ String(",\"wifiConnected\":") +
+ String(WiFi.status() == WL_CONNECTED ? "true" : "false") +
+ String(",\"ipAddress\":\"") +
+ (WiFi.status() == WL_CONNECTED ? WiFi.localIP().toString() : "") +
+ String("\"}");
+
+ sendJSONDataToBLE(statusMsg);
+ Serial.println("已发送设备状态信息");
+ }
+ return true;
+ }
+ return false;
+}
+
+// 处理查询雷达数据命令(蓝牙)
+//这个函数是一个响应式的数据发送函数,仅在接收到查询请求时才会发送当前的雷达传感器数据
+bool processQueryRadarData(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "queryRadarData") == 0) {
+ Serial.println("收到查询雷达数据命令");
+
+ // 发送最新的雷达数据
+ if (deviceConnected) {
+ String radarDataMsg = String("{\"type\":\"radarData\",\"success\":true") +
+ String(",\"deviceId\":") + String(currentDeviceId) + // 设备ID
+ String(",\"timestamp\":") + String(millis()) + // 时间戳
+ String(",\"presence\":") + String(sensorData.presence) +
+ String(",\"heartRate\":") + String(sensorData.heart_rate, 1) +
+ String(",\"breathRate\":") + String(sensorData.breath_rate, 1) +
+ String(",\"motion\":") + String(sensorData.motion) +
+ String(",\"heartbeatWaveform\":") + String((int)sensorData.breath_waveform[0]) +
+ String(",\"breathingWaveform\":") + String((int)sensorData.heart_waveform[0]) +
+ String(",\"distance\":") + String(sensorData.distance) +
+ String(",\"bodyMovement\":") + String(sensorData.body_movement) +
+ String(",\"breathStatus\":") + String(sensorData.breath_status) +
+ String(",\"sleepState\":") + String(sensorData.sleep_state) +
+ String(",\"sleepTime\":") + String(sensorData.sleep_time) +
+ String(",\"sleepScore\":") + String(sensorData.sleep_score) +
+ String(",\"avgHeartRate\":") + String(sensorData.avg_heart_rate) +
+ String(",\"avgBreathRate\":") + String(sensorData.avg_breath_rate) +
+ String(",\"turnCount\":") + String(sensorData.turn_count) +
+ String(",\"largeMoveRatio\":") + String(sensorData.large_move_ratio) +
+ String(",\"smallMoveRatio\":") + String(sensorData.small_move_ratio) +
+ String("}");
+
+ // 使用新的分包发送函数发送JSON数据
+ sendJSONDataToBLE(radarDataMsg);
+ Serial.println("已发送雷达数据");
+ Serial.printf("发送的数据: %s\n", radarDataMsg.c_str());
+ } else {
+ Serial.println("BLE未连接,无法发送雷达数据");
+ }
+ return true;
+ }
+ return false;
+}
+
+// 处理开始持续发送命令
+bool processStartContinuousSend(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "startContinuousSend") == 0) {
+ // 获取发送间隔参数(可选,默认1000ms)
+ if (doc["interval"].is()) {
+ continuousSendInterval = doc["interval"].as();
+ // 限制最小间隔为100ms,最大间隔为10000ms
+ if (continuousSendInterval < 100) continuousSendInterval = 100;
+ if (continuousSendInterval > 10000) continuousSendInterval = 10000;
+ }
+
+ continuousSendEnabled = true;
+ bleFlow.reset(); // 重置流量控制器
+
+ Serial.printf("⚙️ 启动持续发送模式,间隔: %lu ms\n", continuousSendInterval);
+
+ // 发送JSON格式的确认消息
+ if (deviceConnected) {
+ String confirmMsg = String("{\"type\":\"startContinuousSendResult\",\"success\":true,\"message\":\"已启动持续发送模式\",\"interval\":") +
+ String(continuousSendInterval) + "}";
+
+ // 使用新的分包发送函数发送JSON数据
+ sendJSONDataToBLE(confirmMsg);
+ Serial.println("✅ 启动确认消息发送成功");
+
+ delay(5); // 减少延迟以提高实时性
+ Serial.println("🚀 已启动持续发送模式");
+ } else {
+ Serial.println("❌ BLE未连接,无法发送确认消息");
+ }
+ return true;
+ }
+ return false;
+}
+
+// 处理停止持续发送命令
+bool processStopContinuousSend(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "stopContinuousSend") == 0) {
+ continuousSendEnabled = false;
+
+ Serial.println("🛑 停止持续发送模式");
+
+ // 发送JSON格式的确认消息
+ if (deviceConnected) {
+ String confirmMsg = String("{\"type\":\"stopContinuousSendResult\",\"success\":true,\"message\":\"已停止持续发送模式\"}");
+
+ // 使用新的分包发送函数发送JSON数据
+ sendJSONDataToBLE(confirmMsg);
+ Serial.println("✅ 停止确认消息发送成功");
+
+ delay(5); // 减少延迟以提高实时性
+ Serial.println("⏹️ 已停止持续发送模式");
+ } else {
+ Serial.println("❌ BLE未连接,无法发送确认消息");
+ }
+ return true;
+ }
+ return false;
+}
+
+// 分包发送函数 - 优化版本,解决分包混乱和数据截断问题
+void sendDataInChunks(const String& data) {
+ const int MAX_PACKET_SIZE = 20; // BLE最大包大小
+ const int HEADER_SIZE = 6; // 包头大小 "[N/M]"
+ const int CHUNK_SIZE = MAX_PACKET_SIZE - HEADER_SIZE; // 实际数据大小
+
+ int totalLength = data.length();
+ int numChunks = (totalLength + CHUNK_SIZE - 1) / CHUNK_SIZE;
+
+ // 只有在数据较长需要分包时才显示分包信息
+ Serial.printf("📦 开始分包发送,总长度: %d, 分包数: %d\n", totalLength, numChunks);
+
+ for(int i = 0; i < numChunks; i++) {
+ int start = i * CHUNK_SIZE;
+ int chunkLength = min(CHUNK_SIZE, totalLength - start);
+ String chunk = data.substring(start, start + chunkLength);
+
+ // 构造简单包头: "[序号/总数]",确保总长度不超过6字符
+ // 例如: "[1/5]" = 4字符
+ String packetHeader = String("[") + String(i+1) + String("/") + String(numChunks) + String("]");
+
+ // 确保总包大小不超过20字节
+ int maxDataLength = MAX_PACKET_SIZE - packetHeader.length();
+ if (chunk.length() > maxDataLength) {
+ chunk = chunk.substring(0, maxDataLength);
+ }
+
+ String packet = packetHeader + chunk;
+
+ Serial.printf("📤 发送分包 %s: %s\n", packetHeader.c_str(), chunk.c_str());
+
+ // 检查BLE连接状态
+ if (!deviceConnected) {
+ Serial.println("❌ BLE未连接,无法发送数据");
+ return;
+ }
+
+ // 发送数据包
+ pCharacteristic->setValue(packet.c_str());
+ pCharacteristic->notify();
+ Serial.println("✅ 分包发送成功");
+
+ // 等待一段时间确保接收端处理完成,避免数据丢失
+ if (i < numChunks - 1) {
+ Serial.printf("⏳ 等待接收端处理第%d个包...\n", i+1);
+ vTaskDelay(20 / portTICK_PERIOD_MS); // 非阻塞延时,允许调度
+ }
+ }
+
+ Serial.println("📦 分包发送完成");
+}
+
+// 新增:发送JSON数据到BLE(使用纯数据分包发送,不带包头)
+void sendJSONDataToBLE(const String& jsonData) {
+ Serial.printf("📤 准备发送JSON数据: %s\n", jsonData.c_str());
+
+ // 检查BLE连接状态
+ if (!deviceConnected) {
+ Serial.println("❌ BLE未连接,无法发送JSON数据");
+ return;
+ }
+
+ // 直接发送JSON数据,使用纯数据分包(不带包头)
+ const int MAX_PACKET_SIZE = 20; // BLE最大包大小
+ int totalLength = jsonData.length();
+ int numChunks = (totalLength + MAX_PACKET_SIZE - 1) / MAX_PACKET_SIZE;
+
+ Serial.printf("📦 开始分包发送JSON,总长度: %d, 分包数: %d\n", totalLength, numChunks);
+
+ for(int i = 0; i < numChunks; i++) {
+ int start = i * MAX_PACKET_SIZE;
+ int chunkLength = min(MAX_PACKET_SIZE, totalLength - start);
+ String chunk = jsonData.substring(start, start + chunkLength);
+
+ Serial.printf("📤 发送JSON分包 %d/%d: %s\n", i+1, numChunks, chunk.c_str());
+
+ // 发送数据包
+ pCharacteristic->setValue(chunk.c_str());
+ pCharacteristic->notify();
+ Serial.println("✅ JSON分包发送成功");
+
+ // 等待一段时间确保接收端处理完成,避免数据丢失
+ if (i < numChunks - 1) {
+ Serial.printf("⏳ 等待接收端处理第%d个包...\n", i+1);
+ vTaskDelay(10 / portTICK_PERIOD_MS); // 非阻塞延时,允许调度
+ }
+ }
+
+ Serial.println("📦 JSON分包发送完成");
+}
+
+// 新增:发送自定义JSON数据到BLE
+bool sendCustomJSONData(const String& jsonType, const String& jsonString) {
+ if (!deviceConnected) {
+ Serial.println("❌ BLE未连接,无法发送自定义JSON数据");
+ return false;
+ }
+
+ // 构造完整的JSON数据
+ String fullJSON = String("{\"type\":\"") + jsonType + String("\",") + jsonString + String("}");
+
+ Serial.printf("📤 发送自定义JSON数据类型 '%s': %s\n", jsonType.c_str(), fullJSON.c_str());
+
+ // 使用纯数据分包发送函数发送JSON数据
+ sendJSONDataToBLE(fullJSON);
+
+ return true;
+}
+
+// 发送雷达数据到BLE(已移至FreeRTOS任务处理)
+void sendRadarDataToBLE() {
+ // 此函数已废弃,雷达数据发送现在由FreeRTOS任务处理
+ // 保留此函数以防其他代码引用
+ Serial.println("ℹ️ 雷达数据发送已移至FreeRTOS任务处理");
+}
+
+// 处理BLE配置和命令
+void processBLEConfig() {
+ // 增强超时处理机制
+ if (completeData.length() > 0 && (millis() - lastReceiveTime > 3000)) {
+ Serial.println("⏰ [超时] 数据接收超时3秒,清空缓冲区");
+ completeData = "";
+ }
+
+ if (receivedData.length() > 0) {
+ String bleData = receivedData;
+ receivedData = "";
+ bleData.trim();
+
+ Serial.printf("⚙️ [BLE] 准备解析JSON: %s\n", bleData.c_str());
+
+ if (bleData.startsWith("{") && bleData.endsWith("}")) {
+ JsonDocument doc;
+ DeserializationError error = deserializeJson(doc, bleData);
+
+ if (error) {
+ String errorMsg = String("❌ [BLE] JSON解析失败: ") + String(error.c_str());
+ Serial.println(errorMsg);
+ if (deviceConnected) {
+ // 即使JSON解析失败,也发送原始数据的回显
+ String responseMsg = String("{\"type\":\"error\",\"message\":\"配置格式错误,请使用JSON格式\",\"originalData\":\"") + bleData + String("\"}");
+
+ // 使用新的分包发送函数发送JSON数据
+ sendJSONDataToBLE(responseMsg);
+
+ // 同时发送原始数据回显
+ sendRawEchoResponse(bleData);
+ }
+ } else {
+ Serial.println("✅ [BLE] JSON解析成功");
+
+ // 处理各种命令
+ bool processed = false;
+
+ // 处理设置设备ID命令
+ if (!processed) processed = processSetDeviceId(doc);
+
+ // 处理WiFi配置命令
+ if (!processed) processed = processWiFiConfigCommand(doc);
+
+ // 处理查询状态命令
+ if (!processed) processed = processQueryStatus(doc);
+
+ // 处理查询雷达数据命令
+ if (!processed) processed = processQueryRadarData(doc);
+
+ // 处理开始持续发送命令
+ if (!processed) processed = processStartContinuousSend(doc);
+
+ // 处理停止持续发送命令
+ if (!processed) processed = processStopContinuousSend(doc);
+
+ // 处理WiFi扫描命令
+ if (!processed) processed = processScanWiFi(doc);
+
+ // 处理回显请求
+ if (!processed) processed = processEchoRequest(doc);
+
+ // 如果没有处理任何命令,发送错误响应
+ if (!processed) {
+ Serial.println("❓[BLE] 未知命令");
+ if (deviceConnected) {
+ String responseMsg = String("{\"type\":\"error\",\"message\":\"未知命令\",\"receivedData\":\"") + bleData + String("\"}");
+
+ // 使用新的分包发送函数发送JSON数据
+ sendJSONDataToBLE(responseMsg);
+
+ // 同时发送原始数据回显
+ sendRawEchoResponse(bleData);
+ }
+ } else {
+ // 如果命令被处理了,也发送回显确认
+ sendRawEchoResponse(bleData);
+ }
+ }
+ } else {
+ Serial.println("📥 [BLE] 接收到非JSON数据");
+ }
+ }
+}
+
+// FreeRTOS任务:蓝牙数据发送(精简格式以节省空间)
+//主动发送:这是一个持续运行的FreeRTOS任务,不断地从队列中获取数据并主动发送到蓝牙,用于实时数据传输
+void bleSendTask(void *parameter)
+{
+ Serial.println("🔁 R60ABD1蓝牙数据发送任务启动");
+
+ while (1) {
+ PhaseData phaseData;
+
+ // 从队列接收相位数据用于蓝牙发送
+ if (xQueueReceive(phaseDataQueue, &phaseData, portMAX_DELAY) == pdTRUE) {
+ esp_task_wdt_reset();
+
+ // 蓝牙传输 - 构造并发送完整的R60ABD1雷达数据
+ if (deviceConnected && continuousSendEnabled) {
+ String radarDataCore;
+
+ // 检查是否检测到人
+ if (sensorData.presence > 0)
+ {
+ // 检测到人时,发送实际数据,包含R60ABD1扩展功能
+ radarDataCore = String(sensorData.heart_rate, 1) + String("|") +
+ String(sensorData.breath_rate, 1) + String("|") +
+ String((int)sensorData.heart_waveform[0]) + String("|") +
+ String((int)sensorData.breath_waveform[0]) + String("|") +
+ String(sensorData.presence) + String("|") +
+ String(sensorData.motion) + String("|") +
+ // String(sensorData.distance) + String("|") + // R60ABD1距离
+ String(sensorData.sleep_state); // R60ABD1睡眠状态
+ // String(sensorData.sleep_time) + String("|") + // R60ABD1睡眠时长
+ // String(sensorData.sleep_score); // R60ABD1睡眠评分
+ }
+ else {
+ // 未检测到人时,发送零数据,但仍然包含实际的RSSI值
+ radarDataCore = String("0.0") + String("|") +
+ String("0.0") + String("|") +
+ String("0") + String("|") +
+ String("0") + String("|") +
+ String("0") + String("|") +
+ String("0") + String("|") +
+ // String("0") + String("|") + // 距离
+ String("0"); // 睡眠状态
+ // String("0") + String("|") + // 睡眠时长
+ // String("0"); // 睡眠评分
+ }
+
+ // 计算CRC校验和
+ unsigned int crc = 0xFFFF;
+ for (int i = 0; i < radarDataCore.length(); i++) {
+ crc ^= (unsigned int)radarDataCore.charAt(i);
+ for (int j = 0; j < 8; j++) {
+ if (crc & 0x0001) {
+ crc >>= 1;
+ crc ^= 0xA001;
+ } else {
+ crc >>= 1;
+ }
+ }
+ }
+
+ // 添加校验和到数据末尾
+ String radarDataMsg = radarDataCore + String("|") + String(crc, HEX);
+
+ Serial.printf("📤 通过蓝牙发送R60ABD1雷达数据: %s\n", radarDataMsg.c_str());
+
+ // 检查数据长度决定发送方式
+ const int MAX_BLE_PACKET_SIZE = 20; // BLE最大包大小
+ if (radarDataMsg.length() <= MAX_BLE_PACKET_SIZE) {
+ // 数据较短,直接发送
+ pCharacteristic->setValue(radarDataMsg.c_str());
+ pCharacteristic->notify();
+ Serial.println("✅ R60ABD1雷达数据蓝牙发送成功");
+ } else {
+ // 数据较长,使用分包发送
+ Serial.println("🔄 R60ABD1雷达数据较长,使用分包发送");
+ sendDataInChunks(radarDataMsg);
+ }
+ }
+
+ esp_task_wdt_reset();
+ }
+
+ vTaskDelay(1 / portTICK_PERIOD_MS); // 减少延迟以提高实时性
+ // 在BLE任务循环中重置看门狗
+ esp_task_wdt_reset();
+ }
+}
+// FreeRTOS任务:发送生命体征数据到数据库
+void vitalSendTask(void *parameter) {
+ Serial.println("🔁🔁 生命体征数据发送任务启动(WiFi数据库传输)");
+
+ // 记录上次发送睡眠数据的时间
+ unsigned long lastSleepDataTime = 0;
+ const unsigned long SLEEP_DATA_INTERVAL = 5000; // 5秒 = 5000毫秒
+
+ while (1) {
+ VitalData vitalData;
+
+ // 从队列接收生命体征数据用于WiFi数据库发送
+ if (xQueueReceive(vitalDataQueue, &vitalData, portMAX_DELAY) == pdTRUE) {
+ esp_task_wdt_reset();
+
+ // WiFi数据库传输 - 按照Python脚本的双模块架构发送数据
+ if (WiFi.status() == WL_CONNECTED) {
+ // 发送日常数据(高频实时监测数据)- daily_data
+ // 构造日常数据行协议
+ String dailyDataLine = "daily_data,deviceId=" + String(currentDeviceId) + ",dataType=daily ";
+
+ // 添加各个实时监测字段
+ bool firstField = true;
+
+ if (vitalData.heart_rate > 0) {
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "heartRate=" + String(vitalData.heart_rate, 1);// 心率
+ firstField = false;
+ }
+
+ if (vitalData.breath_rate > 0) {
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "breathingRate=" + String(vitalData.breath_rate, 1);// 呼吸率
+ firstField = false;
+ }
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "personDetected=" + String(vitalData.presence) + "i";// 人检
+ firstField = false;
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "humanActivity=" + String(vitalData.motion) + "i";// 活动
+ firstField = false;
+
+ if (vitalData.distance > 0) {
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "humanDistance=" + String(vitalData.distance) + "i";// 距离
+ firstField = false;
+ }
+
+ if (vitalData.sleep_state >= 0) {
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "sleepState=" + String(vitalData.sleep_state) + "i";// 睡眠状态
+ firstField = false;
+ }
+
+ // 添加人体坐标数据 (DP5) - X坐标
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "humanPositionX=" + String(vitalData.pos_x) + "i";// X坐标
+ firstField = false;
+
+ // 添加人体坐标数据 (DP5) - Y坐标
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "humanPositionY=" + String(vitalData.pos_y) + "i";// Y坐标
+ firstField = false;
+
+ // 添加人体坐标数据 (DP5) - Z坐标
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "humanPositionZ=" + String(vitalData.pos_z) + "i";// Z坐标
+ firstField = false;
+
+ // 添加波形数据
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "heartbeatWaveform=" + String((int)sensorData.heart_waveform[0]) + "i";// 心跳波形
+ firstField = false;
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "breathingWaveform=" + String((int)sensorData.breath_waveform[0]) + "i";// 呼吸波形
+ firstField = false;
+
+ // 添加异常状态
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "abnormalState=" + String(vitalData.abnormal_state) + "i";// 异常状态
+ firstField = false;
+
+ // 添加入床状态
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "bedStatus=" + String(vitalData.bed_status) + "i";// 入床状态
+ firstField = false;
+
+ // 挣扎警报和无人警报
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "struggleAlert=" + String(vitalData.struggle_alert) + "i";// 挣扎警报
+ firstField = false;
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "noOneAlert=" + String(vitalData.no_one_alert) + "i";// 无人警报
+ firstField = false;
+
+ // 发送日常数据到数据库
+ if (!dailyDataLine.endsWith(" ")) { // 确保有数据要发送
+ sendDailyDataToInfluxDB(dailyDataLine);
+ esp_task_wdt_reset();
+ delay(10);
+ }
+
+ // 检查是否需要发送睡眠数据(低频汇总数据)- sleep_data
+ unsigned long currentTime = millis();
+ if (currentTime - lastSleepDataTime >= SLEEP_DATA_INTERVAL) {
+ sendSleepDataToInfluxDB();
+ lastSleepDataTime = currentTime;
+ Serial.println("⏰ 睡眠数据定时发送完成");
+ }
+ }
+ } else {
+ Serial.println("❌❌ WiFi未连接,无法发送雷达数据到数据库");
+
+ // 定期打印WiFi状态以便调试
+ static unsigned long lastWifiCheck = 0;
+ if (millis() - lastWifiCheck > 10000) {
+ Serial.printf("📶📶 WiFi状态: %d, 已配置: %s\n",
+ WiFi.status(), wifiManager.getSavedNetworkCount() > 0 ? "是" : "否");
+ lastWifiCheck = millis();
+ }
+ }
+
+ esp_task_wdt_reset();
+ }
+
+ vTaskDelay(10 / portTICK_PERIOD_MS); // 适当延迟
+ }
+
+
+// 发送日常数据到InfluxDB - 高频实时监测数据
+void sendDailyDataToInfluxDB(String dailyDataLine) {
+ if (WiFi.status() != WL_CONNECTED) {
+ Serial.println("❌ WiFi未连接,无法发送日常数据到数据库");
+ return;
+ }
+
+ HTTPClient http;
+ http.setTimeout(2000);
+
+ String url = String("http://") + String(influxDBHost) + ":" + String(influxDBPort) + "/api/v2/write?org=" + String(influxDBOrg) + "&bucket=" + String(influxDBBucket);
+
+ http.begin(url);
+ http.addHeader("Authorization", String("Token ") + String(influxDBToken));
+ http.addHeader("Content-Type", "text/plain; charset=utf-8");
+ http.setReuse(true);
+
+ Serial.println(String("📊 发送日常数据到InfluxDB: ") + dailyDataLine);
+
+ int httpResponseCode = http.POST(dailyDataLine);// 发送日常数据到InfluxDB
+
+ if (httpResponseCode == 204) {
+ Serial.println("✅ 日常数据发送成功");
+ } else {
+ Serial.println(String("❌ 发送日常数据失败: ") + String(httpResponseCode) + " - " + http.getString());
+ }
+
+ http.end();
+}
+
+// 发送睡眠数据到InfluxDB - 模仿Python脚本的低频发送模式
+//这个函数是专门用于发送睡眠数据到InfluxDB数据库的
+void sendSleepDataToInfluxDB() {
+ if (WiFi.status() != WL_CONNECTED) {
+ Serial.println("❌ WiFi未连接,无法发送睡眠数据到数据库");
+ return;
+ }
+
+ // 检查总睡眠时长是否为0,如果为0则不上传数据
+ if (sensorData.sleep_total_time == 0) {
+ Serial.println("😴 总睡眠时长为0,跳过上传睡眠数据");
+ return;
+ }
+
+ HTTPClient http;
+ http.setTimeout(2000);
+
+ String url = String("http://") + String(influxDBHost) + ":" + String(influxDBPort) + "/api/v2/write?org=" + String(influxDBOrg) + "&bucket=" + String(influxDBBucket);
+
+ http.begin(url);
+ http.addHeader("Authorization", String("Token ") + String(influxDBToken));
+ http.addHeader("Content-Type", "text/plain; charset=utf-8");
+ http.setReuse(true);
+
+ // 构造睡眠数据的行协议格式
+ String lineProtocol = String("sleep_data,deviceId=") + String(currentDeviceId) + ",dataType=sleep ";
+
+ // 按顺序添加各个字段 - 构建睡眠分析数据
+ String fields = "";
+ fields += String("sleepQualityScore=") + String((int)sensorData.sleep_score) + "i";//睡眠评分
+ fields += ",sleepQualityGrade=" + String((int)sensorData.sleep_grade) + "i";//睡眠质量评级
+ fields += ",totalSleepDuration=" + String((int)sensorData.sleep_total_time) + "i";// 总睡眠时长
+ fields += ",awakeDurationRatio=" + String((int)sensorData.awake_ratio) + "i";// 清醒时长占比
+ fields += ",lightSleepRatio=" + String((int)sensorData.light_sleep_ratio) + "i";// 浅睡时长占比
+ fields += ",deepSleepRatio=" + String((int)sensorData.deep_sleep_ratio) + "i";// 深睡时长占比
+ fields += ",outOfBedDuration=" + String((int)sensorData.bed_Out_Time) + "i";// 离床时长
+ fields += ",outOfBedCount=" + String((int)sensorData.turn_count) + "i";// 离床次数
+ fields += ",turnCount=" + String((int)sensorData.turnover_count) + "i";// 转身次数
+ fields += ",avgBreathingRate=" + String((int)sensorData.avg_breath_rate) + "i";// 平均呼吸率
+ fields += ",avgHeartRate=" + String((int)sensorData.avg_heart_rate) + "i";// 平均心率
+ fields += ",apneaCount=" + String((int)sensorData.apnea_count) + "i";// 呼吸暂停次数
+ fields += ",abnormalState=" + String((int)sensorData.abnormal_state) + "i";// 睡眠异常状态
+ fields += ",bodyMovement=" + String((int)sensorData.body_movement) + "i";// 体动幅度
+ fields += ",breathStatus=" + String((int)sensorData.breath_status) + "i";// 呼吸状态
+ fields += ",sleepState=" + String((int)sensorData.sleep_state) + "i";// 睡眠状态
+ fields += ",largeMoveRatio=" + String((int)sensorData.large_move_ratio) + "i";// 大幅度体动占比
+ fields += ",smallMoveRatio=" + String((int)sensorData.small_move_ratio) + "i";// 小幅度体动占比
+ fields += ",struggleAlert=" + String((int)sensorData.struggle_alert) + "i";// 挣扎警报
+ fields += ",noOneAlert=" + String((int)sensorData.no_one_alert) + "i";// 无人警报
+ fields += ",awakeDuration=" + String((int)sensorData.awake_time) + "i";// 清醒时长
+ fields += ",lightSleepDuration=" + String((int)sensorData.light_sleep_time) + "i";// 浅睡时长
+ fields += ",deepSleepDuration=" + String((int)sensorData.deep_sleep_time) + "i";// 深睡时长
+ // // 添加波形数组数据(前3个点)
+ // fields += ",breathWaveform1=" + String((int)sensorData.breath_waveform[0]) + "i";// 呼吸波形点1
+ // fields += ",breathWaveform2=" + String((int)sensorData.breath_waveform[1]) + "i";// 呼吸波形点2
+ // fields += ",breathWaveform3=" + String((int)sensorData.breath_waveform[2]) + "i";// 呼吸波形点3
+ // fields += ",heartWaveform1=" + String((int)sensorData.heart_waveform[0]) + "i";// 心率波形点1
+ // fields += ",heartWaveform2=" + String((int)sensorData.heart_waveform[1]) + "i";// 心率波形点2
+ // fields += ",heartWaveform3=" + String((int)sensorData.heart_waveform[2]) + "i";// 心率波形点3
+
+ lineProtocol += fields;
+
+ Serial.println(String("🌙 发送睡眠数据到InfluxDB: ") + lineProtocol);
+
+ int httpResponseCode = http.POST(lineProtocol);// 发送睡眠数据到InfluxDB
+
+ if (httpResponseCode == 204) {
+ Serial.println(String("✅ 睡眠数据已保存到InfluxDB设备") + String(currentDeviceId) + "上");
+ } else {
+ Serial.println(String("❌ 保存睡眠数据到InfluxDB失败: ") + String(httpResponseCode) + " - " + http.getString());
+ }
+
+ http.end();
+}
+
+// 雷达数据处理任务 - 高优先级
+void radarDataTask(void *parameter) {
+ Serial.println("🔁 雷达数据处理任务启动(最高优先级)");
+
+ while (1) {
+ // 雷达数据处理已移至专用任务处理,这里不需要做任何事情
+ // 串口数据现在由uartProcessTask处理
+
+ // 短暂延迟以允许其他任务运行
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ }
+}
+
+
+// 修改串口处理任务以支持二进制协议
+//存储数据:将解析后的数据分别存储到两个结构体中:vitalData,phaseData
+void uartProcessTask(void *parameter) {
+ uint8_t buffer[256]; // 接收缓冲区
+ int bufferIndex = 0;
+ bool inFrame = false;
+ uint8_t prevByte = 0;
+
+ Serial.println("✅ R60ABD1串口数据处理任务启动");
+
+ while(1) {
+ uint8_t c;
+ // 从队列接收字节(最多等待10ms)
+ if(xQueueReceive(uartQueue, &c, 10 / portTICK_PERIOD_MS) == pdTRUE) {
+ // 重置看门狗,防止超时
+ esp_task_wdt_reset();
+ if(!inFrame) {
+ // 寻找帧头
+ if(prevByte == FRAME_HEADER1 && c == FRAME_HEADER2) {
+ // 找到帧头,开始接收帧
+ buffer[0] = FRAME_HEADER1;
+ buffer[1] = FRAME_HEADER2;
+ bufferIndex = 2;
+ inFrame = true;
+ Serial.println("🔍 检测到R60ABD1帧头");
+ }
+ } else {
+ // 在帧中接收数据
+ if(bufferIndex < sizeof(buffer)) {
+ buffer[bufferIndex++] = c;
+
+ // 检查是否到达帧尾
+ if(bufferIndex >= 8 && // 至少包含基本帧头信息
+ buffer[bufferIndex-2] == FRAME_TAIL1 &&
+ buffer[bufferIndex-1] == FRAME_TAIL2) {
+ // 完整帧接收完成,进行解析
+ if(parseR60ABD1Frame(buffer, bufferIndex)) {
+ // 数据解析成功,更新统计
+ static uint32_t frameCounter = 0;
+ frameCounter++;
+
+ // 更新全局传感器数据
+ lastSensorUpdate = millis();
+
+ // 使用FreeRTOS队列发送数据到任务
+ static uint32_t phasePacketCounter = 0;
+ static uint32_t vitalPacketCounter = 0;
+
+ phasePacketCounter++;
+ if (phasePacketCounter >= PHASE_SEND_INTERVAL) {
+ PhaseData phaseData;
+ phaseData.heartbeat_waveform = sensorData.heartbeat_waveform;// 心率波形数据
+ phaseData.breathing_waveform = sensorData.breathing_waveform;// 呼吸波形数据
+
+ if (xQueueSend(phaseDataQueue, &phaseData, 0) == pdTRUE) {
+ // 成功发送到队列
+ } else {
+ Serial.println("❌ 相位数据队列已满,数据丢失");
+ }
+ phasePacketCounter = 0;
+ }
+
+ vitalPacketCounter++;
+ if (vitalPacketCounter >= VITAL_SEND_INTERVAL) {
+ VitalData vitalData;
+ vitalData.heart_rate = sensorData.heart_rate;// 心率数据
+ vitalData.breath_rate = sensorData.breath_rate;// 呼吸率数据
+ vitalData.presence = sensorData.presence;// 人员检测数据
+ vitalData.motion = sensorData.motion;// 运动数据
+ vitalData.distance = sensorData.distance;// 距离数据
+ vitalData.sleep_state = sensorData.sleep_state;// 睡眠状态数据
+ vitalData.sleep_score = sensorData.sleep_score;// 睡眠评分数据(无)
+ vitalData.body_movement = sensorData.body_movement;// 体动数据
+ vitalData.breath_status = sensorData.breath_status;// 呼吸状态数据(去)
+ vitalData.sleep_time = sensorData.sleep_time;// 睡眠时间数据
+ vitalData.bed_status = sensorData.bed_status;//入床状态数据
+ vitalData.abnormal_state = sensorData.abnormal_state;// 异常状态数据
+ vitalData.avg_heart_rate = sensorData.avg_heart_rate;// 平均心率数据
+ vitalData.avg_breath_rate = sensorData.avg_breath_rate;// 平均呼吸率数据
+ vitalData.turn_count = sensorData.turn_count;// 离床次数数据
+ vitalData.large_move_ratio = sensorData.large_move_ratio;// 大动作比例数据
+ vitalData.small_move_ratio = sensorData.small_move_ratio;// 小动作比例数据
+ vitalData.pos_x = sensorData.pos_x;// X坐标数据
+ vitalData.pos_y = sensorData.pos_y;// Y坐标数据
+ vitalData.pos_z = sensorData.pos_z;// Z坐标数据
+ vitalData.deep_sleep_time = sensorData.deep_sleep_time;// 深睡时间数据
+ vitalData.light_sleep_time = sensorData.light_sleep_time;// 浅睡时间数据
+ vitalData.awake_time = sensorData.awake_time;// 唤醒时间数据
+ vitalData.sleep_total_time = sensorData.sleep_total_time;// 睡眠总时间数据(去)
+ vitalData.deep_sleep_ratio = sensorData.deep_sleep_ratio;// 深睡比例数据(去)
+ vitalData.light_sleep_ratio = sensorData.light_sleep_ratio;// 浅睡比例数据(去)
+ vitalData.awake_ratio = sensorData.awake_ratio;// 清醒比例数据(去)
+ vitalData.turnover_count = sensorData.turnover_count;// 离床次数数据(去)
+ vitalData.struggle_alert = sensorData.struggle_alert;// 挤压警报数据
+ vitalData.no_one_alert = sensorData.no_one_alert;// 无人警报数据
+ vitalData.apnea_count = sensorData.apnea_count;// 呼吸暂停次数数据(去)
+ vitalData.heartbeat_waveform = sensorData.heartbeat_waveform;// 心率波形数据
+ vitalData.breathing_waveform = sensorData.breathing_waveform;// 呼吸波形数据
+
+ if (xQueueSend(vitalDataQueue, &vitalData, 0) == pdTRUE) {
+ Serial.println("📤 生命体征数据已加入发送队列");
+ } else {
+ Serial.println("❌ 生命体征数据队列已满,数据丢失");
+ }
+ vitalPacketCounter = 0;
+ }
+
+ if(frameCounter % 100 == 0) {
+ Serial.printf("📈 已处理 %d 个R60ABD1数据帧\n", frameCounter);
+ }
+
+ }
+
+ // 重置状态,准备接收下一帧
+ inFrame = false;
+ }
+ } else {
+ // 缓冲区溢出,重置状态
+ Serial.println("⚠️ R60ABD1帧缓冲区溢出,重置接收状态");
+ inFrame = false;
+ }
+ }
+
+ prevByte = c;
+ }
+
+ // 允许其他任务运行
+ vTaskDelay(1 / portTICK_PERIOD_MS);
+ // 在循环中定期重置看门狗
+ esp_task_wdt_reset();
+ }
+}
+
+// 替换现有的parseSensorLine函数
+bool parseR60ABD1Frame(uint8_t *frame, uint16_t frameLen) {
+ if(frameLen < 8) return false; // 最小帧长度检查
+
+ // 验证帧头和帧尾
+ if(frame[0] != FRAME_HEADER1 || frame[1] != FRAME_HEADER2 ||
+ frame[frameLen-2] != FRAME_TAIL1 || frame[frameLen-1] != FRAME_TAIL2) {
+ return false;
+ }
+
+ // 验证校验和
+ uint8_t checksum = 0;
+ for(int i = 0; i < frameLen-3; i++) { // 不包括校验和字节和帧尾
+ checksum += frame[i];
+ // 在长循环中重置看门狗,防止超时
+ if(i % 50 == 0) {
+ esp_task_wdt_reset();
+ }
+ }
+ if(checksum != frame[frameLen-3]) { // 校验和在倒数第三个字节
+ Serial.println("❌ R60ABD1帧校验和错误");
+ return false;
+ }
+
+ for(int i = 0; i < frameLen && i < 20; i++) { // 显示前20个字节
+ Serial.printf("%02X ", frame[i]);
+ }
+ if(frameLen > 20) Serial.print("... ");
+ Serial.printf("| 校验:0x%02X\n", frame[frameLen-3]);
+
+ // 提取帧内容
+ uint8_t ctrlByte = frame[2]; // 控制字
+ uint8_t cmdByte = frame[3]; // 命令字
+ uint16_t dataLen = (frame[4] << 8) | frame[5]; // 数据长度
+
+ // 根据控制字和命令字处理不同类型的数据
+ switch(ctrlByte)
+ {
+ case CTRL_PRESENCE:
+ // 根据技术文档,0x80控制字的不同命令字对应不同的数据点
+ switch(cmdByte)
+ {
+ // 开关人体存在监测
+ case 0x00:
+ case 0x80:
+ if(dataLen >= 1) {
+ if(frame[6] == 0x01) {
+ Serial.println("🔄 人体存在监测功能已开启");
+ sendRadarCommand(0x84, 0x00, 0x01); // 睡眠监测
+ delay(50);
+ } else {
+ Serial.println("🔄 人体存在监测功能已关闭");
+ }
+ }
+ break;
+
+ // DP1: 存在信息主动上报
+ case 0x01:
+ if(dataLen >= 1) {
+ sensorData.presence = frame[6]; // 0:无人, 1:有人
+ Serial.printf("👤 人体存在: %s\n", sensorData.presence ? "有人" : "无人");
+ }
+ break;
+
+ // DP2: 运动信息上报
+ case 0x02:
+ if(dataLen >= 1) {
+ sensorData.motion = frame[6]; // 0:无, 1:静止, 2:活跃
+ const char* states[] = {"无", "静止", "活跃"};
+ Serial.printf("🏃 运动状态: %s\n", states[sensorData.motion]);
+ }
+ break;
+
+ // DP3: 体动参数
+ case 0x03:
+ if(dataLen >= 1) {
+ sensorData.body_movement = frame[6]; // 0-100
+ Serial.printf("📊体动参数: %d\n", sensorData.body_movement);
+ }
+ break;
+
+ // DP4: 人体距离
+ case 0x04:
+ if(dataLen >= 2) {
+ sensorData.distance = ((uint16_t)frame[6] << 8) | frame[7]; // 单位:厘米
+ Serial.printf("📏人体距离: %d cm\n", sensorData.distance);
+ }
+ break;
+
+ // DP5: 人体方位坐标 - 修正版
+ case 0x05:
+ if(dataLen >= 6) {
+ // 6字节: X(2B), Y(2B), Z(2B)
+ // 从frame[6]开始是数据域
+
+ // 解析X坐标
+ uint16_t x_raw = ((uint16_t)frame[6] << 8) | frame[7];
+ sensorData.pos_x = parseSignedCoordinate(x_raw);
+
+ // 解析Y坐标
+ uint16_t y_raw = ((uint16_t)frame[8] << 8) | frame[9];
+ sensorData.pos_y = parseSignedCoordinate(y_raw);
+
+ // 解析Z坐标
+ uint16_t z_raw = ((uint16_t)frame[10] << 8) | frame[11];
+ sensorData.pos_z = parseSignedCoordinate(z_raw);
+
+ // 调试输出
+ Serial.printf("📍方位坐标 - 原始: X=0x%04X, Y=0x%04X, Z=0x%04X\n",
+ x_raw, y_raw, z_raw);
+ Serial.printf(" 解析后: X=%d, Y=%d, Z=%d cm\n",
+ sensorData.pos_x, sensorData.pos_y, sensorData.pos_z);
+ }
+ break;
+
+ default:
+ Serial.printf("❓未知的0x80命令字: 0x%02X\n", cmdByte);
+ break;
+ }
+ break;
+
+ case CTRL_BREATH:
+ // 根据技术文档,0x81控制字的不同命令字对应不同的呼吸相关数据点
+ switch(cmdByte) {
+ // 开关呼吸监测功能
+ case 0x00:
+ case 0x80:
+ if(dataLen >= 1) {
+ if(frame[6] == 0x01)
+ Serial.println("🔄 呼吸监测功能已开启");
+ else
+ Serial.println("🔄 呼吸监测功能已关闭");
+ }
+ break;
+
+ // DP9: 呼吸信息
+ case 0x01:
+ if(dataLen >= 1) {
+ sensorData.breath_status = frame[6];
+ const char* info_str[] = {"", "正常", "呼吸过高(>25)", "呼吸过低(<10)", "无"};
+ if(sensorData.breath_status >= 1 && sensorData.breath_status <= 4) {
+ Serial.printf("🔍 呼吸信息: %s\n", info_str[sensorData.breath_status]);
+ } else {
+ Serial.printf("🔍 呼吸信息: 未知状态(0x%02X)\n", sensorData.breath_status);
+ }
+ }
+ break;
+
+ // DP8: 呼吸数值上报
+ case 0x02:
+ if(dataLen >= 1) {
+ sensorData.breath_rate = (float)frame[6]; // 0-35次/分钟
+ sensorData.breath_valid = (sensorData.breath_rate >= 0.0f &&
+ sensorData.breath_rate <= 35.0f);
+ Serial.printf("💨 呼吸率: %.1f 次/分\n", sensorData.breath_rate);
+ }
+ break;
+
+ // DP10: 呼吸波形
+ case 0x05:
+ if(dataLen >= 5) {
+ // 1秒上报5个点,每个点需要减去128
+ for(int i = 0; i < 5 && i < dataLen; i++) {
+ sensorData.breath_waveform[i] = (int8_t)(frame[6+i] - 128);
+ }
+ // 示例:打印第一个点
+ Serial.printf("📈 呼吸波形: %d\n", sensorData.breath_waveform[0]);
+ }
+ break;
+
+ default:
+ Serial.printf("❓未知的0x81命令字: 0x%02X\n", cmdByte);
+ break;
+ }
+ break;
+
+ case CTRL_HEARTRATE:
+ // 根据技术文档,0x85控制字的不同命令字对应不同的心率相关数据点
+ switch(cmdByte) {
+ // 开关心率监测功能
+ case 0x00:
+ case 0x80:
+ if(dataLen >= 1) {
+ if(frame[6] == 0x01)
+ Serial.println("🔄 心率监测功能已开启");
+ else
+ Serial.println("🔄 心率监测功能已关闭");
+ }
+ break;
+ // DP6: 心跳数值
+ case 0x02:
+ if(dataLen >= 1) {
+ sensorData.heart_rate = (float)frame[6]; // 60-120次/分钟
+ sensorData.heart_valid = (sensorData.heart_rate >= 60.0f &&
+ sensorData.heart_rate <= 120.0f);
+ Serial.printf("❤️ 心率: %.1f 次/分\n", sensorData.heart_rate);
+ }
+ break;
+
+ // DP7: 心率波形
+ case 0x05:
+ if(dataLen >= 5) {
+ // 1秒上报5个点,每个点需要减去128
+ for(int i = 0; i < 5 && i < dataLen; i++) {
+ sensorData.heart_waveform[i] = (int8_t)(frame[6+i] - 128);
+ }
+ Serial.printf("📈 心率波形: %d\n", sensorData.heart_waveform[0]);
+ }
+ break;
+
+ default:
+ Serial.printf("❓未知的0x85命令字: 0x%02X\n", cmdByte);
+ break;
+ }
+ break;
+
+ case CTRL_SLEEP:
+ // 根据技术文档,0x84控制字的不同命令字对应不同的睡眠相关数据点
+ switch(cmdByte) {
+ // DP10: 开关睡眠监测功能
+ case 0x00:
+ case 0x80:
+ if(dataLen >= 1) {
+ if(frame[6] == 0x01)
+ Serial.println("🔄 睡眠监测功能已开启");
+ else
+ Serial.println("🔄 睡眠监测功能已关闭");
+ }
+ break;
+
+ // DP11: 入床/离床状态
+ case 0x01:
+ case 0x81:
+ if(dataLen >= 1) {
+ sensorData.bed_status = frame[6]; // 0x00:离床, 0x01:入床, 0x02:无
+ const char* status_str[] = {"离床", "入床", "无"};
+ Serial.printf("🛏️ 床状态: %s\n", status_str[sensorData.bed_status]);
+ }
+ break;
+
+ // DP13: 清醒时长 (2字节,单位:分钟)
+ case 0x03:
+ case 0x83:
+ if(dataLen >= 2) {
+ sensorData.awake_time = ((uint16_t)frame[6] << 8) | (uint16_t)frame[7];
+ Serial.printf("⏰ 清醒时长: %d 分钟\n", sensorData.awake_time);
+ }
+ break;
+
+ // DP14: 浅睡时长 (2字节,单位:分钟)
+ case 0x04:
+ case 0x84:
+ if(dataLen >= 2) {
+ sensorData.light_sleep_time = ((uint16_t)frame[6] << 8) | (uint16_t)frame[7];
+ Serial.printf("😪 浅睡时长: %d 分钟\n", sensorData.light_sleep_time);
+ }
+ break;
+
+ // DP15: 深睡时长 (2字节,单位:分钟)
+ case 0x05:
+ case 0x85:
+ if(dataLen >= 2) {
+ sensorData.deep_sleep_time = ((uint16_t)frame[6] << 8) | (uint16_t)frame[7];
+ Serial.printf("💤 深睡时长: %d 分钟\n", sensorData.deep_sleep_time);
+ }
+ break;
+
+ // DP16: 睡眠质量评分 (1字节,0-100分)
+ case 0x06:
+ //case 0x86:
+ if(dataLen >= 1) {
+ sensorData.sleep_score = frame[6];
+ Serial.printf("⭐ 睡眠质量评分: %d 分\n", sensorData.sleep_score);
+ }
+ break;
+
+ case 0x86:
+ if(dataLen >= 2) {
+ //sensorData.sleep_score = ((uint16_t)frame[6] << 8) | (uint16_t)frame[7];
+ sensorData.sleep_score = frame[6];
+ Serial.printf("⭐ 睡眠质量评分: %d 分\n", sensorData.sleep_score);
+ }
+ break;
+
+
+
+ // DP17: 睡眠综合状态 (8字节)
+ case 0x0C:
+ case 0x8D:
+ if(dataLen >= 8) {
+ sensorData.presence = frame[6]; // 存在状态: 1有人, 0无人
+ sensorData.sleep_state = frame[7]; // 睡眠状态: 3离床, 2清醒, 1浅睡, 0深睡
+ sensorData.avg_breath_rate = frame[8]; // 平均呼吸率
+ sensorData.avg_heart_rate = frame[9]; // 平均心率
+ sensorData.turnover_count = frame[10]; // 翻身次数
+ sensorData.large_move_ratio = frame[11]; // 大幅度体动占比(0-100)
+ sensorData.small_move_ratio = frame[12]; // 小幅度体动占比(0-100)
+ sensorData.apnea_count = frame[13]; // 呼吸暂停次数
+
+ Serial.printf("📊 睡眠综合状态 - 存在:%d, 睡眠状态:%d, 睡眠平均呼吸:%d, 睡眠平均心率:%d, 翻身次数:%d, 大动占比:%d%%, 小动占比:%d%%, 呼吸暂停次数:%d\n",
+ sensorData.presence, sensorData.sleep_state,
+ sensorData.avg_breath_rate, sensorData.avg_heart_rate,
+ sensorData.turnover_count, sensorData.large_move_ratio,
+ sensorData.small_move_ratio, sensorData.apnea_count);
+ }
+ break;
+
+ // DP18: 睡眠质量分析报告 (12字节)
+ case 0x0D:
+ case 0x8F:
+ if(dataLen >= 12) {
+ sensorData.sleep_score = frame[6]; // 1B 睡眠评分
+ sensorData.sleep_total_time = ((uint16_t)frame[7] << 8) | (uint16_t)frame[8]; // 2B 总时长(分钟)
+
+ // 修正:以下三项为1字节的百分比,非2字节的绝对时长
+ sensorData.awake_ratio = frame[9]; // 1B 清醒时长占比
+ sensorData.light_sleep_ratio = frame[10]; // 1B 浅睡时长占比
+ sensorData.deep_sleep_ratio = frame[11]; // 1B 深睡时长占比
+
+ sensorData.bed_Out_Time = frame[12]; // 1B 离床时长
+ sensorData.turn_count = frame[13]; // 1B 离床次数
+ sensorData.turnover_count = frame[14]; // 1B 翻身次数
+ sensorData.avg_breath_rate = frame[15]; // 1B 平均呼吸率
+ sensorData.avg_heart_rate = frame[16]; // 1B 平均心跳
+ sensorData.apnea_count = frame[17]; // 1B 呼吸暂停次数
+
+ // 打印日志也应相应修改
+ Serial.printf("📈 睡眠分析报告 - 评分:%d, 总时长:%d分, 清醒占比:%d%%, 浅睡占比:%d%%, 深睡占比:%d%%, 离床时长:%d, 离床次数:%d, 翻身:%d次, 平均呼吸:%d, 平均心跳:%d\n",
+ sensorData.sleep_score,
+ sensorData.sleep_total_time,
+ sensorData.awake_ratio,
+ sensorData.light_sleep_ratio,
+ sensorData.deep_sleep_ratio,
+ sensorData.bed_Out_Time,
+ sensorData.turn_count,
+ sensorData.turnover_count,
+ sensorData.avg_breath_rate,
+ sensorData.avg_heart_rate);
+ }
+ break;
+
+ // DP19: 睡眠异常上报
+ case 0x0E:
+ case 0x8E:
+ if(dataLen >= 1) {
+ sensorData.abnormal_state = frame[6];
+ const char* abnormal_str[] = {
+ "睡眠时长不足4小时", "睡眠时长大于12小时", "长时间异常无人"
+ };
+ if(sensorData.abnormal_state < 3) {
+ Serial.printf("⚠️ 睡眠异常: %s\n", abnormal_str[sensorData.abnormal_state]);
+ }
+ }
+ break;
+
+ // DP20: 睡眠质量评级
+ case 0x10:
+ case 0x90:
+ if(dataLen >= 1) {
+ sensorData.sleep_grade = frame[6];
+ const char* rating_str[] = {"无", "睡眠质量良好", "睡眠质量一般", "睡眠质量较差"};
+ if(sensorData.sleep_grade < 4) {
+ Serial.printf("🏆 睡眠质量评级: %s\n", rating_str[sensorData.sleep_grade]);
+ }
+ }
+ break;
+
+ // DP21: 异常挣扎状态
+ case 0x11:
+ case 0x91:
+ if(dataLen >= 1) {
+ sensorData.struggle_alert = frame[6]; // 0x00:无, 0x01:正常, 0x02:异常挣扎
+ const char* struggle_str[] = {"无", "正常", "异常挣扎"};
+ if(sensorData.struggle_alert < 3) {
+ Serial.printf("⚠️ 挣扎状态: %s\n", struggle_str[sensorData.struggle_alert]);
+ }
+ }
+ break;
+
+ // DP22: 无人计时状态
+ case 0x12:
+ case 0x92:
+ if(dataLen >= 1) {
+ sensorData.no_one_alert = frame[6]; // 0x00:无, 0x01:正常, 0x02:异常
+ const char* no_one_str[] = {"无", "正常", "异常"};
+ if(sensorData.no_one_alert < 3) {
+ Serial.printf("⏰ 无人计时状态: %s\n", no_one_str[sensorData.no_one_alert]);
+ }
+ }
+ break;
+
+ default:
+ Serial.printf("❓未知的0x84命令字: 0x%02X\n", cmdByte);
+ break;
+ }
+ break;
+
+ // case 0x01: // 心跳包标识
+ // Serial.println("💓 心跳包");
+ // break;
+
+ case 0x07: // 雷达探测范围信息
+ if(dataLen >= 1) {
+ if(frame[6] == 0x00)
+ Serial.println("雷达探测范围外");
+ else
+ Serial.println("雷达探测范围内");
+ }
+ break;
+
+ default:
+ Serial.printf("❓未知控制字: 0x%02X\n", ctrlByte);
+ break;
+ }
+
+
+
+
+ // 更新传感器时间戳
+ lastSensorUpdate = millis();
+
+ // 验证数据有效性
+ sensorData.heart_valid = (sensorData.heart_rate > 0 && sensorData.heart_rate < 200);
+ sensorData.breath_valid = (sensorData.breath_rate >= 0.1f && sensorData.breath_rate <= 60.0f);
+
+ if( sensorData.heart_valid ==1 && sensorData.heart_valid == 1 && presence_Bit == 1 )
+ {
+ sensorData.presence = 1;
+ presence_Bit = 0;
+ }
+
+ return true;
+}
+
+// 解析有符号坐标值
+int16_t parseSignedCoordinate(uint16_t raw_value) {
+ // 方法1:手动解析符号位
+ bool is_negative = (raw_value & 0x8000) != 0; // 检查最高位
+ uint16_t magnitude = raw_value & 0x7FFF; // 取低15位数值
+
+ int16_t result = (int16_t)magnitude;
+ if (is_negative) {
+ result = -result; // 如果是负数,加上负号
+ }
+
+ return result;
+}
+
+// 修正后的发送雷达命令函数
+void sendRadarCommand(uint8_t ctrl, uint8_t cmd, uint8_t value) {
+ uint8_t command[10];
+ command[0] = 0x53; // 帧头1
+ command[1] = 0x59; // 帧头2
+ command[2] = ctrl; // 控制字
+ command[3] = cmd; // 命令字
+ command[4] = 0x00; // 长度高字节
+ command[5] = 0x01; // 长度低字节
+ command[6] = value; // 数据
+
+ // 修正校验和计算:帧头(2) + 控制字(1) + 命令字(1) + 长度(2) + 数据(1) = 前7个字节
+ uint8_t checksum = 0;
+ for(int i = 0; i < 7; i++) { // 计算前7个字节的和
+ checksum += command[i];
+ }
+ command[7] = checksum; // 校验码
+
+ // 帧尾
+ command[8] = 0x54; // 帧尾1
+ command[9] = 0x43; // 帧尾2
+
+ // 发送命令
+ mySerial1.write(command, 10);
+
+ // 调试输出
+ Serial.printf("📤 发送: ");
+ for(int i = 0; i < 10; i++) {
+ Serial.printf("%02X ", command[i]);
+ }
+ Serial.println();
+
+ Serial.printf(" 控制字=0x%02X, 命令字=0x%02X, 值=0x%02X, 校验和=0x%02X\n",
+ ctrl, cmd, value, checksum);
+}
+
+// 处理设置设备ID命令
+bool processSetDeviceId(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "setDeviceId") == 0) {
+ // 处理设置设备ID的命令
+ uint16_t newDeviceId = doc["newDeviceId"];
+
+ //验证设备ID范围
+ if (newDeviceId < MIN_DEVICE_ID || newDeviceId > MAX_DEVICE_ID){
+ Serial.printf("[错误] 设备ID超出范围,有效范围: %d-%d\n", MIN_DEVICE_ID, MAX_DEVICE_ID);
+ if (deviceConnected) {
+ String errorMsg = String("{\"type\":\"error\",\"message\":\"设备ID超出范围,有效范围: ") +
+ String(MIN_DEVICE_ID) + "-" + String(MAX_DEVICE_ID) + "\"}";
+
+ // 使用新的分包发送函数发送JSON数据
+ sendJSONDataToBLE(errorMsg);
+ }
+ return true; // 已处理该命令
+ }
+
+ currentDeviceId = newDeviceId;
+
+ Serial.printf("[设备ID] 已设置新的设备ID: %u\n", currentDeviceId);
+
+ // 保存设备ID到Preferences
+ saveDeviceId();
+
+ // 发送确认消息
+ if (deviceConnected) {
+ String confirmMsg = String("{\"type\":\"setDeviceIdResult\",\"success\":true,\"message\":\"设备ID设置成功\",\"newDeviceId\":") +
+ String(newDeviceId) + "}";
+
+ // 使用新的分包发送函数发送JSON数据
+ sendJSONDataToBLE(confirmMsg);
+
+ // 发送更新后的状态信息
+ sendStatusToBLE();
+ }
+ return true;
+ }
+ return false;
+}
+
+// 加载设备ID
+void loadDeviceId() {
+ currentDeviceId = preferences.getUShort("deviceId", 1001);// 从Flash加载设备ID
+ Serial.printf("从Flash加载设备ID: %u\n", currentDeviceId);
+}
+
+// 保存设备ID
+void saveDeviceId() {
+ preferences.putUShort("deviceId", currentDeviceId);// 保存设备ID到Flash
+ Serial.printf("设备ID已保存到Flash: %u\n", currentDeviceId);
+}
+
+// 发送状态信息到BLE
+void sendStatusToBLE() {
+ if (deviceConnected) {
+ String statusMsg = String("{\"type\":\"status\",\"wifiConfigured\":") +
+ String(wifiManager.getSavedNetworkCount() > 0 ? "true" : "false") +
+ String(",\"wifiConnected\":") +
+ String(WiFi.status() == WL_CONNECTED ? "true" : "false") +
+ String(",\"ipAddress\":\"") +
+ (WiFi.status() == WL_CONNECTED ? WiFi.localIP().toString() : "") + "\"" +
+ String(",\"deviceId\":") +
+ String(currentDeviceId) + "}";
+
+ sendJSONDataToBLE(statusMsg);
+ Serial.println("已发送连接状态信息");
+ }
+}
+
+bool processWiFiConfigCommand(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "setWiFiConfig") == 0) {
+ Serial.println("📱 [BLE-WiFi] 收到WiFi配置命令");
+ const char* newSSID = doc["ssid"];
+ const char* newPassword = doc["password"];
+
+ if (newSSID != nullptr && newPassword != nullptr) {
+ return wifiManager.handleConfigurationData(newSSID, newPassword);
+ } else {
+ Serial.println("❌ [BLE-WiFi] WiFi配置参数不完整");
+ if (deviceConnected) {
+ String errorMsg = String("{\"type\":\"error\",\"message\":\"WiFi配置参数不完整,需要ssid和password字段\"}");
+ sendJSONDataToBLE(errorMsg);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// 在setup()函数中添加R60ABD1初始化
+void initR60ABD1() {
+ Serial.println("🔧 初始化R60ABD1雷达模组...");
+
+ // 发送查询指令以激活数据上报
+ Serial.println("📡 发送查询指令以激活数据上报...");
+ // 查询存在信息: 53 59 80 81 00 01 00 7D 54 43
+ uint8_t queryPresenceCmd[] = {0x53, 0x59, 0x80, 0x81, 0x00, 0x01, 0x00, 0x7D, 0x54, 0x43};
+ mySerial1.write(queryPresenceCmd, sizeof(queryPresenceCmd));
+
+ // 1. 确认开启核心功能
+ Serial.println("📡 开启核心监测功能...");
+ // 发送多次人体存在监测开启命令以确保生效
+
+ sendRadarCommand(0x80, 0x00, 0x01); // 人体存在
+ delay(50);
+ sendRadarCommand(0x81, 0x00, 0x01); // 呼吸监测
+ delay(50);
+ sendRadarCommand(0x85, 0x00, 0x01); // 心率监测
+ delay(50);
+ sendRadarCommand(0x84, 0x00, 0x01); // 睡眠监测
+ delay(50);
+
+ // 2. 波形数据开启(需要确认命令字)
+ Serial.println("📡 尝试开启波形数据...");
+ sendRadarCommand(0x81, 0x0C, 0x01); // 呼吸波形
+ delay(50);
+ sendRadarCommand(0x85, 0x0A, 0x01); // 心率波形
+ delay(50);
+
+ //确认开启特殊功能
+
+ sendRadarCommand(0x84, 0x13, 0x01); // 异常挣扎状态开关设置
+ delay(50);
+ sendRadarCommand(0x84, 0x14, 0x01); // 无人计时功能开关设置
+ delay(50);
+
+ // 3. 验证初始化结果
+ Serial.println("🔍 查询当前状态...");
+ sendRadarCommand(0x80, 0x80, 0x0F); // 查询人体存在状态
+ delay(50);
+ sendRadarCommand(0x81, 0x80, 0x0F); // 查询呼吸监测状态
+ delay(50);
+ sendRadarCommand(0x85, 0x80, 0x0F); // 查询心率监测状态
+ delay(50);
+ sendRadarCommand(0x84, 0x80, 0x0F); // 查询睡眠监测状态
+
+ Serial.println("✅ R60ABD1雷达初始化完成");
+ Serial.println("📋 串口将输出解析后的雷达数据,包括:\n - 生命体征数据(心率、呼吸率)\n - 睡眠监测数据(状态、评分、时长)\n - 人体检测数据(存在、距离、运动)\n - 活动监测数据(体动幅度、翻身次数)");
+}
+
+// 检查Boot按钮状态
+void checkBootButton() {
+ Serial.println("🔍 检查Boot按钮状态...");
+
+ // 配置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) {
+ // 检测到Boot按钮按下,设置指示灯为准备清除状态
+ currentConfigClearStatus = CONFIG_PREPARING;
+ Serial.println("⚠️ 检测到Boot按钮按下,长按3秒将清除配置");
+ Serial.println("⏰ 倒计时开始...");
+
+ // 检测长按3秒
+ bootButtonPressTime = millis();
+ while (digitalRead(BOOT_BUTTON_PIN) == LOW) {
+ // 打印倒计时
+ unsigned long pressedTime = millis() - bootButtonPressTime;
+ unsigned long remaining = (CLEAR_CONFIG_DURATION - pressedTime) / 1000;
+
+ if (remaining <= 3 && remaining > 0) {
+ Serial.printf("⏳ 继续按住 %lu 秒将清除配置...\n", remaining);
+ }
+
+ if (pressedTime >= CLEAR_CONFIG_DURATION) {
+ clearConfigRequested = true;
+ Serial.println("✅ 长按3秒确认,将清除配置");
+ // 设置指示灯为清除过程中状态(呼吸灯)
+ currentConfigClearStatus = CONFIG_CLEARING;
+ break;
+ }
+
+ delay(1000); // 每秒检查一次
+ }
+
+ if (!clearConfigRequested) {
+ // 按钮释放,恢复正常状态
+ currentConfigClearStatus = CONFIG_NORMAL;
+ Serial.println("❌ 按钮释放,取消清除操作");
+ }
+ } else {
+ Serial.println("✅ Boot按钮未按下,正常启动");
+ }
+}
+
+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();
+ }
+}
+
+
+// 配置清除指示灯控制任务
+void configClearLedTask(void *parameter) {
+ while (1) {
+ switch (currentConfigClearStatus) {
+ case CONFIG_NORMAL: // 正常运行 - LOW
+ analogWrite(CONFIG_CLEAR_PIN, 0); // 关闭LED
+ break;
+
+ case CONFIG_PREPARING: // 准备清除 - HIGH
+ analogWrite(CONFIG_CLEAR_PIN, 255); // 开启LED
+ 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: // 清除完成 - 快速闪烁3次
+ 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) { // 闪烁3次 (HIGH-LOW为1次)
+ blinkCount = 0;
+ currentConfigClearStatus = CONFIG_NORMAL;
+ digitalWrite(CONFIG_CLEAR_PIN, LOW); // 确保回到LOW状态
+ }
+ }
+ break;
+ }
+
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+ }
+}
+
+// BOOT按钮监控任务
+void bootButtonMonitorTask(void *parameter) {
+ Serial.println("🔍 启动BOOT按钮监控任务...");
+
+ // 配置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;
+ }
+
+ // 检查是否长按了3秒
+ if (buttonPressed && (millis() - buttonPressStartTime >= CLEAR_CONFIG_DURATION)) {
+ if (!clearConfigRequested) {
+ clearConfigRequested = true;
+ analogWrite(NETWORK_LED_PIN, 0); // 关闭网络状态LED
+ Serial.println("✅ 长按3秒确认,将清除配置");
+
+ // 设置指示灯为清除过程中状态(呼吸灯)
+ //currentConfigClearStatus = CONFIG_CLEARING;
+
+ // 清除配置
+ clearStoredConfig();
+
+ Serial.println("🔄 配置清除完成,LED将闪烁3次表示完成...");
+
+ // 设置指示灯为清除完成状态(快速闪烁3次)
+ // currentConfigClearStatus = CONFIG_COMPLETED;
+
+ // 等待闪烁完成
+ // while(currentConfigClearStatus == CONFIG_COMPLETED) {
+ // vTaskDelay(100 / portTICK_PERIOD_MS);
+ // esp_task_wdt_reset();
+ // }
+ analogWrite(CONFIG_CLEAR_PIN, 0); // 关闭LED
+ // analogWrite(NETWORK_LED_PIN, 0); // 关闭网络状态LED
+ Serial.println("🔄 系统即将重启...");
+
+ // 短暂延迟后重启
+ vTaskDelay(1000 / portTICK_PERIOD_MS);
+ ESP.restart();
+ }
+ }
+
+ vTaskDelay(50 / portTICK_PERIOD_MS); // 每50ms检查一次
+
+ // 重置看门狗
+ esp_task_wdt_reset();
+ }
+}
+
+// LED控制任务
+void ledControlTask(void *parameter) {
+ while (1) {
+ 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); // 关闭LED
+ }
+ lastBlinkTime = millis();
+ }
+ break;
+
+ case NET_CONNECTING: // 连接中 - 快闪
+ if (millis() - lastBlinkTime >= FAST_BLINK_INTERVAL) {
+ ledState = !ledState;
+ if(ledState) {
+ ledcWrite(0, 255); // 设置为最大亮度
+ } else {
+ ledcWrite(0, 0); // 关闭LED
+ }
+ lastBlinkTime = millis();
+ }
+ break;
+
+ case NET_CONNECTED: // 已连接 - 呼吸灯效果
+ if (millis() - lastBlinkTime >= BREATHE_INTERVAL) {
+ // 呼吸灯效果
+ ledcWrite(0, breatheValue); // 使用ledcWrite替代analogWrite
+
+ // 更新呼吸灯值
+ 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);
+ }
+}
+
+// 设置网络状态
+void setNetworkStatus(NetworkStatus status) {
+ currentNetworkStatus = status;
+
+ // 切换到呼吸灯模式时,重置呼吸灯参数
+ if (status == NET_CONNECTED) {
+ breatheValue = BREATHE_MIN;
+ breatheIncreasing = true;
+ }
+}
+
+// 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;
+ }
+}
+
+void wifiMonitorTask(void *parameter) {
+ Serial.println("📡 WiFi监控任务启动");
+
+ while(1) {
+ wifiManager.update();
+ vTaskDelay(500 / portTICK_PERIOD_MS);
+ }
+}
+void setup() {
+ Serial.begin(115200);
+
+ // 第一步:检查Boot按钮是否被按下
+ checkBootButton();
+
+ // 确保结束后恢复到正常状态
+ analogWrite(CONFIG_CLEAR_PIN, 0); // 关闭呼吸灯
+ Serial.println("稳定期结束,开始工作。");
+
+ // 如果请求清除配置,这里会先执行清除操作
+ if (clearConfigRequested) {
+ clearStoredConfig();
+ // 清除后重启系统
+ Serial.println("🔄 系统即将重启...");
+ delay(1000);
+ ESP.restart();
+ }
+
+ // 添加启动信息
+ Serial.println("🚀 ESP32-R60ABD1系统启动");
+ Serial.println("🔧 初始化系统组件...");
+
+ // 初始化GPIO模式
+ pinMode(BOOT_BUTTON_PIN, INPUT); // Boot按钮引脚,输入模式,不启用内部上拉
+ pinMode(NETWORK_LED_PIN, OUTPUT); // 网络状态LED,输出模式
+ pinMode(CONFIG_CLEAR_PIN, OUTPUT); // 配置清除指示灯,输出模式
+ pinMode(GPIO8, OUTPUT); // 自定义GPIO8,输出模式
+ pinMode(GPIO9, OUTPUT); // 自定义GPIO9,输出模式
+
+ // 初始化配置清除指示灯为LOW(正常运行状态)
+ digitalWrite(CONFIG_CLEAR_PIN, LOW);
+ digitalWrite(GPIO8, LOW);
+ digitalWrite(GPIO9, LOW);
+ // 初始状态设置
+ digitalWrite(NETWORK_LED_PIN, LOW); // 网络状态LED,输出模式,输出低电平
+ digitalWrite(CONFIG_CLEAR_PIN, LOW);
+
+ // 设置PWM通道用于呼吸灯效果
+ ledcSetup(0, 5000, 8); // PWM通道0, 5kHz, 8位分辨率
+ ledcSetup(1, 5000, 8); // PWM通道1, 5kHz, 8位分辨率
+ ledcAttachPin(NETWORK_LED_PIN, 0); // 网络状态LED使用通道0
+ ledcAttachPin(CONFIG_CLEAR_PIN, 1); // 配置清除LED使用通道1
+
+ // 注册WiFi事件
+ WiFi.onEvent(WiFiEvent);
+
+
+ // 设置初始网络状态
+ setNetworkStatus(NET_INITIAL);
+
+ esp_task_wdt_init(30, true); // 初始化任务看门狗,30秒超时
+ esp_task_wdt_add(NULL);
+
+ // 增加串口缓冲区大小以处理R60ABD1数据
+ mySerial1.setRxBufferSize(UART_RX_BUFFER_SIZE);
+ mySerial1.begin(BAUD_RATE, SERIAL_8N1, UART1_RX, UART1_TX);
+
+ Serial.println("UART1配置完成,缓冲区大小: 4096字节");
+
+ preferences.begin("radar_data", false);
+
+ wifiManager.begin();
+
+ Serial.println("💾 加载设备配置...");
+ loadDeviceId();
+
+ Serial.println("🏗️ 创建FreeRTOS队列...");
+ // 创建FreeRTOS队列
+ phaseDataQueue = xQueueCreate(QUEUE_SIZE, sizeof(PhaseData));
+ vitalDataQueue = xQueueCreate(QUEUE_SIZE, sizeof(VitalData));
+ uartQueue = xQueueCreate(2048, sizeof(char)); // 创建串口数据队列
+
+ if (phaseDataQueue == NULL || vitalDataQueue == NULL || uartQueue == NULL) {
+ Serial.println("❌ 队列创建失败");
+ } else {
+ Serial.println("✅ FreeRTOS队列创建成功");
+ }
+
+ // 设置串口中断回调函数
+ mySerial1.onReceive(serialRxCallback);
+ Serial.println("✅ 串口中断回调已设置");
+
+ Serial.println("🏃 创建FreeRTOS任务...");
+ // 创建FreeRTOS任务
+ xTaskCreatePinnedToCore(
+ bleSendTask,
+ "BleSendTask",
+ TASK_STACK_SIZE,
+ NULL,
+ 3, // 提高优先级到3
+ &bleSendTaskHandle,
+ 1
+ );
+
+ xTaskCreatePinnedToCore(
+ vitalSendTask,
+ "VitalSendTask",
+ TASK_STACK_SIZE,
+ NULL,
+ 2, // 中等优先级
+ &vitalSendTaskHandle,
+ 1
+ );
+
+ // 创建专门的雷达数据处理任务
+ xTaskCreatePinnedToCore(
+ radarDataTask,
+ "RadarProcessTask",
+ 4096,
+ NULL,
+ 4, // 最高优先级
+ NULL,
+ 1
+ );
+
+ // 创建R60ABD1串口数据处理任务
+ xTaskCreatePinnedToCore(
+ uartProcessTask, // R60ABD1串口处理任务
+ "UartProcessTask",
+ 4096,
+ NULL,
+ 5, // 最高优先级
+ &uartProcessTaskHandle,
+ 1
+ );
+
+ // 创建配置清除指示灯控制任务
+ xTaskCreate(
+ configClearLedTask, // 任务函数
+ "Config Clear LED Task", // 任务名称
+ 2048, // 堆栈大小
+ NULL, // 参数
+ 1, // 优先级
+ NULL // 任务句柄
+ );
+
+ // 创建BOOT按钮监控任务
+ xTaskCreate(
+ bootButtonMonitorTask, // 任务函数
+ "Boot Button Monitor Task", // 任务名称
+ 2048, // 堆栈大小
+ NULL, // 参数
+ 1, // 优先级
+ NULL // 任务句柄
+ );
+
+ // 创建LED控制任务
+ xTaskCreate(
+ ledControlTask, // 任务函数
+ "LED Control Task", // 任务名称
+ 2048, // 堆栈大小
+ NULL, // 参数
+ 1, // 优先级
+ NULL // 任务句柄
+ );
+
+ // 创建WiFi监控任务
+ 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进行网络配置");
+ }
+
+ // 从Flash加载WiFi首次连接标志(模仿getString行为:若Flash无该键,则保持当前变量值不变)
+ 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; // 最多等待15秒
+ // 使用非阻塞方式等待WiFi连接,期间允许任务调度运行
+ 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连接成功!");
+ // 初始化R60ABD1雷达模组
+ initR60ABD1(); // 调用R60ABD1初始化函数
+
+ Serial.println("🎉 系统初始化完成,等待雷达数据...");
+
+ // 启动时发送一次睡眠数据
+ if (WiFi.status() == WL_CONNECTED) {
+ Serial.println("🌅 启动时发送睡眠数据到数据库");
+ sendSleepDataToInfluxDB();
+ }
+}
+
+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;
+ }
+ // 使用非阻塞方式轮询发送一系列查询命令,间隔2000ms
+ {
+ 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; // 2秒
+
+ 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();
+ // updateStatusFlags();
+
+ // 在主循环中添加更频繁的看门狗重置
+ esp_task_wdt_reset();
+ delay(1);
+}
\ No newline at end of file
diff --git a/src/radar_manager.cpp b/src/radar_manager.cpp
new file mode 100644
index 0000000..fb14ced
--- /dev/null
+++ b/src/radar_manager.cpp
@@ -0,0 +1,1721 @@
+#include "radar_manager.h"
+#include "wifi_manager.h"
+#include
+#include
+
+extern uint16_t currentDeviceId; // 当前设备ID
+extern Preferences preferences; // Flash存储对象
+extern WiFiManager wifiManager; // WiFi管理器对象
+
+const int BAUD_RATE = 115200; // 串口波特率
+const int UART1_RX = 3; // UART1接收引脚
+const int UART1_TX = 2; // UART1发送引脚
+
+const uint32_t PHASE_SEND_INTERVAL = 1; // 相位数据发送间隔(毫秒)
+const uint32_t VITAL_SEND_INTERVAL = 10; // 生命体征数据发送间隔(毫秒)
+
+const unsigned long SENSOR_TIMEOUT = 40000; // 传感器超时时间(毫秒)
+static uint32_t packetCounter = 0; // 数据包计数器
+static bool shouldSendOtherData = false; // 是否发送其他数据标志
+
+unsigned long lastSleepDataTime = 0; // 上次发送睡眠数据时间
+const unsigned long SLEEP_DATA_INTERVAL = 5000; // 睡眠数据发送间隔(毫秒)
+
+const char* influxDBHost = "8.134.11.76"; // InfluxDB服务器地址
+const int influxDBPort = 8086; // InfluxDB服务器端口
+const char* influxDBToken = "KuTa5ZsqoHIhi2IglOO06zExUYw1_mJ6K0mcA9X1y6O6CJDog3_Cgr8mUw1SwpuCCKRElqxa6wAhrrhsYPytkg=="; // InfluxDB访问令牌
+const char* influxDBOrg = "gzlg"; // InfluxDB组织名称
+const char* influxDBBucket = "gzlg"; // InfluxDB存储桶名称
+
+uint8_t presence_Bit = 1; // 存在标志位
+
+SensorData sensorData; // 传感器数据结构体
+HardwareSerial mySerial1(1); // 硬件串口1对象
+
+QueueHandle_t phaseDataQueue; // 相位数据队列句柄
+QueueHandle_t vitalDataQueue; // 生命体征数据队列句柄
+QueueHandle_t uartQueue; // UART数据队列句柄
+TaskHandle_t bleSendTaskHandle = NULL; // BLE发送任务句柄
+TaskHandle_t vitalSendTaskHandle = NULL; // 生命体征发送任务句柄
+TaskHandle_t uartProcessTaskHandle = NULL; // UART处理任务句柄
+
+BLEServer* pServer = NULL; // BLE服务器指针
+BLECharacteristic* pCharacteristic = NULL; // BLE特征值指针
+
+bool deviceConnected = false; // 设备连接状态
+bool oldDeviceConnected = false; // 旧设备连接状态
+String receivedData = ""; // 接收到的数据
+String completeData = ""; // 完整数据
+unsigned long lastReceiveTime = 0; // 上次接收数据时间
+
+bool continuousSendEnabled = false; // 持续发送使能标志
+unsigned long continuousSendInterval = 500; // 持续发送间隔(毫秒)
+BLEFlowController bleFlow(500); // BLE流控制器对象
+
+unsigned long lastSensorUpdate = 0; // 上次传感器更新时间
+LastSentData lastSentData = {0}; // 上次发送的数据,初始化为0
+unsigned long lastCheckTime = 0; // 上次检测时间,初始化为0
+
+/**
+ * @brief BLE流控制器构造函数
+ * 初始化BLE数据流控制参数,限制数据发送速率
+ * @param maxBps 最大每秒发送字节数
+ */
+BLEFlowController::BLEFlowController(size_t maxBps) : maxBytesPerSecond(maxBps), bytesSent(0) {
+ lastResetTime = millis();
+ lastSendTime = 0;
+}
+
+/**
+ * @brief 检查是否可以发送数据
+ * 检查当前是否满足发送条件,包括速率限制和时间间隔
+ * @param dataSize 要发送的数据大小
+ * @return 是否可以发送数据
+ */
+bool BLEFlowController::canSend(size_t dataSize) {
+ unsigned long currentTime = millis();
+
+ if(currentTime - lastResetTime >= 1000) {
+ bytesSent = 0;
+ lastResetTime = currentTime;
+ }
+
+ if((bytesSent + dataSize) > maxBytesPerSecond) {
+ return false;
+ }
+
+ if(currentTime - lastSendTime < 5) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * @brief 检查发送时间间隔
+ * 检查距离上次发送是否满足最小时间间隔
+ * @return 是否满足发送条件
+ */
+bool BLEFlowController::check() {
+ unsigned long currentTime = millis();
+ if(currentTime - lastSendTime < 5) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * @brief 记录数据发送
+ * 更新已发送字节数和最后发送时间
+ * @param dataSize 已发送的数据大小
+ */
+void BLEFlowController::recordSend(size_t dataSize) {
+ bytesSent += dataSize;
+ lastSendTime = millis();
+}
+
+/**
+ * @brief 重置流控制器
+ * 重置已发送字节数和时间戳
+ */
+void BLEFlowController::reset() {
+ bytesSent = 0;
+ lastResetTime = millis();
+ lastSendTime = 0;
+}
+
+/**
+ * @brief BLE服务器连接回调
+ * 当客户端连接时触发
+ * @param pServer BLE服务器指针
+ */
+void MyServerCallbacks::onConnect(BLEServer* pServer) {
+ deviceConnected = true;
+ Serial.println("✅ [BLE] 客户端已连接");
+}
+
+/**
+ * @brief BLE服务器断开连接回调
+ * 当客户端断开连接时触发
+ * @param pServer BLE服务器指针
+ */
+void MyServerCallbacks::onDisconnect(BLEServer* pServer) {
+ deviceConnected = false;
+ Serial.println("🔴 [BLE] 客户端已断开");
+ continuousSendEnabled = false;
+ Serial.println("🔄 重置持续发送状态");
+}
+
+/**
+ * @brief BLE特征值写入回调
+ * 当客户端写入数据时触发
+ * @param pCharacteristic BLE特征值指针
+ */
+void MyCallbacks::onWrite(BLECharacteristic *pCharacteristic) {
+ std::string value = pCharacteristic->getValue();
+ Serial.printf("🔵 [BLE] 收到写入数据,长度: %d 字节\n", value.length());
+
+ if (value.length() > 0) {
+ String fragment = "";
+ for (int i = 0; i < value.length(); i++)
+ fragment += value[i];
+
+ Serial.printf("📄 [BLE] 接收数据片段: %s\n", fragment.c_str());
+
+ completeData += fragment;
+ lastReceiveTime = millis();
+
+ int openBrace = completeData.indexOf('{');
+
+ if (openBrace >= 0) {
+ int depth = 0;
+ int closeBrace = -1;
+
+ for (int i = openBrace; i < completeData.length(); i++) {
+ char c = completeData.charAt(i);
+ if (c == '{') {
+ depth++;
+ } else if (c == '}') {
+ depth--;
+ if (depth == 0) {
+ closeBrace = i;
+ break;
+ }
+ }
+ }
+
+ if (closeBrace > openBrace) {
+ String jsonData = completeData.substring(openBrace, closeBrace + 1);
+ completeData = completeData.substring(closeBrace + 1);
+
+ Serial.printf("📥 [BLE] 完整JSON数据: %s\n", jsonData.c_str());
+ receivedData = jsonData;
+ }
+ }
+ }
+}
+
+/**
+ * @brief 初始化雷达管理器
+ * 初始化串口、队列和FreeRTOS任务
+ */
+void initRadarManager() {
+ Serial.println("🔧 初始化雷达管理器...");
+ mySerial1.setRxBufferSize(UART_RX_BUFFER_SIZE);
+ mySerial1.begin(BAUD_RATE, SERIAL_8N1, UART1_RX, UART1_TX);
+ Serial.println("UART1配置完成,缓冲区大小: 4096字节");
+
+ phaseDataQueue = xQueueCreate(QUEUE_SIZE, sizeof(PhaseData));
+ vitalDataQueue = xQueueCreate(QUEUE_SIZE, sizeof(VitalData));
+ uartQueue = xQueueCreate(2048, sizeof(char));
+
+ if (phaseDataQueue == NULL || vitalDataQueue == NULL || uartQueue == NULL) {
+ Serial.println("❌ 队列创建失败");
+ } else {
+ Serial.println("✅ FreeRTOS队列创建成功");
+ }
+
+ mySerial1.onReceive(serialRxCallback);
+ Serial.println("✅ 串口中断回调已设置");
+
+ xTaskCreatePinnedToCore(
+ bleSendTask,
+ "BleSendTask",
+ TASK_STACK_SIZE,
+ NULL,
+ 3,
+ &bleSendTaskHandle,
+ 1
+ );
+
+ xTaskCreatePinnedToCore(
+ vitalSendTask,
+ "VitalSendTask",
+ TASK_STACK_SIZE,
+ NULL,
+ 2,
+ &vitalSendTaskHandle,
+ 1
+ );
+
+ xTaskCreatePinnedToCore(
+ radarDataTask,
+ "RadarProcessTask",
+ 4096,
+ NULL,
+ 4,
+ NULL,
+ 1
+ );
+
+ xTaskCreatePinnedToCore(
+ uartProcessTask,
+ "UartProcessTask",
+ 4096,
+ NULL,
+ 5,
+ &uartProcessTaskHandle,
+ 1
+ );
+
+ Serial.println("✅ 雷达管理器初始化完成");
+}
+
+/**
+ * @brief 初始化R60ABD1雷达模组
+ * 发送初始化命令激活雷达数据上报功能
+ */
+void initR60ABD1() {
+ Serial.println("🔧 初始化R60ABD1雷达模组...");
+
+ Serial.println("📡 发送查询指令以激活数据上报...");
+ uint8_t queryPresenceCmd[] = {0x53, 0x59, 0x80, 0x81, 0x00, 0x01, 0x00, 0x7D, 0x54, 0x43};
+ mySerial1.write(queryPresenceCmd, sizeof(queryPresenceCmd));
+
+ Serial.println("📡 开启核心监测功能...");
+
+ sendRadarCommand(0x80, 0x00, 0x01);
+ delay(50);
+ sendRadarCommand(0x81, 0x00, 0x01);
+ delay(50);
+ sendRadarCommand(0x85, 0x00, 0x01);
+ delay(50);
+ sendRadarCommand(0x84, 0x00, 0x01);
+ delay(50);
+
+ Serial.println("📡 尝试开启波形数据...");
+ sendRadarCommand(0x81, 0x0C, 0x01);
+ delay(50);
+ sendRadarCommand(0x85, 0x0A, 0x01);
+ delay(50);
+
+ sendRadarCommand(0x84, 0x13, 0x01);
+ delay(50);
+ sendRadarCommand(0x84, 0x14, 0x01);
+ delay(50);
+
+ Serial.println("🔍 查询当前状态...");
+ sendRadarCommand(0x80, 0x80, 0x0F);
+ delay(50);
+ sendRadarCommand(0x81, 0x80, 0x0F);
+ delay(50);
+ sendRadarCommand(0x85, 0x80, 0x0F);
+ delay(50);
+ sendRadarCommand(0x84, 0x80, 0x0F);
+
+ Serial.println("✅ R60ABD1雷达初始化完成");
+}
+
+/**
+ * @brief 解析R60ABD1雷达数据帧
+ * 解析雷达返回的数据帧并提取传感器数据
+ * @param frame 数据帧指针
+ * @param frameLen 数据帧长度
+ * @return 是否解析成功
+ */
+bool parseR60ABD1Frame(uint8_t *frame, uint16_t frameLen) {
+ if(frameLen < 8) return false;
+
+ if(frame[0] != FRAME_HEADER1 || frame[1] != FRAME_HEADER2 ||
+ frame[frameLen-2] != FRAME_TAIL1 || frame[frameLen-1] != FRAME_TAIL2) {
+ return false;
+ }
+
+ uint8_t checksum = 0;
+ for(int i = 0; i < frameLen-3; i++) {
+ checksum += frame[i];
+ if(i % 50 == 0) {
+ esp_task_wdt_reset();
+ }
+ }
+ if(checksum != frame[frameLen-3]) {
+ Serial.println("❌ R60ABD1帧校验和错误");
+ return false;
+ }
+
+ for(int i = 0; i < frameLen && i < 20; i++) {
+ Serial.printf("%02X ", frame[i]);
+ }
+ if(frameLen > 20) Serial.print("... ");
+ Serial.printf("| 校验:0x%02X\n", frame[frameLen-3]);
+
+ uint8_t ctrlByte = frame[2];
+ uint8_t cmdByte = frame[3];
+ uint16_t dataLen = (frame[4] << 8) | frame[5];
+
+ switch(ctrlByte) {
+ case CTRL_PRESENCE:
+ switch(cmdByte) {
+ case 0x00:
+ case 0x80:
+ if(dataLen >= 1) {
+ if(frame[6] == 0x01) {
+ Serial.println("🔄 人体存在监测功能已开启");
+ sendRadarCommand(0x84, 0x00, 0x01);
+ } else {
+ Serial.println("🔄 人体存在监测功能已关闭");
+ }
+ }
+ break;
+
+ case 0x01:
+ if(dataLen >= 1) {
+ sensorData.presence = frame[6];
+ Serial.printf("👤 人体存在: %s\n", sensorData.presence ? "有人" : "无人");
+ }
+ break;
+
+ case 0x02:
+ if(dataLen >= 1) {
+ sensorData.motion = frame[6];
+ const char* states[] = {"无", "静止", "活跃"};
+ Serial.printf("🏃 运动状态: %s\n", states[sensorData.motion]);
+ }
+ break;
+
+ case 0x03:
+ if(dataLen >= 1) {
+ sensorData.body_movement = frame[6];
+ Serial.printf("📊体动参数: %d\n", sensorData.body_movement);
+ }
+ break;
+
+ case 0x04:
+ if(dataLen >= 2) {
+ sensorData.distance = ((uint16_t)frame[6] << 8) | frame[7];
+ Serial.printf("📏人体距离: %d cm\n", sensorData.distance);
+ }
+ break;
+
+ case 0x05:
+ if(dataLen >= 6) {
+ uint16_t x_raw = ((uint16_t)frame[6] << 8) | frame[7];
+ sensorData.pos_x = parseSignedCoordinate(x_raw);
+
+ uint16_t y_raw = ((uint16_t)frame[8] << 8) | frame[9];
+ sensorData.pos_y = parseSignedCoordinate(y_raw);
+
+ uint16_t z_raw = ((uint16_t)frame[10] << 8) | frame[11];
+ sensorData.pos_z = parseSignedCoordinate(z_raw);
+
+ Serial.printf("📍方位坐标 - 原始: X=0x%04X, Y=0x%04X, Z=0x%04X\n",
+ x_raw, y_raw, z_raw);
+ Serial.printf(" 解析后: X=%d, Y=%d, Z=%d cm\n",
+ sensorData.pos_x, sensorData.pos_y, sensorData.pos_z);
+ }
+ break;
+
+ default:
+ Serial.printf("❓未知的0x80命令字: 0x%02X\n", cmdByte);
+ break;
+ }
+ break;
+
+ case CTRL_BREATH:
+ switch(cmdByte) {
+ case 0x00:
+ case 0x80:
+ if(dataLen >= 1) {
+ if(frame[6] == 0x01)
+ Serial.println("🔄 呼吸监测功能已开启");
+ else
+ Serial.println("🔄 呼吸监测功能已关闭");
+ }
+ break;
+
+ case 0x01:
+ if(dataLen >= 1) {
+ sensorData.breath_status = frame[6];
+ const char* info_str[] = {"", "正常", "呼吸过高(>25)", "呼吸过低(<10)", "无"};
+ if(sensorData.breath_status >= 1 && sensorData.breath_status <= 4) {
+ Serial.printf("🔍 呼吸信息: %s\n", info_str[sensorData.breath_status]);
+ } else {
+ Serial.printf("🔍 呼吸信息: 未知状态(0x%02X)\n", sensorData.breath_status);
+ }
+ }
+ break;
+
+ case 0x02:
+ if(dataLen >= 1) {
+ sensorData.breath_rate = (float)frame[6];
+ sensorData.breath_valid = (sensorData.breath_rate >= 0.0f &&
+ sensorData.breath_rate <= 35.0f);
+ Serial.printf("💨 呼吸率: %.1f 次/分\n", sensorData.breath_rate);
+ }
+ break;
+
+ case 0x05:
+ if(dataLen >= 5) {
+ for(int i = 0; i < 5 && i < dataLen; i++) {
+ sensorData.breath_waveform[i] = (int8_t)(frame[6+i] - 128);
+ }
+ Serial.printf("📈 呼吸波形: %d\n", sensorData.breath_waveform[0]);
+ }
+ break;
+
+ default:
+ Serial.printf("❓未知的0x81命令字: 0x%02X\n", cmdByte);
+ break;
+ }
+ break;
+
+ case CTRL_HEARTRATE:
+ switch(cmdByte) {
+ case 0x00:
+ case 0x80:
+ if(dataLen >= 1) {
+ if(frame[6] == 0x01)
+ Serial.println("🔄 心率监测功能已开启");
+ else
+ Serial.println("🔄 心率监测功能已关闭");
+ }
+ break;
+ case 0x02:
+ if(dataLen >= 1) {
+ sensorData.heart_rate = (float)frame[6];
+ sensorData.heart_valid = (sensorData.heart_rate >= 60.0f &&
+ sensorData.heart_rate <= 120.0f);
+ Serial.printf("❤️ 心率: %.1f 次/分\n", sensorData.heart_rate);
+ }
+ break;
+
+ case 0x05:
+ if(dataLen >= 5) {
+ for(int i = 0; i < 5 && i < dataLen; i++) {
+ sensorData.heart_waveform[i] = (int8_t)(frame[6+i] - 128);
+ }
+ Serial.printf("📈 心率波形: %d\n", sensorData.heart_waveform[0]);
+ }
+ break;
+
+ default:
+ Serial.printf("❓未知的0x85命令字: 0x%02X\n", cmdByte);
+ break;
+ }
+ break;
+
+ case CTRL_SLEEP:
+ switch(cmdByte) {
+ case 0x00:
+ case 0x80:
+ if(dataLen >= 1) {
+ if(frame[6] == 0x01)
+ Serial.println("🔄 睡眠监测功能已开启");
+ else
+ Serial.println("🔄 睡眠监测功能已关闭");
+ }
+ break;
+
+ case 0x01:
+ case 0x81:
+ if(dataLen >= 1) {
+ sensorData.bed_status = frame[6];
+ const char* status_str[] = {"离床", "入床", "无"};
+ Serial.printf("🛏️ 床状态: %s\n", status_str[sensorData.bed_status]);
+ }
+ break;
+
+ case 0x03:
+ case 0x83:
+ if(dataLen >= 2) {
+ sensorData.awake_time = ((uint16_t)frame[6] << 8) | (uint16_t)frame[7];
+ Serial.printf("⏰ 清醒时长: %d 分钟\n", sensorData.awake_time);
+ }
+ break;
+
+ case 0x04:
+ case 0x84:
+ if(dataLen >= 2) {
+ sensorData.light_sleep_time = ((uint16_t)frame[6] << 8) | (uint16_t)frame[7];
+ Serial.printf("😪 浅睡时长: %d 分钟\n", sensorData.light_sleep_time);
+ }
+ break;
+
+ case 0x05:
+ case 0x85:
+ if(dataLen >= 2) {
+ sensorData.deep_sleep_time = ((uint16_t)frame[6] << 8) | (uint16_t)frame[7];
+ Serial.printf("💤 深睡时长: %d 分钟\n", sensorData.deep_sleep_time);
+ }
+ break;
+
+ case 0x06:
+ if(dataLen >= 1) {
+ sensorData.sleep_score = frame[6];
+ Serial.printf("⭐ 睡眠质量评分: %d 分\n", sensorData.sleep_score);
+ }
+ break;
+
+ case 0x86:
+ if(dataLen >= 2) {
+ sensorData.sleep_score = frame[6];
+ Serial.printf("⭐ 睡眠质量评分: %d 分\n", sensorData.sleep_score);
+ }
+ break;
+
+ case 0x0C:
+ case 0x8D:
+ if(dataLen >= 8) {
+ sensorData.presence = frame[6];
+ sensorData.sleep_state = frame[7];
+ sensorData.avg_breath_rate = frame[8];
+ sensorData.avg_heart_rate = frame[9];
+ sensorData.turnover_count = frame[10];
+ sensorData.large_move_ratio = frame[11];
+ sensorData.small_move_ratio = frame[12];
+ sensorData.apnea_count = frame[13];
+
+ Serial.printf("📊 睡眠综合状态 - 存在:%d, 睡眠状态:%d, 睡眠平均呼吸:%d, 睡眠平均心率:%d, 翻身次数:%d, 大动占比:%d%%, 小动占比:%d%%, 呼吸暂停次数:%d\n",
+ sensorData.presence, sensorData.sleep_state,
+ sensorData.avg_breath_rate, sensorData.avg_heart_rate,
+ sensorData.turnover_count, sensorData.large_move_ratio,
+ sensorData.small_move_ratio, sensorData.apnea_count);
+ }
+ break;
+
+ case 0x0D:
+ case 0x8F:
+ if(dataLen >= 12) {
+ sensorData.sleep_score = frame[6];
+ sensorData.sleep_total_time = ((uint16_t)frame[7] << 8) | (uint16_t)frame[8];
+
+ sensorData.awake_ratio = frame[9];
+ sensorData.light_sleep_ratio = frame[10];
+ sensorData.deep_sleep_ratio = frame[11];
+
+ sensorData.bed_Out_Time = frame[12];
+ sensorData.turn_count = frame[13];
+ sensorData.turnover_count = frame[14];
+ sensorData.avg_breath_rate = frame[15];
+ sensorData.avg_heart_rate = frame[16];
+ sensorData.apnea_count = frame[17];
+
+ Serial.printf("📈 睡眠分析报告 - 评分:%d, 总时长:%d分, 清醒占比:%d%%, 浅睡占比:%d%%, 深睡占比:%d%%, 离床时长:%d, 离床次数:%d, 翻身:%d次, 平均呼吸:%d, 平均心跳:%d\n",
+ sensorData.sleep_score,
+ sensorData.sleep_total_time,
+ sensorData.awake_ratio,
+ sensorData.light_sleep_ratio,
+ sensorData.deep_sleep_ratio,
+ sensorData.bed_Out_Time,
+ sensorData.turn_count,
+ sensorData.turnover_count,
+ sensorData.avg_breath_rate,
+ sensorData.avg_heart_rate);
+ }
+ break;
+
+ case 0x0E:
+ case 0x8E:
+ if(dataLen >= 1) {
+ sensorData.abnormal_state = frame[6];
+ const char* abnormal_str[] = {
+ "睡眠时长不足4小时", "睡眠时长大于12小时", "长时间异常无人"
+ };
+ if(sensorData.abnormal_state < 3) {
+ Serial.printf("⚠️ 睡眠异常: %s\n", abnormal_str[sensorData.abnormal_state]);
+ }
+ }
+ break;
+
+ case 0x10:
+ case 0x90:
+ if(dataLen >= 1) {
+ sensorData.sleep_grade = frame[6];
+ const char* rating_str[] = {"无", "睡眠质量良好", "睡眠质量一般", "睡眠质量较差"};
+ if(sensorData.sleep_grade < 4) {
+ Serial.printf("🏆 睡眠质量评级: %s\n", rating_str[sensorData.sleep_grade]);
+ }
+ }
+ break;
+
+ case 0x11:
+ case 0x91:
+ if(dataLen >= 1) {
+ sensorData.struggle_alert = frame[6];
+ const char* struggle_str[] = {"无", "正常", "异常挣扎"};
+ if(sensorData.struggle_alert < 3) {
+ Serial.printf("⚠️ 挣扎状态: %s\n", struggle_str[sensorData.struggle_alert]);
+ }
+ }
+ break;
+
+ case 0x12:
+ case 0x92:
+ if(dataLen >= 1) {
+ sensorData.no_one_alert = frame[6];
+ const char* no_one_str[] = {"无", "正常", "异常"};
+ if(sensorData.no_one_alert < 3) {
+ Serial.printf("⏰ 无人计时状态: %s\n", no_one_str[sensorData.no_one_alert]);
+ }
+ }
+ break;
+
+ default:
+ Serial.printf("❓未知的0x84命令字: 0x%02X\n", cmdByte);
+ break;
+ }
+ break;
+
+ case 0x07:
+ if(dataLen >= 1) {
+ if(frame[6] == 0x00)
+ Serial.println("雷达探测范围外");
+ else
+ Serial.println("雷达探测范围内");
+ }
+ break;
+
+ default:
+ Serial.printf("❓未知控制字: 0x%02X\n", ctrlByte);
+ break;
+ }
+
+ lastSensorUpdate = millis();
+
+ sensorData.heart_valid = (sensorData.heart_rate > 0 && sensorData.heart_rate < 200);
+ sensorData.breath_valid = (sensorData.breath_rate >= 0.1f && sensorData.breath_rate <= 60.0f);
+
+ if( sensorData.heart_valid ==1 && sensorData.heart_valid == 1 && presence_Bit == 1 )
+ {
+ sensorData.presence = 1;
+ presence_Bit = 0;
+ }
+
+ return true;
+}
+
+int16_t parseSignedCoordinate(uint16_t raw_value) {
+ bool is_negative = (raw_value & 0x8000) != 0;
+ uint16_t magnitude = raw_value & 0x7FFF;
+
+ int16_t result = (int16_t)magnitude;
+ if (is_negative) {
+ result = -result;
+ }
+
+ return result;
+}
+
+/**
+ * @brief 发送雷达命令
+ * 构造并发送控制命令到雷达模组
+ * @param ctrl 控制字节
+ * @param cmd 命令字节
+ * @param value 值字节
+ */
+void sendRadarCommand(uint8_t ctrl, uint8_t cmd, uint8_t value) {
+ uint8_t command[10];
+ command[0] = 0x53;
+ command[1] = 0x59;
+ command[2] = ctrl;
+ command[3] = cmd;
+ command[4] = 0x00;
+ command[5] = 0x01;
+ command[6] = value;
+
+ uint8_t checksum = 0;
+ for(int i = 0; i < 7; i++) {
+ checksum += command[i];
+ }
+ command[7] = checksum;
+
+ command[8] = 0x54;
+ command[9] = 0x43;
+
+ mySerial1.write(command, 10);
+
+ Serial.printf("📤 发送: ");
+ for(int i = 0; i < 10; i++) {
+ Serial.printf("%02X ", command[i]);
+ }
+ Serial.println();
+
+ Serial.printf(" 控制字=0x%02X, 命令字=0x%02X, 值=0x%02X, 校验和=0x%02X\n",
+ ctrl, cmd, value, checksum);
+}
+
+void IRAM_ATTR serialRxCallback() {
+ if (uartQueue != NULL) {
+ while(mySerial1.available()) {
+ char c = mySerial1.read();
+
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ xQueueSendFromISR(uartQueue, &c, &xHigherPriorityTaskWoken);
+
+ if(xHigherPriorityTaskWoken) {
+ portYIELD_FROM_ISR();
+ }
+ }
+ }
+}
+
+/**
+ * @brief 检查数据是否发生变化
+ * 比较当前传感器数据与上次发送的数据,判断是否需要发送更新
+ * @return 是否发生变化
+ */
+bool isDataChanged() {
+ // 心率变化阈值:0.5
+ if (fabs(sensorData.heart_rate - lastSentData.heart_rate) > 0.5) {
+ return true;
+ }
+
+ // 呼吸率变化阈值:0.5
+ if (fabs(sensorData.breath_rate - lastSentData.breath_rate) > 0.5) {
+ return true;
+ }
+
+ // 存在状态变化
+ if (sensorData.presence != lastSentData.presence) {
+ return true;
+ }
+
+ // 运动状态变化
+ if (sensorData.motion != lastSentData.motion) {
+ return true;
+ }
+
+ // 睡眠状态变化
+ if (sensorData.sleep_state != lastSentData.sleep_state) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * @brief BLE数据发送任务
+ * 实现基于数据变化和定时检测的发送机制
+ * 1. 定时检测数据变化(基于continuousSendInterval)
+ * 2. 检测到变化时立即发送数据
+ * 3. 发送后更新lastSentData为当前数据
+ * @param parameter 任务参数(未使用)
+ */
+void bleSendTask(void *parameter) {
+ Serial.println("🔁 R60ABD1蓝牙数据发送任务启动");
+
+ while (1) {
+ esp_task_wdt_reset();
+
+ // 检查是否需要进行数据检测
+ if (continuousSendEnabled && deviceConnected) {
+ unsigned long currentTime = millis();
+
+ // 按照设定的时间间隔进行检测
+ if (currentTime - lastCheckTime >= continuousSendInterval) {
+ lastCheckTime = currentTime;
+
+ // 检查数据是否发生变化
+ if (isDataChanged()) {
+ // 构建雷达数据字符串
+ String radarDataCore;
+
+ if (sensorData.presence > 0) {
+ radarDataCore = String(sensorData.heart_rate, 1) + String("|") +
+ String(sensorData.breath_rate, 1) + String("|") +
+ String((int)sensorData.heart_waveform[0]) + String("|") +
+ String((int)sensorData.breath_waveform[0]) + String("|") +
+ String(sensorData.presence) + String("|") +
+ String(sensorData.motion) + String("|") +
+ String(sensorData.sleep_state);
+ } else {
+ radarDataCore = String("0.0") + String("|") +
+ String("0.0") + String("|") +
+ String("0") + String("|") +
+ String("0") + String("|") +
+ String("0") + String("|") +
+ String("0") + String("|") +
+ String("0");
+ }
+
+ // 计算CRC校验
+ unsigned int crc = 0xFFFF;
+ for (int i = 0; i < radarDataCore.length(); i++) {
+ crc ^= (unsigned int)radarDataCore.charAt(i);
+ for (int j = 0; j < 8; j++) {
+ if (crc & 0x0001) {
+ crc >>= 1;
+ crc ^= 0xA001;
+ } else {
+ crc >>= 1;
+ }
+ }
+ }
+
+ String radarDataMsg = radarDataCore + String("|") + String(crc, HEX);
+
+ Serial.printf("📤 通过蓝牙发送R60ABD1雷达数据: %s\n", radarDataMsg.c_str());
+
+ const int MAX_BLE_PACKET_SIZE = 20;
+ if (radarDataMsg.length() <= MAX_BLE_PACKET_SIZE) {
+ pCharacteristic->setValue(radarDataMsg.c_str());
+ pCharacteristic->notify();
+ Serial.println("✅ R60ABD1雷达数据蓝牙发送成功");
+ } else {
+ Serial.println("🔄 R60ABD1雷达数据较长,使用分包发送");
+ sendDataInChunks(radarDataMsg);
+ }
+
+ // 更新上次发送的数据
+ lastSentData.heart_rate = sensorData.heart_rate;
+ lastSentData.breath_rate = sensorData.breath_rate;
+ lastSentData.presence = sensorData.presence;
+ lastSentData.motion = sensorData.motion;
+ lastSentData.sleep_state = sensorData.sleep_state;
+
+ Serial.println("📊 数据已更新,上次发送数据已保存");
+ }
+ }
+ }
+
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+ esp_task_wdt_reset();
+ }
+}
+
+/**
+ * @brief 生命体征数据发送任务
+ * 从队列中获取生命体征数据并发送到InfluxDB数据库
+ * @param parameter 任务参数(未使用)
+ */
+void vitalSendTask(void *parameter) {
+ Serial.println("🔁🔁 生命体征数据发送任务启动(WiFi数据库传输)");
+
+ unsigned long lastSleepDataTime = 0;
+ const unsigned long SLEEP_DATA_INTERVAL = 5000;
+
+ while (1) {
+ VitalData vitalData;
+
+ if (xQueueReceive(vitalDataQueue, &vitalData, portMAX_DELAY) == pdTRUE) {
+ esp_task_wdt_reset();
+
+ if (WiFi.status() == WL_CONNECTED) {
+ // 检查心率和呼吸率是否都为0,如果是则跳过发送
+ if (vitalData.heart_rate == 0 || vitalData.breath_rate == 0) {
+ Serial.println("⚠️ 心率和呼吸率都为0,跳过发送数据到数据库");
+ continue;
+ }
+
+ String dailyDataLine = "daily_data,deviceId=" + String(currentDeviceId) + ",dataType=daily ";
+
+ bool firstField = true;
+
+ if (vitalData.heart_rate > 0) {
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "heartRate=" + String(vitalData.heart_rate, 1);
+ firstField = false;
+ }
+
+ if (vitalData.breath_rate > 0) {
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "breathingRate=" + String(vitalData.breath_rate, 1);
+ firstField = false;
+ }
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "personDetected=" + String(vitalData.presence) + "i";
+ firstField = false;
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "humanActivity=" + String(vitalData.motion) + "i";
+ firstField = false;
+
+ if (vitalData.distance > 0) {
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "humanDistance=" + String(vitalData.distance) + "i";
+ firstField = false;
+ }
+
+ if (vitalData.sleep_state >= 0) {
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "sleepState=" + String(vitalData.sleep_state) + "i";
+ firstField = false;
+ }
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "humanPositionX=" + String(vitalData.pos_x) + "i";
+ firstField = false;
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "humanPositionY=" + String(vitalData.pos_y) + "i";
+ firstField = false;
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "humanPositionZ=" + String(vitalData.pos_z) + "i";
+ firstField = false;
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "heartbeatWaveform=" + String((int)sensorData.heart_waveform[0]) + "i";
+ firstField = false;
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "breathingWaveform=" + String((int)sensorData.breath_waveform[0]) + "i";
+ firstField = false;
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "abnormalState=" + String(vitalData.abnormal_state) + "i";
+ firstField = false;
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "bedStatus=" + String(vitalData.bed_status) + "i";
+ firstField = false;
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "struggleAlert=" + String(vitalData.struggle_alert) + "i";
+ firstField = false;
+
+ if (!firstField) dailyDataLine += ",";
+ dailyDataLine += "noOneAlert=" + String(vitalData.no_one_alert) + "i";
+ firstField = false;
+
+ if (!dailyDataLine.endsWith(" ")) {
+ sendDailyDataToInfluxDB(dailyDataLine);
+ esp_task_wdt_reset();
+ }
+
+ unsigned long currentTime = millis();
+ if (currentTime - lastSleepDataTime >= SLEEP_DATA_INTERVAL) {
+ sendSleepDataToInfluxDB();
+ lastSleepDataTime = currentTime;
+ Serial.println("⏰ 睡眠数据定时发送完成");
+ }
+ } else {
+ Serial.println("❌❌ WiFi未连接,无法发送雷达数据到数据库");
+
+ static unsigned long lastWifiCheck = 0;
+ if (millis() - lastWifiCheck > 10000) {
+ Serial.printf("📶📶 WiFi状态: %d\n", WiFi.status());
+ lastWifiCheck = millis();
+ }
+ }
+
+ esp_task_wdt_reset();
+ }
+
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+ }
+}
+
+/**
+ * @brief 发送日常数据到InfluxDB数据库
+ * 通过HTTP协议将数据写入InfluxDB时序数据库
+ * @param dailyDataLine 数据行字符串
+ */
+void sendDailyDataToInfluxDB(String dailyDataLine) {
+ if (WiFi.status() != WL_CONNECTED) {
+ Serial.println("❌ WiFi未连接,无法发送日常数据到数据库");
+ return;
+ }
+
+ HTTPClient http;
+ http.setTimeout(2000);
+
+ String url = String("http://") + String(influxDBHost) + ":" + String(influxDBPort) + "/api/v2/write?org=" + String(influxDBOrg) + "&bucket=" + String(influxDBBucket);
+
+ http.begin(url);
+ http.addHeader("Authorization", String("Token ") + String(influxDBToken));
+ http.addHeader("Content-Type", "text/plain; charset=utf-8");
+ http.setReuse(true);
+
+ Serial.println(String("📊 发送日常数据到InfluxDB: ") + dailyDataLine);
+
+ int httpResponseCode = http.POST(dailyDataLine);
+
+ if (httpResponseCode == 204) {
+ Serial.println("✅ 日常数据发送成功");
+ } else {
+ Serial.println(String("❌ 发送日常数据失败: ") + String(httpResponseCode) + " - " + http.getString());
+ }
+
+ http.end();
+}
+
+/**
+ * @brief 发送睡眠数据到InfluxDB数据库
+ * 将睡眠相关的统计数据发送到InfluxDB时序数据库
+ */
+void sendSleepDataToInfluxDB() {
+ if (WiFi.status() != WL_CONNECTED) {
+ Serial.println("❌ WiFi未连接,无法发送睡眠数据到数据库");
+ return;
+ }
+
+ if (sensorData.sleep_total_time == 0) {
+ Serial.println("😴 总睡眠时长为0,跳过上传睡眠数据");
+ return;
+ }
+
+ HTTPClient http;
+ http.setTimeout(2000);
+
+ String url = String("http://") + String(influxDBHost) + ":" + String(influxDBPort) + "/api/v2/write?org=" + String(influxDBOrg) + "&bucket=" + String(influxDBBucket);
+
+ http.begin(url);
+ http.addHeader("Authorization", String("Token ") + String(influxDBToken));
+ http.addHeader("Content-Type", "text/plain; charset=utf-8");
+ http.setReuse(true);
+
+ String lineProtocol = String("sleep_data,deviceId=") + String(currentDeviceId) + ",dataType=sleep ";
+
+ String fields = "";
+ fields += String("sleepQualityScore=") + String((int)sensorData.sleep_score) + "i";
+ fields += ",sleepQualityGrade=" + String((int)sensorData.sleep_grade) + "i";
+ fields += ",totalSleepDuration=" + String((int)sensorData.sleep_total_time) + "i";
+ fields += ",awakeDurationRatio=" + String((int)sensorData.awake_ratio) + "i";
+ fields += ",lightSleepRatio=" + String((int)sensorData.light_sleep_ratio) + "i";
+ fields += ",deepSleepRatio=" + String((int)sensorData.deep_sleep_ratio) + "i";
+ fields += ",outOfBedDuration=" + String((int)sensorData.bed_Out_Time) + "i";
+ fields += ",outOfBedCount=" + String((int)sensorData.turn_count) + "i";
+ fields += ",turnCount=" + String((int)sensorData.turnover_count) + "i";
+ fields += ",avgBreathingRate=" + String((int)sensorData.avg_breath_rate) + "i";
+ fields += ",avgHeartRate=" + String((int)sensorData.avg_heart_rate) + "i";
+ fields += ",apneaCount=" + String((int)sensorData.apnea_count) + "i";
+ fields += ",abnormalState=" + String((int)sensorData.abnormal_state) + "i";
+ fields += ",bodyMovement=" + String((int)sensorData.body_movement) + "i";
+ fields += ",breathStatus=" + String((int)sensorData.breath_status) + "i";
+ fields += ",sleepState=" + String((int)sensorData.sleep_state) + "i";
+ fields += ",largeMoveRatio=" + String((int)sensorData.large_move_ratio) + "i";
+ fields += ",smallMoveRatio=" + String((int)sensorData.small_move_ratio) + "i";
+ fields += ",struggleAlert=" + String((int)sensorData.struggle_alert) + "i";
+ fields += ",noOneAlert=" + String((int)sensorData.no_one_alert) + "i";
+ fields += ",awakeDuration=" + String((int)sensorData.awake_time) + "i";
+ fields += ",lightSleepDuration=" + String((int)sensorData.light_sleep_time) + "i";
+ fields += ",deepSleepDuration=" + String((int)sensorData.deep_sleep_time) + "i";
+
+ lineProtocol += fields;
+
+ Serial.println(String("🌙 发送睡眠数据到InfluxDB: ") + lineProtocol);
+
+ int httpResponseCode = http.POST(lineProtocol);
+
+ if (httpResponseCode == 204) {
+ Serial.println(String("✅ 睡眠数据已保存到InfluxDB设备") + String(currentDeviceId) + "上");
+ } else {
+ Serial.println(String("❌ 保存睡眠数据到InfluxDB失败: ") + String(httpResponseCode) + " - " + http.getString());
+ }
+
+ http.end();
+}
+
+/**
+ * @brief 雷达数据处理任务
+ * 处理雷达数据并分发到相应的队列
+ * @param parameter 任务参数(未使用)
+ */
+void radarDataTask(void *parameter) {
+ Serial.println("🔁 雷达数据处理任务启动(最高优先级)");
+
+ while (1) {
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ }
+}
+
+/**
+ * @brief UART数据处理任务
+ * 从UART队列中读取数据并解析雷达数据帧
+ * @param parameter 任务参数(未使用)
+ */
+void uartProcessTask(void *parameter) {
+ uint8_t buffer[256];
+ int bufferIndex = 0;
+ bool inFrame = false;
+ uint8_t prevByte = 0;
+
+ Serial.println("✅ R60ABD1串口数据处理任务启动");
+
+ while(1) {
+ uint8_t c;
+ if(xQueueReceive(uartQueue, &c, 10 / portTICK_PERIOD_MS) == pdTRUE) {
+ esp_task_wdt_reset();
+ if(!inFrame) {
+ if(prevByte == FRAME_HEADER1 && c == FRAME_HEADER2) {
+ buffer[0] = FRAME_HEADER1;
+ buffer[1] = FRAME_HEADER2;
+ bufferIndex = 2;
+ inFrame = true;
+ Serial.println("🔍 检测到R60ABD1帧头");
+ }
+ } else {
+ if(bufferIndex < sizeof(buffer)) {
+ buffer[bufferIndex++] = c;
+
+ if(bufferIndex >= 8 &&
+ buffer[bufferIndex-2] == FRAME_TAIL1 &&
+ buffer[bufferIndex-1] == FRAME_TAIL2) {
+ if(parseR60ABD1Frame(buffer, bufferIndex)) {
+ static uint32_t frameCounter = 0;
+ frameCounter++;
+
+ lastSensorUpdate = millis();
+
+ static uint32_t phasePacketCounter = 0;
+ static uint32_t vitalPacketCounter = 0;
+
+ phasePacketCounter++;
+ if (phasePacketCounter >= PHASE_SEND_INTERVAL) {
+ PhaseData phaseData;
+ phaseData.heartbeat_waveform = sensorData.heartbeat_waveform;
+ phaseData.breathing_waveform = sensorData.breathing_waveform;
+
+ if (xQueueSend(phaseDataQueue, &phaseData, 0) == pdTRUE) {
+ } else {
+ Serial.println("❌ 相位数据队列已满,数据丢失");
+ }
+ phasePacketCounter = 0;
+ }
+
+ vitalPacketCounter++;
+ if (vitalPacketCounter >= VITAL_SEND_INTERVAL) {
+ VitalData vitalData;
+ vitalData.heart_rate = sensorData.heart_rate;
+ vitalData.breath_rate = sensorData.breath_rate;
+ vitalData.presence = sensorData.presence;
+ vitalData.motion = sensorData.motion;
+ vitalData.distance = sensorData.distance;
+ vitalData.sleep_state = sensorData.sleep_state;
+ vitalData.sleep_score = sensorData.sleep_score;
+ vitalData.body_movement = sensorData.body_movement;
+ vitalData.breath_status = sensorData.breath_status;
+ vitalData.sleep_time = sensorData.sleep_time;
+ vitalData.bed_status = sensorData.bed_status;
+ vitalData.abnormal_state = sensorData.abnormal_state;
+ vitalData.avg_heart_rate = sensorData.avg_heart_rate;
+ vitalData.avg_breath_rate = sensorData.avg_breath_rate;
+ vitalData.turn_count = sensorData.turn_count;
+ vitalData.large_move_ratio = sensorData.large_move_ratio;
+ vitalData.small_move_ratio = sensorData.small_move_ratio;
+ vitalData.pos_x = sensorData.pos_x;
+ vitalData.pos_y = sensorData.pos_y;
+ vitalData.pos_z = sensorData.pos_z;
+ vitalData.deep_sleep_time = sensorData.deep_sleep_time;
+ vitalData.light_sleep_time = sensorData.light_sleep_time;
+ vitalData.awake_time = sensorData.awake_time;
+ vitalData.sleep_total_time = sensorData.sleep_total_time;
+ vitalData.deep_sleep_ratio = sensorData.deep_sleep_ratio;
+ vitalData.light_sleep_ratio = sensorData.light_sleep_ratio;
+ vitalData.awake_ratio = sensorData.awake_ratio;
+ vitalData.turnover_count = sensorData.turnover_count;
+ vitalData.struggle_alert = sensorData.struggle_alert;
+ vitalData.no_one_alert = sensorData.no_one_alert;
+ vitalData.apnea_count = sensorData.apnea_count;
+ vitalData.heartbeat_waveform = sensorData.heartbeat_waveform;
+ vitalData.breathing_waveform = sensorData.breathing_waveform;
+
+ if (xQueueSend(vitalDataQueue, &vitalData, 0) == pdTRUE) {
+ Serial.println("📤 生命体征数据已加入发送队列");
+ } else {
+ Serial.println("❌ 生命体征数据队列已满,数据丢失");
+ }
+ vitalPacketCounter = 0;
+ }
+
+ if(frameCounter % 100 == 0) {
+ Serial.printf("📈 已处理 %d 个R60ABD1数据帧\n", frameCounter);
+ }
+ }
+
+ inFrame = false;
+ }
+ } else {
+ Serial.println("⚠️ R60ABD1帧缓冲区溢出,重置接收状态");
+ inFrame = false;
+ }
+ }
+
+ prevByte = c;
+ }
+
+ vTaskDelay(1 / portTICK_PERIOD_MS);
+ esp_task_wdt_reset();
+ }
+}
+
+/**
+ * @brief 分块发送数据
+ * 将大数据分块发送,避免BLE MTU限制
+ * @param data 要发送的数据字符串
+ */
+void sendDataInChunks(const String& data) {
+ const int MAX_PACKET_SIZE = 20;
+ const int HEADER_SIZE = 6;
+ const int CHUNK_SIZE = MAX_PACKET_SIZE - HEADER_SIZE;
+
+ int totalLength = data.length();
+ int numChunks = (totalLength + CHUNK_SIZE - 1) / CHUNK_SIZE;
+
+ Serial.printf("📦 开始分包发送,总长度: %d, 分包数: %d\n", totalLength, numChunks);
+
+ for(int i = 0; i < numChunks; i++) {
+ int start = i * CHUNK_SIZE;
+ int chunkLength = min(CHUNK_SIZE, totalLength - start);
+ String chunk = data.substring(start, start + chunkLength);
+
+ String packetHeader = String("[") + String(i+1) + String("/") + String(numChunks) + String("]");
+
+ int maxDataLength = MAX_PACKET_SIZE - packetHeader.length();
+ if (chunk.length() > maxDataLength) {
+ chunk = chunk.substring(0, maxDataLength);
+ }
+
+ String packet = packetHeader + chunk;
+
+ Serial.printf("📤 发送分包 %s: %s\n", packetHeader.c_str(), chunk.c_str());
+
+ if (!deviceConnected) {
+ Serial.println("❌ BLE未连接,无法发送数据");
+ return;
+ }
+
+ pCharacteristic->setValue(packet.c_str());
+ pCharacteristic->notify();
+ Serial.println("✅ 分包发送成功");
+
+ if (i < numChunks - 1) {
+ Serial.printf("⏳ 等待接收端处理第%d个包...\n", i+1);
+ vTaskDelay(20 / portTICK_PERIOD_MS);
+ }
+ }
+
+ Serial.println("📦 分包发送完成");
+}
+
+/**
+ * @brief 发送JSON数据到BLE
+ * 将JSON格式数据通过BLE发送给客户端
+ * @param jsonData JSON格式数据字符串
+ */
+void sendJSONDataToBLE(const String& jsonData) {
+ Serial.printf("📤 准备发送JSON数据: %s\n", jsonData.c_str());
+
+ if (!deviceConnected) {
+ Serial.println("❌ BLE未连接,无法发送JSON数据");
+ return;
+ }
+
+ const int MAX_PACKET_SIZE = 20;
+ int totalLength = jsonData.length();
+ int numChunks = (totalLength + MAX_PACKET_SIZE - 1) / MAX_PACKET_SIZE;
+
+ Serial.printf("📦 开始分包发送JSON,总长度: %d, 分包数: %d\n", totalLength, numChunks);
+
+ for(int i = 0; i < numChunks; i++) {
+ int start = i * MAX_PACKET_SIZE;
+ int chunkLength = min(MAX_PACKET_SIZE, totalLength - start);
+ String chunk = jsonData.substring(start, start + chunkLength);
+
+ Serial.printf("📤 发送JSON分包 %d/%d: %s\n", i+1, numChunks, chunk.c_str());
+
+ pCharacteristic->setValue(chunk.c_str());
+ pCharacteristic->notify();
+ Serial.println("✅ JSON分包发送成功");
+
+ if (i < numChunks - 1) {
+ Serial.printf("⏳ 等待接收端处理第%d个包...\n", i+1);
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+ }
+ }
+
+ Serial.println("📦 JSON分包发送完成");
+}
+
+/**
+ * @brief 发送自定义JSON数据到BLE
+ * 构造自定义JSON格式数据并通过BLE发送
+ * @param jsonType JSON类型
+ * @param jsonString JSON内容字符串
+ * @return 是否发送成功
+ */
+bool sendCustomJSONData(const String& jsonType, const String& jsonString) {
+ if (!deviceConnected) {
+ Serial.println("❌ BLE未连接,无法发送自定义JSON数据");
+ return false;
+ }
+
+ String fullJSON = String("{\"type\":\"") + jsonType + String("\",") + jsonString + String("}");
+
+ Serial.printf("📤 发送自定义JSON数据类型 '%s': %s\n", jsonType.c_str(), fullJSON.c_str());
+
+ sendJSONDataToBLE(fullJSON);
+
+ return true;
+}
+
+/**
+ * @brief 发送雷达数据到BLE
+ * 发送雷达传感器数据(已移至FreeRTOS任务处理)
+ */
+void sendRadarDataToBLE() {
+ Serial.println("ℹ️ 雷达数据发送已移至FreeRTOS任务处理");
+}
+
+/**
+ * @brief 处理查询雷达数据命令
+ * 处理来自BLE的雷达数据查询请求
+ * @param doc JSON文档对象
+ * @return 是否处理成功
+ */
+bool processQueryRadarData(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "queryRadarData") == 0) {
+ Serial.println("收到查询雷达数据命令");
+
+ if (deviceConnected) {
+ String radarDataMsg = String("{\"type\":\"radarData\",\"success\":true") +
+ String(",\"deviceId\":") + String(currentDeviceId) +
+ String(",\"timestamp\":") + String(millis()) +
+ String(",\"presence\":") + String(sensorData.presence) +
+ String(",\"heartRate\":") + String(sensorData.heart_rate, 1) +
+ String(",\"breathRate\":") + String(sensorData.breath_rate, 1) +
+ String(",\"motion\":") + String(sensorData.motion) +
+ String(",\"heartbeatWaveform\":") + String((int)sensorData.breath_waveform[0]) +
+ String(",\"breathingWaveform\":") + String((int)sensorData.heart_waveform[0]) +
+ String(",\"distance\":") + String(sensorData.distance) +
+ String(",\"bodyMovement\":") + String(sensorData.body_movement) +
+ String(",\"breathStatus\":") + String(sensorData.breath_status) +
+ String(",\"sleepState\":") + String(sensorData.sleep_state) +
+ String(",\"sleepTime\":") + String(sensorData.sleep_time) +
+ String(",\"sleepScore\":") + String(sensorData.sleep_score) +
+ String(",\"avgHeartRate\":") + String(sensorData.avg_heart_rate) +
+ String(",\"avgBreathRate\":") + String(sensorData.avg_breath_rate) +
+ String(",\"turnCount\":") + String(sensorData.turn_count) +
+ String(",\"largeMoveRatio\":") + String(sensorData.large_move_ratio) +
+ String(",\"smallMoveRatio\":") + String(sensorData.small_move_ratio) +
+ String("}");
+
+ sendJSONDataToBLE(radarDataMsg);
+ Serial.println("已发送雷达数据");
+ Serial.printf("发送的数据: %s\n", radarDataMsg.c_str());
+ } else {
+ Serial.println("BLE未连接,无法发送雷达数据");
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief 处理启动持续发送命令
+ * 处理来自BLE的启动持续发送请求
+ * @param doc JSON文档对象
+ * @return 是否处理成功
+ */
+bool processStartContinuousSend(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "startContinuousSend") == 0) {
+ if (doc["interval"].is()) {
+ continuousSendInterval = doc["interval"].as();
+ if (continuousSendInterval < 100) continuousSendInterval = 100;
+ if (continuousSendInterval > 10000) continuousSendInterval = 10000;
+ }
+
+ continuousSendEnabled = true;
+ bleFlow.reset();
+
+ Serial.printf("⚙️ 启动持续发送模式,间隔: %lu ms\n", continuousSendInterval);
+
+ if (deviceConnected) {
+ String confirmMsg = String("{\"type\":\"startContinuousSendResult\",\"success\":true,\"message\":\"已启动持续发送模式\",\"interval\":") +
+ String(continuousSendInterval) + "}";
+
+ sendJSONDataToBLE(confirmMsg);
+ Serial.println("✅ 启动确认消息发送成功");
+ Serial.println("🚀 已启动持续发送模式");
+ } else {
+ Serial.println("❌ BLE未连接,无法发送确认消息");
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief 处理停止持续发送命令
+ * 处理来自BLE的停止持续发送请求
+ * @param doc JSON文档对象
+ * @return 是否处理成功
+ */
+bool processStopContinuousSend(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "stopContinuousSend") == 0) {
+ continuousSendEnabled = false;
+
+ Serial.println("🛑 停止持续发送模式");
+
+ if (deviceConnected) {
+ String confirmMsg = String("{\"type\":\"stopContinuousSendResult\",\"success\":true,\"message\":\"已停止持续发送模式\"}");
+
+ sendJSONDataToBLE(confirmMsg);
+ Serial.println("✅ 停止确认消息发送成功");
+ Serial.println("⏹️ 已停止持续发送模式");
+ } else {
+ Serial.println("❌ BLE未连接,无法发送确认消息");
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief 处理BLE配置数据
+ * 处理从BLE接收到的配置数据,解析JSON命令并执行相应操作
+ */
+void processBLEConfig() {
+ if (completeData.length() > 0 && (millis() - lastReceiveTime > 3000)) {
+ Serial.println("⏰ [超时] 数据接收超时3秒,自动当作接收完成");
+
+ if (receivedData.length() == 0) {
+ Serial.println("📥 [BLE] 超时后将completeData当作接收数据进行处理");
+ receivedData = completeData;
+ }
+
+ completeData = "";
+ }
+
+ if (receivedData.length() > 0) {
+ String bleData = receivedData;
+ receivedData = "";
+ bleData.trim();
+
+ Serial.printf("⚙️ [BLE] 准备解析JSON: %s\n", bleData.c_str());
+
+ if (bleData.startsWith("{") && bleData.endsWith("}")) {
+ JsonDocument doc;
+ DeserializationError error = deserializeJson(doc, bleData);
+
+ if (error) {
+ String errorMsg = String("❌ [BLE] JSON解析失败: ") + String(error.c_str());
+ Serial.println(errorMsg);
+ if (deviceConnected) {
+ String responseMsg = String("{\"type\":\"error\",\"message\":\"配置格式错误,请使用JSON格式\",\"originalData\":\"") + bleData + String("\"}");
+
+ sendJSONDataToBLE(responseMsg);
+ }
+ } else {
+ Serial.println("✅ [BLE] JSON解析成功");
+
+ bool processed = false;
+
+ if (!processed) processed = processSetDeviceId(doc);
+
+ if (!processed) processed = processWiFiConfigCommand(doc);
+
+ if (!processed) processed = processQueryStatus(doc);
+
+ if (!processed) processed = processQueryRadarData(doc);
+
+ if (!processed) processed = processStartContinuousSend(doc);
+
+ if (!processed) processed = processStopContinuousSend(doc);
+
+ if (!processed) processed = processScanWiFi(doc);
+
+ if (!processed) processed = processGetSavedNetworks(doc);
+
+ if (!processed) processed = processEchoRequest(doc);
+
+ if (!processed) {
+ Serial.println("❓[BLE] 未知命令");
+ if (deviceConnected) {
+ String responseMsg = String("{\"type\":\"error\",\"message\":\"未知命令\",\"receivedData\":\"") + bleData + String("\"}");
+
+ sendJSONDataToBLE(responseMsg);
+ }
+ }
+ }
+ } else {
+ Serial.println("📥 [BLE] 接收到非JSON数据");
+ }
+ }
+}
+
+/**
+ * @brief 处理设置设备ID命令
+ * 处理来自BLE的设置设备ID请求
+ * @param doc JSON文档对象
+ * @return 是否处理成功
+ */
+bool processSetDeviceId(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "setDeviceId") == 0) {
+ uint16_t newDeviceId = doc["newDeviceId"];
+
+ if (newDeviceId < 1000 || newDeviceId > 1999){
+ Serial.printf("[错误] 设备ID超出范围,有效范围: 1000-1999\n");
+ if (deviceConnected) {
+ String errorMsg = String("{\"type\":\"error\",\"message\":\"设备ID超出范围,有效范围: 1000-1999\"}");
+
+ sendJSONDataToBLE(errorMsg);
+ }
+ return true;
+ }
+
+ currentDeviceId = newDeviceId;
+
+ Serial.printf("[设备ID] 已设置新的设备ID: %u\n", currentDeviceId);
+
+ saveDeviceId();
+ Serial.printf("设备ID已保存到Flash: %u\n", currentDeviceId);
+
+ if (deviceConnected) {
+ String confirmMsg = String("{\"type\":\"setDeviceIdResult\",\"success\":true,\"message\":\"设备ID设置成功\",\"newDeviceId\":") +
+ String(newDeviceId) + "}";
+
+ sendJSONDataToBLE(confirmMsg);
+
+ sendStatusToBLE();
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief 发送设备状态到BLE
+ * 将当前设备状态信息通过BLE发送给客户端
+ */
+void sendStatusToBLE() {
+ if (deviceConnected) {
+ String statusMsg = String("{\"type\":\"status\",\"wifiConfigured\":") +
+ String(wifiManager.getSavedNetworkCount() > 0 ? "true" : "false") +
+ String(",\"wifiConnected\":") +
+ String(WiFi.status() == WL_CONNECTED ? "true" : "false") +
+ String(",\"ipAddress\":\"") +
+ (WiFi.status() == WL_CONNECTED ? WiFi.localIP().toString() : "") + "\"" +
+ String(",\"deviceId\":") +
+ String(currentDeviceId) + "}";
+
+ sendJSONDataToBLE(statusMsg);
+ Serial.println("已发送连接状态信息");
+ }
+}
+
+/**
+ * @brief 处理查询状态命令
+ * 处理来自BLE的查询设备状态请求
+ * @param doc JSON文档对象
+ * @return 是否处理成功
+ */
+bool processQueryStatus(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "queryStatus") == 0) {
+ if (deviceConnected) {
+ String statusMsg = String("{\"type\":\"deviceStatus\",\"success\":true,\"deviceId\":") +
+ String(currentDeviceId) +
+ String(",\"wifiConfigured\":") +
+ String(wifiManager.getSavedNetworkCount() > 0 ? "true" : "false") +
+ String(",\"wifiConnected\":") +
+ String(WiFi.status() == WL_CONNECTED ? "true" : "false") +
+ String(",\"ipAddress\":\"") +
+ (WiFi.status() == WL_CONNECTED ? WiFi.localIP().toString() : "") +
+ String("\"}");
+
+ sendJSONDataToBLE(statusMsg);
+ Serial.println("已发送设备状态信息");
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief 处理WiFi配置命令
+ * 处理来自BLE的WiFi配置请求
+ * @param doc JSON文档对象
+ * @return 是否处理成功
+ */
+bool processWiFiConfigCommand(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "setWiFiConfig") == 0) {
+ Serial.println("📱 [BLE-WiFi] 收到WiFi配置命令");
+ const char* newSSID = doc["ssid"];
+ const char* newPassword = doc["password"];
+
+ if (newSSID != nullptr && newPassword != nullptr) {
+ return wifiManager.handleConfigurationData(newSSID, newPassword);
+ } else {
+ Serial.println("❌ [BLE-WiFi] WiFi配置参数不完整");
+ if (deviceConnected) {
+ String errorMsg = String("{\"type\":\"error\",\"message\":\"WiFi配置参数不完整,需要ssid和password字段\"}");
+ sendJSONDataToBLE(errorMsg);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * @brief 处理扫描WiFi命令
+ * 处理来自BLE的WiFi扫描请求
+ * @param doc JSON文档对象
+ * @return 是否处理成功
+ */
+bool processScanWiFi(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "scanWiFi") == 0) {
+ Serial.println("📱 [BLE-WiFi] 收到WiFi扫描命令");
+ wifiManager.scanAndSendResults();
+ return true;
+ }
+ return false;
+}
+
+bool processGetSavedNetworks(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "getSavedNetworks") == 0) {
+ Serial.println("📱 [BLE-WiFi] 收到获取已保存WiFi网络命令");
+ wifiManager.getSavedNetworks();
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief 处理回显请求命令
+ * 处理来自BLE的回显测试请求
+ * @param doc JSON文档对象
+ * @return 是否处理成功
+ */
+bool processEchoRequest(JsonDocument& doc) {
+ const char* command = doc["command"];
+ if (command != nullptr && strcmp(command, "echo") == 0) {
+ Serial.println("📱 [BLE] 收到回显请求");
+
+ const char* echoContent = doc["content"];
+ if (echoContent != nullptr) {
+ String echoResponse = String("{\"type\":\"echoResponse\",\"originalContent\":\"") +
+ echoContent + String("\",\"receivedSuccessfully\":true}");
+
+ if (deviceConnected) {
+ sendJSONDataToBLE(echoResponse);
+ Serial.printf("📤 [BLE] 回显响应已发送: %s\n", echoContent);
+ }
+ } else {
+ String echoResponse = String("{\"type\":\"echoResponse\",\"receivedSuccessfully\":true,\"message\":\"Echo command received\"}");
+
+ if (deviceConnected) {
+ sendJSONDataToBLE(echoResponse);
+ Serial.println("📤 [BLE] 简单回显响应已发送");
+ }
+ }
+
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief 发送原始数据回显响应
+ * 将接收到的原始数据通过BLE回显给客户端
+ * @param rawData 原始数据字符串
+ */
+void sendRawEchoResponse(const String& rawData) {
+ if (deviceConnected) {
+ String echoResponse = String("{\"type\":\"rawEchoResponse\",\"originalData\":\"") +
+ rawData + String("\",\"received\":true}");
+
+ sendJSONDataToBLE(echoResponse);
+ Serial.printf("📤 [BLE] 原始数据回显已发送: %s\n", rawData.c_str());
+ }
+}
diff --git a/src/radar_manager.h b/src/radar_manager.h
new file mode 100644
index 0000000..cb30051
--- /dev/null
+++ b/src/radar_manager.h
@@ -0,0 +1,235 @@
+#ifndef RADAR_MANAGER_H
+#define RADAR_MANAGER_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+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
diff --git a/src/wifi_manager.cpp b/src/wifi_manager.cpp
new file mode 100644
index 0000000..4e12bbc
--- /dev/null
+++ b/src/wifi_manager.cpp
@@ -0,0 +1,572 @@
+#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;
+}
+
+/**
+ * @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);
+ String configStr = preferences.getString(key.c_str(), "");
+
+ if (configStr.length() > 0) {
+ JsonDocument doc;
+ DeserializationError error = deserializeJson(doc, configStr);
+
+ if (!error) {
+ const char* ssid = doc["ssid"];
+ const char* password = doc["password"];
+
+ if (ssid && password) {
+ strncpy(savedNetworks[savedNetworkCount].ssid, ssid, 31);
+ 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++) {
+ 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);
+
+ String key = "wifi_" + String(i);
+ preferences.putString(key.c_str(), configStr);
+
+ Serial.printf("🔄 更新WiFi配置: %s\n", ssid);
+ return true;
+ }
+ }
+
+ // 添加新配置
+ if (savedNetworkCount < MAX_WIFI_NETWORKS) {
+ strncpy(savedNetworks[savedNetworkCount].ssid, ssid, 31);
+ 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);
+ 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);
+
+ return true;
+ } else {
+ Serial.println("❌ [WiFi] 连接超时");
+ currentState = WIFI_DISCONNECTED;
+ setNetworkStatus(NET_DISCONNECTED);
+ return false;
+ }
+}
+
+/**
+ * @brief 扫描并匹配WiFi网络
+ * 扫描附近的WiFi网络,并尝试匹配已保存的配置
+ * 如果找到多个匹配的网络,优先连接信号强度最强的那一组
+ * @return 是否成功连接到匹配的网络
+ */
+bool WiFiManager::scanAndMatchNetworks() {
+ Serial.println("🔍 [WiFi] 开始扫描WiFi网络...");
+ currentState = WIFI_SCANNING;
+
+ WiFi.disconnect(true);
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ WiFi.mode(WIFI_STA);
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+
+ int n = WiFi.scanNetworks();
+ Serial.printf("🔍 扫描到 %d 个WiFi网络\n", n);
+
+ if (n <= 0) {
+ Serial.println("❌ 未扫描到任何WiFi网络或扫描失败");
+ currentState = WIFI_DISCONNECTED;
+ return false;
+ }
+
+ // 收集所有匹配的、信号强度符合要求的网络
+ struct {
+ const char* ssid;
+ const char* password;
+ int rssi;
+ } bestNetwork = {nullptr, nullptr, -1000}; // 初始化为非常弱的信号
+
+ // 遍历已保存的网络,寻找匹配的网络
+ 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 (rssi > bestNetwork.rssi) {
+ bestNetwork.ssid = savedNetworks[i].ssid;
+ bestNetwork.password = savedNetworks[i].password;
+ bestNetwork.rssi = rssi;
+ Serial.printf("📈 更新最佳网络: %s, 信号: %d dBm\n",
+ bestNetwork.ssid, bestNetwork.rssi);
+ }
+ } else {
+ Serial.printf("⚠️ 信号强度过低,跳过\n");
+ }
+ }
+ }
+ }
+
+ // 如果找到最佳网络,尝试连接
+ if (bestNetwork.ssid != nullptr) {
+ Serial.printf("✅ 选择信号最强的网络: %s, 信号: %d dBm\n",
+ bestNetwork.ssid, bestNetwork.rssi);
+ if (connectToNetwork(bestNetwork.ssid, bestNetwork.password)) {
+ return true;
+ }
+ }
+
+ Serial.println("❌ 未找到匹配的WiFi网络或信号过弱");
+ currentState = WIFI_DISCONNECTED;
+ 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() {
+ Serial.println("📱 [BLE-WiFi] 开始WiFi扫描...");
+
+ WiFi.disconnect(true);
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ WiFi.mode(WIFI_STA);
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+
+ int n = WiFi.scanNetworks();
+ Serial.printf("🔍 扫描到 %d 个WiFi网络\n", n);
+
+ if (n <= 0) {
+ Serial.println("❌ 未扫描到任何WiFi网络或扫描失败");
+ 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);
+
+ 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;
+ }
+
+ // 先扫描WiFi网络,检查是否存在匹配的网络
+ Serial.println("🔍 [WiFi] 扫描WiFi网络,检查是否存在匹配的网络...");
+ int n = WiFi.scanNetworks();
+ Serial.printf("🔍 扫描到 %d 个WiFi网络\n", n);
+
+ if (n == 0) {
+ Serial.println("❌ 未扫描到任何WiFi网络");
+
+ 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网络");
+ }
+
+ if (deviceConnected) {
+ sendJSONDataToBLE(errorMsg);
+ }
+ return false;
+ }
+
+ // 尝试连接到指定网络
+ 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配置失败");
+
+ 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连接断开");
+ }
+
+ // 处理重连逻辑
+ 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();
+}
\ No newline at end of file
diff --git a/src/wifi_manager.h b/src/wifi_manager.h
new file mode 100644
index 0000000..bd95c64
--- /dev/null
+++ b/src/wifi_manager.h
@@ -0,0 +1,121 @@
+#ifndef WIFI_MANAGER_H
+#define WIFI_MANAGER_H
+
+#include
+#include
+#include
+#include
+
+/**
+ * @brief 最大WiFi网络配置数量
+ * 定义设备可以保存的WiFi网络配置的最大数量
+ */
+#define MAX_WIFI_NETWORKS 10
+
+/**
+ * @brief 最小信号强度阈值
+ * 定义WiFi网络信号强度的最低要求,低于此值的网络会被过滤
+ * 单位:dBm
+ */
+#define MIN_RSSI_THRESHOLD -200
+
+/**
+ * @brief WiFi连接超时时间
+ * 定义WiFi连接的最大等待时间,超过此时间认为连接失败
+ * 单位:毫秒
+ */
+#define WIFI_CONNECT_TIMEOUT 15000
+
+/**
+ * @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 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
\ No newline at end of file