From 578dcd482a95e23b04cd3d98dcded810f56a350e Mon Sep 17 00:00:00 2001 From: jxh Date: Thu, 20 Mar 2025 21:38:43 +0800 Subject: [PATCH] =?UTF-8?q?#=20V0.2=20=E5=AE=9E=E7=8E=B0=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0HA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.cpp | 207 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 175 insertions(+), 32 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index aee441b..7ad1b68 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,29 @@ #include // 多WiFi连接支持 #include // MQTT客户端 +// 定义调试宏 +#define DEBUG + +#ifdef DEBUG +#define debugPrint(x) Serial.print (x) +#define debugPrintln(x) Serial.println (x) +#else +#define debugPrint(x) +#define debugPrintln(x) +#endif + +// 定义SensorData结构体 声明一个SensorData类型的全局变量 +struct SensorData +{ + float voltage; + float current; + float power; + float temp; + float humidity; +} sensorData; + +// 配置 + ESP8266WiFiMulti wifiMulti; // 多WiFi连接实例 WiFiClient espClient; @@ -15,6 +38,10 @@ void mqtt_callback (char *topic, byte *payload, void publishSensorData (); // MQTT发布函数 void reconnectMQTT (); // MQTT重连函数 void checkWifiConnection (); // WiFi连接检查函数 +void checkMqttConnection (); // MQTT连接检查函数 + +bool parseHexJson (String jsonStr); // 解析16进制字符串并返回数据 +bool parseCompactData (const char *data); // 解析紧凑型数据并返回数据 // MQTT相关配置信息 const char *mqtt_broker_addr = "mqtt.lovelyqi.cn"; // 服务器地址 @@ -23,20 +50,8 @@ const char *mqtt_username = "wireless"; // 账号(非必须) const char *mqtt_password = "charge"; // 密码(非必须) const uint16_t mqtt_client_buff_size = 4096; // 客户端缓存大小(非必须) String mqtt_client_id = "wireless_charge_client"; // 客户端ID -const char *mqtt_topic_pub = "esp32/wireless_charge/pub"; // 需要发布到的主题 -const char *mqtt_topic_sub = "esp32/wireless_charge/sub"; // 需要订阅的主题 - -// 定义SensorData结构体 -struct SensorData -{ - float voltage; - float current; - float power; - float temp; - float humidity; -}; - -SensorData sensorData; // 声明一个SensorData类型的全局变量 +const char *mqtt_topic_pub = "wireless_charger/status"; // 需要发布到的主题 +const char *mqtt_topic_sub = "wireless_charger/control"; // 需要订阅的主题 // 初始化函数 void @@ -65,32 +80,33 @@ setup () void loop () { - unsigned long currentMillis = millis (); // 读取当前时间 + // unsigned long currentMillis = millis (); // 读取当前时间 - // 1. 处理串口数据接收 + // 处理串口数据接收 if (Serial.available () > 0) { String jsonStr = Serial.readStringUntil ('\n'); if (jsonStr.startsWith ("{")) { - parseJSON (jsonStr); + parseHexJson (jsonStr); + } + else + { + Serial.println ("Invalid JSON format"); } } - Serial.println ("Serial read Done"); - // 2. 维护MQTT连接 - if (!mqttClient.connected ()) - { - reconnectMQTT (); // MQTT重连函数 - } - // mqttClient.loop (); // 维持MQTT心跳 - Serial.println ("mqttClient.connected()"); + // 维护WiFi连接 + checkWifiConnection (); + // 维护MQTT连接 + checkMqttConnection (); - // 3. 定时上传数据(10秒间隔) + // 3. 定时上传数据( xxx ms间隔 ) static unsigned long lastUpload = 0; - if (millis () - lastUpload > 10000) + if (millis () - lastUpload > 500) { publishSensorData (); + mqttClient.loop (); // 维持MQTT心跳 lastUpload = millis (); } @@ -105,7 +121,7 @@ loop () void parseJSON (String jsonStr) { - StaticJsonDocument<256> doc; + DynamicJsonDocument doc (256); // 动态分配内存 DeserializationError error = deserializeJson (doc, jsonStr); if (!error) @@ -120,6 +136,10 @@ parseJSON (String jsonStr) if (sensorData.voltage < 4.5) Serial.println ("电压异常!"); } + else + { + Serial.println ("JSON解析失败"); + } } // 串口中断接收 @@ -128,7 +148,7 @@ serialEvent () { String jsonData = Serial.readStringUntil ('\n'); if (jsonData.startsWith ("{")) - parseJSON (jsonData); + parseCompactData (jsonData.c_str ()); } // MQTT消息回调函数,该函数会在PubSubClient对象的loop方法中被调用 @@ -148,7 +168,8 @@ mqtt_callback (char *topic, byte *payload, unsigned int length) void publishSensorData () { - StaticJsonDocument<200> doc; + DynamicJsonDocument doc (256); // 动态分配内存 + doc["voltage"] = sensorData.voltage; doc["current"] = sensorData.current; doc["power"] = sensorData.power; @@ -159,7 +180,7 @@ publishSensorData () char payload[256]; serializeJson (doc, payload); - if (mqttClient.publish ("homeassistant/charger/state", payload)) + if (mqttClient.publish (mqtt_topic_pub, payload)) { Serial.println ("Data published"); } @@ -179,7 +200,8 @@ reconnectMQTT () delay (100); - if (mqttClient.connected ()) // 每个客户端需要有唯一的ID,不然上线时会把其他相同ID的客户端踢下线 + if (mqttClient + .connected ()) // 每个客户端需要有唯一的ID,不然上线时会把其他相同ID的客户端踢下线 { Serial.println ("MQTT Connected"); mqttClient.publish (mqtt_topic_pub, @@ -200,4 +222,125 @@ checkWifiConnection () { WiFi.reconnect (); } +} + +// MQTT连接检查 +void +checkMqttConnection () +{ + if (!mqttClient.connected ()) + { + reconnectMQTT (); // MQTT重连函数 + } +} + +// 非标准JSON解析函数 + +// 十六进制转换器 +uint16_t +hex4ToUint (const char *p) +{ + uint16_t val = 0; + for (uint8_t i = 0; i < 4; i++) + { + val <<= 4; + if (*p >= '0' && *p <= '9') + { + val |= *p - '0'; + } + else if (*p >= 'A' && *p <= 'F') + { + val |= *p - 'A' + 10; + } + p++; + } + return val; +} +// 输入示例:{"vol":1069,"cur":0037,"pwr":00E7,"tmp":0CA2,"hum":0BF6} +bool +parseHexJson (String jsonStr) +{ + // 键名位置查找(基于固定结构优化) + const char *key_pos[5] = { + strstr (jsonStr.c_str (), "\"vol\":"), // 电压位置 + strstr (jsonStr.c_str (), "\"cur\":"), // 电流位置 + strstr (jsonStr.c_str (), "\"pwr\":"), // 功率位置 + strstr (jsonStr.c_str (), "\"tmp\":"), // 温度位置 + strstr (jsonStr.c_str (), "\"hum\":") // 湿度位置 + }; + + // 校验键名完整性 + for (uint8_t i = 0; i < 5; i++) + { + if (!key_pos[i]) + return false; + key_pos[i] += 5; // 跳过键名部分(如"vol":) + } + + // 逐字段解析(固定4字节长度) + sensorData.voltage = hex4ToUint (key_pos[0]) / 100; + Serial.println (key_pos[0]); + Serial.println (hex4ToUint ("109A")); + sensorData.current = hex4ToUint (key_pos[1]) / 100; + sensorData.power = hex4ToUint (key_pos[2]) / 100; + Serial.println (sensorData.power); + sensorData.temp = hex4ToUint (key_pos[3]) / 100; + sensorData.humidity = hex4ToUint (key_pos[4]) / 100; + + return true; +} + +// 接收端解析函数(返回解析成功状态) +bool +parseCompactData (const char *data) +{ + // 格式验证(长度26字节:{xxxx,xxxx,xxxx,xxxx,xxxx}) + if ( data[0] != '{' || data[25] != '}') + { + Serial.println ("Failed to parse data, invalid format."); + return false; + } + + // 分割缓冲区初始化 + char segment[5] = { 0 }; // 4字符+终止符 + uint16_t params[5]; // 存储解析出的五个参数 + uint8_t paramIndex = 0; + uint8_t charIndex = 0; + + // 逐字符解析(1-24字符区间) + for (uint16_t i = 1; i < 25; i++) + { + if (data[i] == ',' || i == 24) + { // 分隔符或结尾触发转换 + params[paramIndex++] = strtoul (segment, NULL, 16); + memset (segment, 0, 5); + charIndex = 0; + } + else + { + segment[charIndex++] = data[i]; + } + } + + // 检查是否成功解析了五个参数 + if (paramIndex != 5) + { + Serial.println ("Failed to parse data, not enough parameters."); + return false; + } + + // 将解析出的数据按顺序赋值给全局的sensorData结构体 + sensorData.voltage = params[0] / 100.0f; + sensorData.current = params[1] / 100.0f; + sensorData.power = params[2] / 100.0f; + sensorData.temp = params[3] / 100.0f; + sensorData.humidity = params[4] / 100.0f; + + Serial.println (sensorData.voltage); + Serial.println (sensorData.current); + Serial.println (sensorData.power); + Serial.println (sensorData.temp); + Serial.println (sensorData.humidity); + + return true; } \ No newline at end of file