更新雷达系统代码,添加WiFi重连机制和雷达数据处理功能
This commit is contained in:
45
.vscode/settings.json
vendored
45
.vscode/settings.json
vendored
@@ -3,7 +3,50 @@
|
|||||||
"cmath": "cpp",
|
"cmath": "cpp",
|
||||||
"array": "cpp",
|
"array": "cpp",
|
||||||
"string": "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"
|
"C_Cpp.errorSquiggles": "disabled"
|
||||||
}
|
}
|
||||||
1861
src/main.cpp
1861
src/main.cpp
File diff suppressed because it is too large
Load Diff
2582
src/main_backup.cpp.bak
Normal file
2582
src/main_backup.cpp.bak
Normal file
File diff suppressed because it is too large
Load Diff
1721
src/radar_manager.cpp
Normal file
1721
src/radar_manager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
235
src/radar_manager.h
Normal file
235
src/radar_manager.h
Normal 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
src/wifi_manager.cpp
Normal file
567
src/wifi_manager.cpp
Normal 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
src/wifi_manager.h
Normal file
121
src/wifi_manager.h
Normal 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
tcp-output(1).py
Normal file
330
tcp-output(1).py
Normal 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
tcp-output(5).py
243
tcp-output(5).py
@@ -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
test/app.cpp
1177
test/app.cpp
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user