# V0.2 实现数据上传HA
This commit is contained in:
207
src/main.cpp
207
src/main.cpp
@ -3,6 +3,29 @@
|
|||||||
#include <ESP8266WiFiMulti.h> // 多WiFi连接支持
|
#include <ESP8266WiFiMulti.h> // 多WiFi连接支持
|
||||||
#include <PubSubClient.h> // MQTT客户端
|
#include <PubSubClient.h> // 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连接实例
|
ESP8266WiFiMulti wifiMulti; // 多WiFi连接实例
|
||||||
|
|
||||||
WiFiClient espClient;
|
WiFiClient espClient;
|
||||||
@ -15,6 +38,10 @@ void mqtt_callback (char *topic, byte *payload,
|
|||||||
void publishSensorData (); // MQTT发布函数
|
void publishSensorData (); // MQTT发布函数
|
||||||
void reconnectMQTT (); // MQTT重连函数
|
void reconnectMQTT (); // MQTT重连函数
|
||||||
void checkWifiConnection (); // WiFi连接检查函数
|
void checkWifiConnection (); // WiFi连接检查函数
|
||||||
|
void checkMqttConnection (); // MQTT连接检查函数
|
||||||
|
|
||||||
|
bool parseHexJson (String jsonStr); // 解析16进制字符串并返回数据
|
||||||
|
bool parseCompactData (const char *data); // 解析紧凑型数据并返回数据
|
||||||
|
|
||||||
// MQTT相关配置信息
|
// MQTT相关配置信息
|
||||||
const char *mqtt_broker_addr = "mqtt.lovelyqi.cn"; // 服务器地址
|
const char *mqtt_broker_addr = "mqtt.lovelyqi.cn"; // 服务器地址
|
||||||
@ -23,20 +50,8 @@ const char *mqtt_username = "wireless"; // 账号(非必须)
|
|||||||
const char *mqtt_password = "charge"; // 密码(非必须)
|
const char *mqtt_password = "charge"; // 密码(非必须)
|
||||||
const uint16_t mqtt_client_buff_size = 4096; // 客户端缓存大小(非必须)
|
const uint16_t mqtt_client_buff_size = 4096; // 客户端缓存大小(非必须)
|
||||||
String mqtt_client_id = "wireless_charge_client"; // 客户端ID
|
String mqtt_client_id = "wireless_charge_client"; // 客户端ID
|
||||||
const char *mqtt_topic_pub = "esp32/wireless_charge/pub"; // 需要发布到的主题
|
const char *mqtt_topic_pub = "wireless_charger/status"; // 需要发布到的主题
|
||||||
const char *mqtt_topic_sub = "esp32/wireless_charge/sub"; // 需要订阅的主题
|
const char *mqtt_topic_sub = "wireless_charger/control"; // 需要订阅的主题
|
||||||
|
|
||||||
// 定义SensorData结构体
|
|
||||||
struct SensorData
|
|
||||||
{
|
|
||||||
float voltage;
|
|
||||||
float current;
|
|
||||||
float power;
|
|
||||||
float temp;
|
|
||||||
float humidity;
|
|
||||||
};
|
|
||||||
|
|
||||||
SensorData sensorData; // 声明一个SensorData类型的全局变量
|
|
||||||
|
|
||||||
// 初始化函数
|
// 初始化函数
|
||||||
void
|
void
|
||||||
@ -65,32 +80,33 @@ setup ()
|
|||||||
void
|
void
|
||||||
loop ()
|
loop ()
|
||||||
{
|
{
|
||||||
unsigned long currentMillis = millis (); // 读取当前时间
|
// unsigned long currentMillis = millis (); // 读取当前时间
|
||||||
|
|
||||||
// 1. 处理串口数据接收
|
// 处理串口数据接收
|
||||||
if (Serial.available () > 0)
|
if (Serial.available () > 0)
|
||||||
{
|
{
|
||||||
String jsonStr = Serial.readStringUntil ('\n');
|
String jsonStr = Serial.readStringUntil ('\n');
|
||||||
if (jsonStr.startsWith ("{"))
|
if (jsonStr.startsWith ("{"))
|
||||||
{
|
{
|
||||||
parseJSON (jsonStr);
|
parseHexJson (jsonStr);
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
Serial.println ("Serial read Done");
|
|
||||||
|
|
||||||
// 2. 维护MQTT连接
|
|
||||||
if (!mqttClient.connected ())
|
|
||||||
{
|
{
|
||||||
reconnectMQTT (); // MQTT重连函数
|
Serial.println ("Invalid JSON format");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// mqttClient.loop (); // 维持MQTT心跳
|
|
||||||
Serial.println ("mqttClient.connected()");
|
|
||||||
|
|
||||||
// 3. 定时上传数据(10秒间隔)
|
// 维护WiFi连接
|
||||||
|
checkWifiConnection ();
|
||||||
|
// 维护MQTT连接
|
||||||
|
checkMqttConnection ();
|
||||||
|
|
||||||
|
// 3. 定时上传数据( xxx ms间隔 )
|
||||||
static unsigned long lastUpload = 0;
|
static unsigned long lastUpload = 0;
|
||||||
if (millis () - lastUpload > 10000)
|
if (millis () - lastUpload > 500)
|
||||||
{
|
{
|
||||||
publishSensorData ();
|
publishSensorData ();
|
||||||
|
mqttClient.loop (); // 维持MQTT心跳
|
||||||
lastUpload = millis ();
|
lastUpload = millis ();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +121,7 @@ loop ()
|
|||||||
void
|
void
|
||||||
parseJSON (String jsonStr)
|
parseJSON (String jsonStr)
|
||||||
{
|
{
|
||||||
StaticJsonDocument<256> doc;
|
DynamicJsonDocument doc (256); // 动态分配内存
|
||||||
DeserializationError error = deserializeJson (doc, jsonStr);
|
DeserializationError error = deserializeJson (doc, jsonStr);
|
||||||
|
|
||||||
if (!error)
|
if (!error)
|
||||||
@ -120,6 +136,10 @@ parseJSON (String jsonStr)
|
|||||||
if (sensorData.voltage < 4.5)
|
if (sensorData.voltage < 4.5)
|
||||||
Serial.println ("电压异常!");
|
Serial.println ("电压异常!");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.println ("JSON解析失败");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 串口中断接收
|
// 串口中断接收
|
||||||
@ -128,7 +148,7 @@ serialEvent ()
|
|||||||
{
|
{
|
||||||
String jsonData = Serial.readStringUntil ('\n');
|
String jsonData = Serial.readStringUntil ('\n');
|
||||||
if (jsonData.startsWith ("{"))
|
if (jsonData.startsWith ("{"))
|
||||||
parseJSON (jsonData);
|
parseCompactData (jsonData.c_str ());
|
||||||
}
|
}
|
||||||
|
|
||||||
// MQTT消息回调函数,该函数会在PubSubClient对象的loop方法中被调用
|
// MQTT消息回调函数,该函数会在PubSubClient对象的loop方法中被调用
|
||||||
@ -148,7 +168,8 @@ mqtt_callback (char *topic, byte *payload, unsigned int length)
|
|||||||
void
|
void
|
||||||
publishSensorData ()
|
publishSensorData ()
|
||||||
{
|
{
|
||||||
StaticJsonDocument<200> doc;
|
DynamicJsonDocument doc (256); // 动态分配内存
|
||||||
|
|
||||||
doc["voltage"] = sensorData.voltage;
|
doc["voltage"] = sensorData.voltage;
|
||||||
doc["current"] = sensorData.current;
|
doc["current"] = sensorData.current;
|
||||||
doc["power"] = sensorData.power;
|
doc["power"] = sensorData.power;
|
||||||
@ -159,7 +180,7 @@ publishSensorData ()
|
|||||||
char payload[256];
|
char payload[256];
|
||||||
serializeJson (doc, payload);
|
serializeJson (doc, payload);
|
||||||
|
|
||||||
if (mqttClient.publish ("homeassistant/charger/state", payload))
|
if (mqttClient.publish (mqtt_topic_pub, payload))
|
||||||
{
|
{
|
||||||
Serial.println ("Data published");
|
Serial.println ("Data published");
|
||||||
}
|
}
|
||||||
@ -179,7 +200,8 @@ reconnectMQTT ()
|
|||||||
|
|
||||||
delay (100);
|
delay (100);
|
||||||
|
|
||||||
if (mqttClient.connected ()) // 每个客户端需要有唯一的ID,不然上线时会把其他相同ID的客户端踢下线
|
if (mqttClient
|
||||||
|
.connected ()) // 每个客户端需要有唯一的ID,不然上线时会把其他相同ID的客户端踢下线
|
||||||
{
|
{
|
||||||
Serial.println ("MQTT Connected");
|
Serial.println ("MQTT Connected");
|
||||||
mqttClient.publish (mqtt_topic_pub,
|
mqttClient.publish (mqtt_topic_pub,
|
||||||
@ -201,3 +223,124 @@ checkWifiConnection ()
|
|||||||
WiFi.reconnect ();
|
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;
|
||||||
|
}
|
Reference in New Issue
Block a user