更新雷达系统代码,添加WiFi重连机制和雷达数据处理功能

This commit is contained in:
userName
2026-03-03 17:50:50 +08:00
parent 5e2dfc8ac8
commit 8d7b7df1fa
10 changed files with 6183 additions and 2701 deletions
+44 -1
View File
@@ -3,7 +3,50 @@
"cmath": "cpp",
"array": "cpp",
"string": "cpp",
"string_view": "cpp"
"string_view": "cpp",
"new": "cpp",
"atomic": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"map": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"limits": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp"
},
"C_Cpp.errorSquiggles": "disabled"
}
+504 -1201
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+235
View File
@@ -0,0 +1,235 @@
#ifndef RADAR_MANAGER_H
#define RADAR_MANAGER_H
#include <Arduino.h>
#include <ArduinoJson.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <HTTPClient.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include <Preferences.h>
class WiFiManager;
#define SERVICE_UUID "a8c1e5c0-3d5d-4a9d-8d5e-7c8b6a4e2f1a" // BLE服务UUID
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" // BLE特征值UUID
#define UART_RX_BUFFER_SIZE 4096 // UART接收缓冲区大小
#define QUEUE_SIZE 50 // 队列大小
#define TASK_STACK_SIZE 8192 // 任务堆栈大小
#define FRAME_HEADER1 0x53 // 帧头字节1
#define FRAME_HEADER2 0x59 // 帧头字节2
#define FRAME_TAIL1 0x54 // 帧尾字节1
#define FRAME_TAIL2 0x43 // 帧尾字节2
// 控制字定义
#define CTRL_PRESENCE 0x80 // 人体存在检测
#define CTRL_BREATH 0x81 // 呼吸检测
#define CTRL_SLEEP 0x84 // 睡眠监测
#define CTRL_HEARTRATE 0x85 // 心率监测
// 命令字定义
#define CMD_REPORT 0x80 // 主动上报
#define CMD_QUERY 0x81 // 查询命令
#define CMD_SET 0x82 // 设置命令
// 定义R60ABD1数据结构
typedef struct {
uint8_t present; // 有人/无人状态 (DP1)
uint16_t distance; // 人体距离 (DP3) 单位cm
uint8_t heartRate; // 心率 (DP6) 单位BPM
uint8_t breathRate; // 呼吸率 (DP8) 单位次/分钟
uint8_t heartWave; // 心率波形 (DP7) 数值+128
uint8_t breathWave; // 呼吸波形 (DP10) 数值+128
uint8_t sleepState; // 睡眠状态 (DP12)
uint32_t sleepTime; // 睡眠时长 (DP13) 单位秒
uint8_t sleepScore; // 睡眠质量评分 (DP14)
uint8_t bedEntry; // 入床/离床状态 (DP11)
uint8_t abnormal; // 异常状态 (DP18)
} R60ABD1Data; // R60ABD1雷达数据结构体
typedef struct { // 传感器数据结构体
float breath_rate; // 呼吸率
float heart_rate; // 心率
uint8_t breath_valid; // 呼吸率有效标志
uint8_t heart_valid; // 心率有效标志
uint8_t presence; // 存在状态
uint8_t motion; // 运动状态
int heartbeat_waveform; // 心跳波形
int breathing_waveform; // 呼吸波形
uint16_t distance; // 距离
uint8_t body_movement; // 身体运动
uint8_t breath_status; // 呼吸状态
uint8_t sleep_state; // 睡眠状态
uint32_t sleep_time; // 睡眠时长
uint8_t sleep_score; // 睡眠评分
uint8_t sleep_grade; // 睡眠等级
uint8_t bed_entry; // 入床状态
uint8_t abnormal_state; // 异常状态
uint8_t avg_heart_rate; // 平均心率
uint8_t avg_breath_rate; // 平均呼吸率
uint8_t turn_count; // 翻身次数
uint8_t large_move_ratio; // 大幅运动比例
uint8_t small_move_ratio; // 小幅运动比例
int16_t pos_x; // X坐标
int16_t pos_y; // Y坐标
int16_t pos_z; // Z坐标
int8_t breath_waveform[5]; // 呼吸波形数组
int8_t heart_waveform[5]; // 心跳波形数组
uint16_t deep_sleep_time; // 深度睡眠时长
uint16_t light_sleep_time; // 浅度睡眠时长
uint16_t awake_time; // 清醒时长
uint16_t sleep_total_time; // 总睡眠时长
uint8_t deep_sleep_ratio; // 深度睡眠比例
uint8_t light_sleep_ratio; // 浅度睡眠比例
uint8_t awake_ratio; // 清醒比例
uint8_t turnover_count; // 翻身计数
uint8_t struggle_alert; // 挣扎警报
uint8_t no_one_alert; // 无人警报
uint8_t bed_status; // 床状态
uint8_t bed_Out_Time; // 离床时间
uint8_t apnea_count; // 呼吸暂停次数
} SensorData; // 传感器数据结构体
typedef struct { // 相位数据结构体
int heartbeat_waveform; // 心跳波形
int breathing_waveform; // 呼吸波形
} PhaseData; // 相位数据结构体
typedef struct { // 生命体征数据结构体
float heart_rate; // 心率
float breath_rate; // 呼吸率
uint8_t presence; // 存在状态
uint8_t motion; // 运动状态
uint16_t distance; // 距离
uint8_t sleep_state; // 睡眠状态
uint8_t sleep_score; // 睡眠评分
uint8_t body_movement; // 身体运动
uint8_t breath_status; // 呼吸状态
uint32_t sleep_time; // 睡眠时长
uint8_t bed_entry; // 入床状态
uint8_t abnormal_state; // 异常状态
uint8_t avg_heart_rate; // 平均心率
uint8_t avg_breath_rate; // 平均呼吸率
uint8_t turn_count; // 翻身次数
uint8_t large_move_ratio; // 大幅运动比例
uint8_t small_move_ratio; // 小幅运动比例
int16_t pos_x; // X坐标
int16_t pos_y; // Y坐标
int16_t pos_z; // Z坐标
uint16_t deep_sleep_time; // 深度睡眠时长
uint16_t light_sleep_time; // 浅度睡眠时长
uint16_t awake_time; // 清醒时长
uint16_t sleep_total_time; // 总睡眠时长
uint8_t deep_sleep_ratio; // 深度睡眠比例
uint8_t light_sleep_ratio; // 浅度睡眠比例
uint8_t awake_ratio; // 清醒比例
uint8_t turnover_count; // 翻身计数
uint8_t struggle_alert; // 挣扎警报
uint8_t no_one_alert; // 无人警报
uint8_t bed_status; // 床状态
uint8_t apnea_count; // 呼吸暂停次数
int heartbeat_waveform; // 心跳波形
int breathing_waveform; // 呼吸波形
} VitalData; // 生命体征数据结构体
typedef struct { // 上次发送数据结构体
float heart_rate; // 心率
float breath_rate; // 呼吸率
uint8_t presence; // 存在状态
uint8_t motion; // 运动状态
uint8_t sleep_state; // 睡眠状态
} LastSentData; // 上次发送数据结构体
class BLEFlowController { // BLE流控制器类
private:
size_t maxBytesPerSecond; // 最大每秒发送字节数
size_t bytesSent; // 已发送字节数
unsigned long lastResetTime; // 上次重置时间
unsigned long lastSendTime; // 上次发送时间
public:
BLEFlowController(size_t maxBps);
bool canSend(size_t dataSize);
bool check();
void recordSend(size_t dataSize);
void reset();
};
extern SensorData sensorData; // 传感器数据
extern HardwareSerial mySerial1; // 硬件串口1
extern QueueHandle_t phaseDataQueue; // 相位数据队列
extern QueueHandle_t vitalDataQueue; // 生命体征数据队列
extern QueueHandle_t uartQueue; // UART数据队列
extern TaskHandle_t bleSendTaskHandle; // BLE发送任务句柄
extern TaskHandle_t vitalSendTaskHandle; // 生命体征发送任务句柄
extern TaskHandle_t uartProcessTaskHandle; // UART处理任务句柄
extern BLEServer* pServer; // BLE服务器指针
extern BLECharacteristic* pCharacteristic; // BLE特征值指针
extern bool deviceConnected; // 设备连接状态
extern bool oldDeviceConnected; // 旧设备连接状态
extern String receivedData; // 接收到的数据
extern String completeData; // 完整数据
extern unsigned long lastReceiveTime; // 上次接收数据时间
extern bool continuousSendEnabled; // 持续发送使能标志
extern unsigned long continuousSendInterval; // 持续发送间隔
extern BLEFlowController bleFlow; // BLE流控制器
extern unsigned long lastSensorUpdate; // 上次传感器更新时间
extern LastSentData lastSentData; // 上次发送的数据
extern unsigned long lastCheckTime; // 上次检测时间
extern uint16_t currentDeviceId; // 当前设备ID
extern Preferences preferences; // Flash存储对象
extern WiFiManager wifiManager; // WiFi管理器
void initRadarManager();
void initR60ABD1();
bool parseR60ABD1Frame(uint8_t *frame, uint16_t frameLen);
int16_t parseSignedCoordinate(uint16_t raw_value);
void sendRadarCommand(uint8_t ctrl, uint8_t cmd, uint8_t value);
void IRAM_ATTR serialRxCallback();
void bleSendTask(void *parameter);
void vitalSendTask(void *parameter);
void radarDataTask(void *parameter);
void uartProcessTask(void *parameter);
void sendDataInChunks(const String& data);
void sendJSONDataToBLE(const String& jsonData);
bool sendCustomJSONData(const String& jsonType, const String& jsonString);
void sendRadarDataToBLE();
bool processQueryRadarData(JsonDocument& doc);
bool processStartContinuousSend(JsonDocument& doc);
bool processStopContinuousSend(JsonDocument& doc);
void processBLEConfig();
bool processSetDeviceId(JsonDocument& doc);
bool processQueryStatus(JsonDocument& doc);
bool processWiFiConfigCommand(JsonDocument& doc);
bool processScanWiFi(JsonDocument& doc);
bool processGetSavedNetworks(JsonDocument& doc);
bool processEchoRequest(JsonDocument& doc);
void sendRawEchoResponse(const String& rawData);
void sendStatusToBLE();
void loadDeviceId();
void saveDeviceId();
void sendDailyDataToInfluxDB(String dailyDataLine);
void sendSleepDataToInfluxDB();
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer);
void onDisconnect(BLEServer* pServer);
};
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic);
};
#endif
+567
View File
@@ -0,0 +1,567 @@
#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扫描...");
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();
}
+121
View File
@@ -0,0 +1,121 @@
#ifndef WIFI_MANAGER_H
#define WIFI_MANAGER_H
#include <Arduino.h>
#include <WiFi.h>
#include <Preferences.h>
#include <ArduinoJson.h>
/**
* @brief WiFi网络配置数量
* WiFi网络配置的最大数量
*/
#define MAX_WIFI_NETWORKS 10
/**
* @brief
* WiFi网络信号强度的最低要求
* dBm
*/
#define MIN_RSSI_THRESHOLD -200
/**
* @brief WiFi连接超时时间
* WiFi连接的最大等待时间
*
*/
#define WIFI_CONNECT_TIMEOUT 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
+330
View File
@@ -0,0 +1,330 @@
import time
import random
import requests
from datetime import datetime
# 添加InfluxDB配置
INFLUXDB_URL = 'http://8.134.11.76:8086'
INFLUXDB_TOKEN = 'KuTa5ZsqoHIhi2IglOO06zExUYw1_mJ6K0mcA9X1y6O6CJDog3_Cgr8mUw1SwpuCCKRElqxa6wAhrrhsYPytkg=='
INFLUXDB_ORG = 'gzlg'
INFLUXDB_BUCKET = 'gzlg'
# 配置设备ID:设置为0则自动注册,设置为1000~1999则固定使用该ID
CONFIG_DEVICE_ID = 1003
# 分配的设备ID(初始为None,注册后更新)
assigned_device_id = None
# 相位计数器
phase_counter = 0
def get_next_device_id():
"""从InfluxDB查询已注册的设备ID,并返回下一个可用的ID"""
try:
# 查询InfluxDB中已存在的设备ID
query = '''
from(bucket: "{}")
|> range(start: -365d)
|> filter(fn: (r) => r["_measurement"] == "device_data")
|> keep(columns: ["deviceId"])
|> distinct(column: "deviceId")
|> sort()
'''.format(INFLUXDB_BUCKET)
url = f"{INFLUXDB_URL}/api/v2/query?org={INFLUXDB_ORG}"
headers = {
"Authorization": f"Token {INFLUXDB_TOKEN}",
"Content-Type": "application/json"
}
data = {
"query": query
}
response = requests.post(url, headers=headers, json=data)
print(f"🔍 InfluxDB查询响应状态: {response.status_code}")
if response.status_code == 200:
print(f"🔍 InfluxDB查询响应内容: {response.text}")
# 解析响应数据
device_ids = []
lines = response.text.strip().split('\n')
for line in lines:
if line and not line.startswith('#') and 'deviceId' not in line:
try:
# 解析CSV格式的响应数据
# 格式类似于: ,_result,0,1001,1001
parts = line.split(',')
if len(parts) >= 5:
# deviceId在第4个位置(索引3
device_id_str = parts[3].strip()
if device_id_str:
device_id = int(device_id_str)
if 1000 <= device_id <= 1999: # 只考虑1000-1999范围内的设备ID
device_ids.append(device_id)
print(f"📱 发现设备ID: {device_id}")
except (ValueError, IndexError) as e:
# 忽略解析错误的行
print(f"⚠️ 忽略无法解析的行: {line}")
continue
# 对设备ID进行排序
device_ids.sort()
# 找到下一个可用的ID
next_id = 1001
for device_id in device_ids:
if device_id == next_id:
next_id += 1
elif device_id > next_id:
break
# 确保ID在有效范围内
if next_id > 1999:
next_id = 1001 # 如果超出范围,从头开始
print(f"📊 查询到已注册设备: {device_ids}")
print(f"🆕 分配新设备ID: {next_id}")
return next_id
else:
print(f"❌ 查询InfluxDB失败: {response.status_code} - {response.text}")
# 如果查询失败,返回默认ID
return 1001
except Exception as e:
print(f"❌ 查询设备ID时出错: {e}")
import traceback
traceback.print_exc()
# 如果查询失败,返回默认ID
return 1001
def register_device():
"""注册设备并获取设备ID"""
global assigned_device_id
# 检查配置的设备ID
if CONFIG_DEVICE_ID == 0:
# 自动注册模式
try:
# 获取下一个可用的设备ID
next_device_id = get_next_device_id()
assigned_device_id = next_device_id
print(f"✅ 设备注册成功! 设备ID: {assigned_device_id} (0x{assigned_device_id:04X})")
return True
except Exception as e:
print(f"❌ 注册过程中发生错误: {e}")
return False
elif 1000 <= CONFIG_DEVICE_ID <= 1999:
# 固定设备ID模式
assigned_device_id = CONFIG_DEVICE_ID
print(f"✅ 使用固定设备ID: {assigned_device_id} (0x{assigned_device_id:04X})")
return True
else:
# 配置的设备ID无效
print(f"❌ 配置的设备ID {CONFIG_DEVICE_ID} 无效,请设置为0(自动注册)或1000~1999之间的值")
return False
def save_data_to_influxdb(protocol_id, data_value):
"""保存日常数据到InfluxDB"""
try:
# 根据协议ID确定字段名
field_mapping = {
1: "heartRate",
2: "breathingRate",
13: "personDetected",
14: "humanActivity",
15: "humanDistance", # 人体距离 (cm)
16: "humanPosition", # 人体方位 (cm)
17: "sleepState" # 睡眠状态
}
if protocol_id in field_mapping:
field_name = field_mapping[protocol_id]
# 创建数据点 - 使用 "daily_data" 作为测量值名称
data_point = {
"measurement": "daily_data", # 改为 daily_data 以区分日常数据
"tags": {
"deviceId": assigned_device_id,
"dataType": "daily" # 标识这是日常数据
},
"time": datetime.utcnow().isoformat() + "Z",
"fields": {}
}
# 对特定字段进行数值处理
if protocol_id in [1, 2]: # 心率和呼吸频率需要除以10
data_point["fields"][field_name] = float(data_value) / 10.0
elif protocol_id in [13, 14]: # 人检/活动数据,确保是整数0或1
# 强制转换为整数,确保只有0或1
data_point["fields"][field_name] = int(data_value)
if data_point["fields"][field_name] not in [0, 1]:
print(f"⚠️ 警告: 人检/活动数据值异常: {data_value}, 强制转换为: {data_point['fields'][field_name]}")
elif protocol_id == 15: # 人体距离,范围0-65535
data_point["fields"][field_name] = int(data_value)
elif protocol_id == 16: # 人体方位,可以是正负值
data_point["fields"][field_name] = int(data_value)
elif protocol_id == 17: # 睡眠状态,使用预定义值
data_point["fields"][field_name] = int(data_value)
else:
data_point["fields"][field_name] = data_value
# 发送数据到InfluxDB
url = f"{INFLUXDB_URL}/api/v2/write?org={INFLUXDB_ORG}&bucket={INFLUXDB_BUCKET}"
headers = {
"Authorization": f"Token {INFLUXDB_TOKEN}",
"Content-Type": "text/plain; charset=utf-8"
}
# 构造行协议格式的数据,明确指定数据类型
if protocol_id in [13, 14, 15, 16, 17]:
# 对于人检/活动/距离/位置/睡眠状态数据,使用整数格式
line_protocol = f"daily_data,deviceId={assigned_device_id},dataType=daily {field_name}={int(data_point['fields'][field_name])}i"
else:
# 对于其他数据,使用浮点数格式
line_protocol = f"daily_data,deviceId={assigned_device_id},dataType=daily {field_name}={data_point['fields'][field_name]}"
response = requests.post(url, headers=headers, data=line_protocol)
if response.status_code == 204:
print(f"✅ 日常数据已保存到InfluxDB设备{assigned_device_id}上: {field_name}={data_point['fields'][field_name]}")
else:
print(f"❌ 保存日常数据到InfluxDB失败: {response.status_code} - {response.text}")
else:
print(f"⚠️ 未知的协议ID: {protocol_id}")
except Exception as e:
print(f"❌ 保存日常数据到InfluxDB时出错: {e}")
import traceback
traceback.print_exc()
def save_sleep_data_to_influxdb(sleep_data):
"""保存睡眠数据到InfluxDB"""
try:
# 构造睡眠数据的行协议格式 - 使用 "sleep_data" 作为测量值名称
line_protocol = f"sleep_data,deviceId={assigned_device_id},dataType=sleep "
# 按顺序添加各个字段
fields = []
fields.append(f"sleepQualityScore={int(sleep_data['sleepQualityScore'])}i") # 1B 睡眠质量评分 (0~100)
fields.append(f"totalSleepDuration={int(sleep_data['totalSleepDuration'])}i") # 2B 睡眠总时长 (0~65535 分钟)
fields.append(f"awakeDurationRatio={int(sleep_data['awakeDurationRatio'])}i") # 1B 清醒时长占比 (0~100)
fields.append(f"lightSleepRatio={int(sleep_data['lightSleepRatio'])}i") # 1B 浅睡时长占比 (0~100)
fields.append(f"deepSleepRatio={int(sleep_data['deepSleepRatio'])}i") # 1B 深睡时长占比 (0~100)
fields.append(f"outOfBedDuration={int(sleep_data['outOfBedDuration'])}i") # 1B 离床时长 (0~255)
fields.append(f"outOfBedCount={int(sleep_data['outOfBedCount'])}i") # 1B 离床次数 (0~255)
fields.append(f"turnCount={int(sleep_data['turnCount'])}i") # 1B 翻身次数 (0~255)
fields.append(f"avgBreathingRate={int(sleep_data['avgBreathingRate'])}i") # 1B 平均呼吸 (0~25)
fields.append(f"avgHeartRate={int(sleep_data['avgHeartRate'])}i") # 1B 平均心跳 (0~100)
fields.append(f"apneaCount={int(sleep_data['apneaCount'])}i") # 1B 呼吸暂停次数 (0~10)
line_protocol += ",".join(fields)
# 发送数据到InfluxDB
url = f"{INFLUXDB_URL}/api/v2/write?org={INFLUXDB_ORG}&bucket={INFLUXDB_BUCKET}"
headers = {
"Authorization": f"Token {INFLUXDB_TOKEN}",
"Content-Type": "text/plain; charset=utf-8"
}
response = requests.post(url, headers=headers, data=line_protocol)
if response.status_code == 204:
print(f"✅ 睡眠数据已保存到InfluxDB设备{assigned_device_id}")
else:
print(f"❌ 保存睡眠数据到InfluxDB失败: {response.status_code} - {response.text}")
except Exception as e:
print(f"❌ 保存睡眠数据到InfluxDB时出错: {e}")
import traceback
traceback.print_exc()
def generate_random_sleep_data():
"""生成随机睡眠数据用于测试"""
sleep_data = {
"sleepQualityScore": random.randint(0, 100), # 1B 睡眠质量评分 (0~100)
"totalSleepDuration": random.randint(0, 65535), # 2B 睡眠总时长 (0~65535 分钟)
"awakeDurationRatio": random.randint(0, 100), # 1B 清醒时长占比 (0~100)
"lightSleepRatio": random.randint(0, 100), # 1B 浅睡时长占比 (0~100)
"deepSleepRatio": random.randint(0, 100), # 1B 深睡时长占比 (0~100)
"outOfBedDuration": random.randint(0, 255), # 1B 离床时长 (0~255)
"outOfBedCount": random.randint(0, 255), # 1B 离床次数 (0~255)
"turnCount": random.randint(0, 255), # 1B 翻身次数 (0~255)
"avgBreathingRate": random.randint(0, 25), # 1B 平均呼吸 (0~25)
"avgHeartRate": random.randint(0, 100), # 1B 平均心跳 (0~100)
"apneaCount": random.randint(0, 10), # 1B 呼吸暂停次数 (0~10)
}
# 确保比例字段总和为100
total_ratio = sleep_data["awakeDurationRatio"] + sleep_data["lightSleepRatio"] + sleep_data["deepSleepRatio"]
if total_ratio != 100:
# 调整浅睡时长占比以确保总和为100
adjustment = 100 - total_ratio
sleep_data["lightSleepRatio"] = max(0, min(100, sleep_data["lightSleepRatio"] + adjustment))
# 再次检查总和
total_ratio = sleep_data["awakeDurationRatio"] + sleep_data["lightSleepRatio"] + sleep_data["deepSleepRatio"]
if total_ratio != 100:
# 如果仍然不是100,则最后一次调整浅睡占比
sleep_data["lightSleepRatio"] += (100 - total_ratio)
sleep_data["lightSleepRatio"] = max(0, min(100, sleep_data["lightSleepRatio"]))
return sleep_data
def main():
global assigned_device_id
try:
# 第一步:注册设备获取设备ID
if not register_device():
print("❌ 设备注册失败,程序退出")
return
print(f"🎯 开始使用设备ID {assigned_device_id} 发送数据...")
# 启动时立即发送一次睡眠数据
print("⏰ 启动时生成并发送睡眠数据...")
initial_sleep_data = generate_random_sleep_data()
save_sleep_data_to_influxdb(initial_sleep_data)
# 记录上次发送睡眠数据的时间
last_sleep_data_time = time.time()
# 第二步:开始发送数据
while True:
# 初始化数据值
data_value = 0
# 发送其他数据
protocol_id = random.choice([1, 2, 13, 14, 15, 16, 17]) # 1=心跳, 2=呼吸, 13=检测到人, 14=人体活动, 15=人体距离, 16=人体方位, 17=睡眠状态
if protocol_id == 1: # 心跳
data_value = random.randint(600, 1000)
elif protocol_id == 2: # 呼吸
data_value = random.randint(120, 200)
elif protocol_id == 13: # 检测到人(1检测到,0未检测到)
data_value = random.choice([1])
elif protocol_id == 14: # 人体活动(1活动,0静止)
data_value = random.choice([0])
elif protocol_id == 15: # 人体距离 (cm),范围0-65535
data_value = random.randint(0, 65535)
elif protocol_id == 16: # 人体方位 (cm),可以有正负值
data_value = random.randint(-32768, 32767)
elif protocol_id == 17: # 睡眠状态 (0x00=深睡, 0x01=浅睡, 0x02=清醒, 0x03=无)
data_value = random.choice([0, 1, 2, 3])
# 直接发送数据到InfluxDB
save_data_to_influxdb(protocol_id, data_value)
# 每隔一段时间(例如30分钟)发送一次睡眠数据
current_time = time.time()
if current_time - last_sleep_data_time >= 1800: # 30分钟 = 1800秒
print("⏰ 生成并发送睡眠数据...")
sleep_data = generate_random_sleep_data()
save_sleep_data_to_influxdb(sleep_data)
last_sleep_data_time = current_time
# 设置发送间隔
time.sleep(0.4) # 每0.4秒发送一次数据
except KeyboardInterrupt:
print(f"\n🛑 设备 {assigned_device_id} 发送端已停止")
except Exception as e:
print(f"❌ 发生错误: {e}")
if __name__ == "__main__":
main()
-243
View File
@@ -1,243 +0,0 @@
import time
import random
import requests
from datetime import datetime
# 添加InfluxDB配置
INFLUXDB_URL = 'http://8.134.11.76:8086'
INFLUXDB_TOKEN = 'KuTa5ZsqoHIhi2IglOO06zExUYw1_mJ6K0mcA9X1y6O6CJDog3_Cgr8mUw1SwpuCCKRElqxa6wAhrrhsYPytkg=='
INFLUXDB_ORG = 'gzlg'
INFLUXDB_BUCKET = 'gzlg'
# 配置设备ID:设置为0则自动注册,设置为1000~1999则固定使用该ID
CONFIG_DEVICE_ID = 1002
# 分配的设备ID(初始为None,注册后更新)
assigned_device_id = None
# 相位计数器
phase_counter = 0
def get_next_device_id():
"""从InfluxDB查询已注册的设备ID,并返回下一个可用的ID"""
try:
# 查询InfluxDB中已存在的设备ID
query = '''
from(bucket: "{}")
|> range(start: -365d)
|> filter(fn: (r) => r["_measurement"] == "device_data")
|> keep(columns: ["deviceId"])
|> distinct(column: "deviceId")
|> sort()
'''.format(INFLUXDB_BUCKET)
url = f"{INFLUXDB_URL}/api/v2/query?org={INFLUXDB_ORG}"
headers = {
"Authorization": f"Token {INFLUXDB_TOKEN}",
"Content-Type": "application/json"
}
data = {
"query": query
}
response = requests.post(url, headers=headers, json=data)
print(f"🔍 InfluxDB查询响应状态: {response.status_code}")
if response.status_code == 200:
print(f"🔍 InfluxDB查询响应内容: {response.text}")
# 解析响应数据
device_ids = []
lines = response.text.strip().split('\n')
for line in lines:
if line and not line.startswith('#') and 'deviceId' not in line:
try:
# 解析CSV格式的响应数据
# 格式类似于: ,_result,0,1001,1001
parts = line.split(',')
if len(parts) >= 5:
# deviceId在第4个位置(索引3
device_id_str = parts[3].strip()
if device_id_str:
device_id = int(device_id_str)
if 1000 <= device_id <= 1999: # 只考虑1000-1999范围内的设备ID
device_ids.append(device_id)
print(f"📱 发现设备ID: {device_id}")
except (ValueError, IndexError) as e:
# 忽略解析错误的行
print(f"⚠️ 忽略无法解析的行: {line}")
continue
# 对设备ID进行排序
device_ids.sort()
# 找到下一个可用的ID
next_id = 1001
for device_id in device_ids:
if device_id == next_id:
next_id += 1
elif device_id > next_id:
break
# 确保ID在有效范围内
if next_id > 1999:
next_id = 1001 # 如果超出范围,从头开始
print(f"📊 查询到已注册设备: {device_ids}")
print(f"🆕 分配新设备ID: {next_id}")
return next_id
else:
print(f"❌ 查询InfluxDB失败: {response.status_code} - {response.text}")
# 如果查询失败,返回默认ID
return 1001
except Exception as e:
print(f"❌ 查询设备ID时出错: {e}")
import traceback
traceback.print_exc()
# 如果查询失败,返回默认ID
return 1001
def register_device():
"""注册设备并获取设备ID"""
global assigned_device_id
# 检查配置的设备ID
if CONFIG_DEVICE_ID == 0:
# 自动注册模式
try:
# 获取下一个可用的设备ID
next_device_id = get_next_device_id()
assigned_device_id = next_device_id
print(f"✅ 设备注册成功! 设备ID: {assigned_device_id} (0x{assigned_device_id:04X})")
return True
except Exception as e:
print(f"❌ 注册过程中发生错误: {e}")
return False
elif 1000 <= CONFIG_DEVICE_ID <= 1999:
# 固定设备ID模式
assigned_device_id = CONFIG_DEVICE_ID
print(f"✅ 使用固定设备ID: {assigned_device_id} (0x{assigned_device_id:04X})")
return True
else:
# 配置的设备ID无效
print(f"❌ 配置的设备ID {CONFIG_DEVICE_ID} 无效,请设置为0(自动注册)或1000~1999之间的值")
return False
def save_data_to_influxdb(protocol_id, data_value):
"""保存数据到InfluxDB"""
try:
# 创建数据点
data_point = {
"measurement": "device_data",
"tags": {
"deviceId": assigned_device_id
},
"time": datetime.utcnow().isoformat() + "Z",
"fields": {}
}
# 根据协议ID确定字段名
field_mapping = {
1: "heartRate",
2: "breathingRate",
3: "heartPhase",
4: "breathingPhase",
13: "personDetected",
14: "humanActivity"
}
if protocol_id in field_mapping:
field_name = field_mapping[protocol_id]
# 对特定字段进行数值处理
if protocol_id in [1, 2]: # 心率和呼吸频率需要除以10
data_point["fields"][field_name] = float(data_value) / 10.0
elif protocol_id in [3, 4]: # 相位数据需要除以1000
data_point["fields"][field_name] = float(data_value) / 1000.0
elif protocol_id in [13, 14]: # 人检/活动数据,确保是整数0或1
# 强制转换为整数,确保只有0或1
data_point["fields"][field_name] = int(data_value)
if data_point["fields"][field_name] not in [0, 1]:
print(f"⚠️ 警告: 人检/活动数据值异常: {data_value}, 强制转换为: {data_point['fields'][field_name]}")
else:
data_point["fields"][field_name] = data_value
print(f"📊 准备保存数据: 协议ID={protocol_id}, 字段={field_name}, 值={data_point['fields'][field_name]}, 原始值={data_value}")
# 发送数据到InfluxDB
url = f"{INFLUXDB_URL}/api/v2/write?org={INFLUXDB_ORG}&bucket={INFLUXDB_BUCKET}"
headers = {
"Authorization": f"Token {INFLUXDB_TOKEN}",
"Content-Type": "text/plain; charset=utf-8"
}
# 构造行协议格式的数据,明确指定数据类型
if protocol_id in [13, 14]:
# 对于人检/活动数据,使用整数格式
line_protocol = f"device_data,deviceId={assigned_device_id} {field_name}={int(data_point['fields'][field_name])}i"
else:
# 对于其他数据,使用浮点数格式
line_protocol = f"device_data,deviceId={assigned_device_id} {field_name}={data_point['fields'][field_name]}"
response = requests.post(url, headers=headers, data=line_protocol)
if response.status_code == 204:
print(f"✅ 数据已保存到InfluxDB设备{assigned_device_id}上: {field_name}={data_point['fields'][field_name]}")
else:
print(f"❌ 保存数据到InfluxDB失败: {response.status_code} - {response.text}")
else:
print(f"⚠️ 未知的协议ID: {protocol_id}")
except Exception as e:
print(f"❌ 保存数据到InfluxDB时出错: {e}")
import traceback
traceback.print_exc()
def main():
global assigned_device_id, phase_counter
try:
# 第一步:注册设备获取设备ID
if not register_device():
print("❌ 设备注册失败,程序退出")
return
print(f"🎯 开始使用设备ID {assigned_device_id} 发送数据...")
# 第二步:开始发送数据
while True:
# 初始化数据值
data_value = 0
# 每发送100个相位数据后发送其他数据
if phase_counter < 2:
# 发送相位数据 (心率相位或呼吸相位)
protocol_id = random.choice([3, 4]) # 3=心率相位, 4=呼吸相位
data_value = random.randint(-10000, 10000)
phase_counter += 1
else:
# 重置计数器
phase_counter = 0
# 发送其他数据
protocol_id = random.choice([1, 2, 13, 14]) # 1=心跳, 2=呼吸, 13=检测到人, 14=人体活动
if protocol_id == 1: # 心跳
data_value = random.randint(600, 1000)
elif protocol_id == 2: # 呼吸
data_value = random.randint(120, 200)
elif protocol_id == 13: # 检测到人(1检测到,0未检测到)
data_value = random.choice([0, 1])
elif protocol_id == 14: # 人体活动(1活动,0静止)
data_value = random.choice([0, 1])
# 直接发送数据到InfluxDB
save_data_to_influxdb(protocol_id, data_value)
# 设置发送间隔
time.sleep(0.1) # 每100ms发送一次数据
except KeyboardInterrupt:
print(f"\n🛑 设备 {assigned_device_id} 发送端已停止")
except Exception as e:
print(f"❌ 发生错误: {e}")
if __name__ == "__main__":
main()
-1177
View File
File diff suppressed because it is too large Load Diff