wenbo2005.online

学习记录 / Arduino 与 ESP

华为云&ESP8266

Arduino 与 ESP2025-04-29
/*如果MQTT连接返回-1状态码,那么需要将 PubSubClient.h 库中的两个宏定义MQTT_KEEPALIVE、MQTT_MAX_PACKET_SIZE 的数值调大*/

#include <ESP8266WiFi.h>  
#include <PubSubClient.h>  
#include <ArduinoJson.h>
#include "gateway.h"
#include "common.h"

//其他参数
#define ServiceId "LoRaService"
#define DeviceId "680b37c184adf27cda525864_GateWayESP8266"

//发布主题
#define SubsetTopic "$oc/devices/" DeviceId "/sys/events/up"
#define BatchReportTopic "$oc/devices/" DeviceId "/sys/gateway/sub_devices/properties/report"

//订阅主题
#define SysTopic "$oc/devices/" DeviceId "/sys/messages/down"
#define ADDSubsetTopic "$oc/devices/" DeviceId "/sys/events/down"

//子设备状态结构体
typedef struct
{
    String device_id;       //设备唯一标识
    bool online;            //在线状态
    float ac_v;             //交流电压
    float ac_i;             //交流电流
    uint8_t isuser;         //存在标志位
} SubDevice_str;

//WiFi
const char* wifi_ssid = "MyESP8266";  
const char* wifi_password = "12345678";

//华为云服务器地址
const uint16_t mqttPort = 1883;  
const char* mqttServer = "7e2cd9b1d1.st1.iotda-device.cn-north-4.myhuaweicloud.com";  

//MQTT三元组
const char* mqttUsername ="680b37c184adf27cda525864_GateWay_ESP8266";
const char* mqttClientId = "680b37c184adf27cda525864_GateWay_ESP8266_0_0_2025042610";  
const char* mqttPassword = "***";

WiFiClient espClient;
PubSubClient client(espClient);

#define LED_PIN 2
uint8_t esp8266_status=0;

//子设备数量(拟定255个设备=>内存分配)
const uint8_t subDevice_num = 255;
//子设备列表
SubDevice_str subDeviceList[subDevice_num]={{"LoRa_01",false,220,5.7},{"LoRa_02",false,218,4.6}};

void setup()
{  
    Serial.begin(115200);  
    pinMode(LED_PIN, OUTPUT);

    WiFi.mode(WIFI_STA);                    //设置为STA模式
    WiFi.begin(wifi_ssid,wifi_password);    //开始连接路由器
    client.setServer(mqttServer,mqttPort);  //服务器配置
    client.setCallback(MQTTCallback);       //MQTT接收回调函数
    System_Init();
}

void loop()
{
    Status_Display();
    STM32_Rx_Display();
    if(esp8266_status==0)
    {
        client.loop();
    }

}

uint32_t stm32_rx_time=0;
void STM32_Rx_Display()
{
    if(millis() - stm32_rx_time < 200) return;
    stm32_rx_time = millis();

    String Databuff="";
    while(Serial.available()>0)
    {
        Databuff+=char(Serial.read());
        delay(1);
    }
    if(Databuff.length() != 0)
    {
        if(esp8266_status==0)
        {
            if(Databuff.equals("SD"))
                SyncSubDevices();
            else if(Databuff.equals("RDS"))
                Report_SubDevice_Status();
            else if(Databuff.equals("RSD"))
                Report_SubDevices_Data();
        }
        else if(esp8266_status==1)
        {
            Serial.printf("NoWIFI");
        }
        else if(esp8266_status==2)
        {
            Serial.printf("NoService");
        }
    }
    Databuff = "";
}

/*********************************
 * 华为MQTT服务器主题订阅回调函数
 * topic: 消息类型.例如: $oc/devices/xxx/sys/messages/down
 * payload: 消息内容.例如: {"ststus":1}
*********************************/
void MQTTCallback(char* topic, byte* payload, unsigned int length)
{
    Serial.print("Payload: ");
    for (unsigned int i = 0; i < length; i++)
        Serial.print((char)payload[i]);

    // 1. 检测删除通知
    if(strstr((char*)payload, "delete_sub_device_notify") != NULL)
    {
        //提取version字段
        uint8_t version = 0;
        char* version_start = strstr((char*)payload, "\"version\":");
        if(version_start)
            version = atoi(version_start + 10); // 跳过"\"version\":"
        //动态生成回复
        char response[256];
        snprintf(response, sizeof(response),
            "{\"object_device_id\":\"%s\","
            "\"services\":[{"
            "\"service_id\":\"$sub_device_manager\","
            "\"event_type\":\"delete_sub_device_response\","
            "\"paras\":{\"result_code\":0,\"version\":%d}}]}",
            DeviceId, version);
        //发送回复
        client.publish(SubsetTopic, response);
    }
}

/*********************************
 * 批量上报子设备属性数据
 * 上报之前保证设备在线
 *********************************/
void Report_SubDevices_Data()
{
    DynamicJsonDocument doc(1024);
    JsonArray devices = doc.createNestedArray("devices");

    for(int i = 0; i < subDevice_num; i++)
    {
        JsonObject device = devices.createNestedObject();
        device["device_id"] = subDeviceList[i].device_id;

        JsonArray services = device.createNestedArray("services");
        JsonObject service = services.createNestedObject();
        service["service_id"] = ServiceId;          //服务ID

        JsonObject properties = service.createNestedObject("properties");
        properties["AC_V"] = subDeviceList[i].ac_v; // 交流电压
        properties["AC_I"] = subDeviceList[i].ac_i; // 交流电流
    }

    //将JSON数据序列化为字符串
    char jsonBuffer[1024];
    serializeJson(doc, jsonBuffer);
    if (client.publish(BatchReportTopic, jsonBuffer))
        Serial.println("子设备数据批量上报成功");
    else
        Serial.println("上报失败,错误码: " + String(client.state()));
}

/*********************************
 * 子设备状态上报函数
 *********************************/
void Report_SubDevice_Status()
{
    DynamicJsonDocument doc(512);
    doc["object_device_id"] = DeviceId;
    //创建服务数组
    JsonArray services = doc.createNestedArray("services");
    //创建状态上报服务
    JsonObject service = services.createNestedObject();
    service["service_id"] = "$sub_device_manager";
    service["event_type"] = "sub_device_update_status";
    //创建参数对象
    JsonObject paras = service.createNestedObject("paras");
    //创建设备状态数组
    JsonArray statusArray = paras.createNestedArray("device_statuses");
    //填充设备状态
    for(int i=0; i<subDevice_num; i++)
    {
        JsonObject status = statusArray.createNestedObject();
        status["device_id"] = subDeviceList[i].device_id;
        status["status"] = subDeviceList[i].online ? "ONLINE" : "OFFLINE";
    }
    //序列化并发送
    char jsonBuffer[512];
    serializeJson(doc, jsonBuffer);
    if(client.publish(SubsetTopic, jsonBuffer))
        Serial.println("子设备状态上报成功");
    else
        Serial.println("子设备状态批量上报失败,错误码: " + String(client.state()));
}

/*********************************
 * 网关同步子设备列表
 * 新增的子设备信息会通过平台通知网关子设备新增接口通知网关设备
 * 删除的子设备信息会通过平台通知网关子设备删除通知网关设备
*********************************/
void SyncSubDevices()
{
    DynamicJsonDocument doc(1024);
    //主设备ID
    doc["object_device_id"] = DeviceId;
    //创建服务数组
    JsonArray services = doc.createNestedArray("services");
    // 创建子设备管理服务
    JsonObject service = services.createNestedObject();
    service["service_id"] = "$sub_device_manager";
    service["event_type"] = "sub_device_sync_request";
    //创建参数对象
    JsonObject paras = service.createNestedObject("paras");
    paras["version"] = 1; // 版本号根据实际情况维护

    //序列化并发送
    char jsonBuffer[1024];
    serializeJson(doc, jsonBuffer);

    if(client.publish(SubsetTopic, jsonBuffer))
        Serial.println("设备同步请求发送成功");
    else
        Serial.println("设备同步请求发送失败");
}

/*********************************
 * ESP8266系统初始化函数: WIFI,Service
*********************************/
void System_Init()
{
    Serial.println("\nConnect WIFI...");
    while(WiFi.status()!=WL_CONNECTED)
    {
        bool led_flag = digitalRead(LED_PIN);
        digitalWrite(LED_PIN,!led_flag);
        delay(200);
    }
    Serial.println("WIFI_OK");
    Serial.println("Connect Service...");
    while(!client.connected())
    {
        if(client.connect(mqttClientId, mqttUsername, mqttPassword))
        {
            Serial.println("Server_OK");
            client.subscribe(SysTopic);         //订阅系统消息主题
            client.subscribe(ADDSubsetTopic);   //订阅增加子设备新增消息主题
            digitalWrite(LED_PIN,0);
        }
        else
            Serial.print(client.state());

        bool led_flag = digitalRead(LED_PIN);
        digitalWrite(LED_PIN,!led_flag);
        delay(500);
    }
}

/*********************************
 * ESP8266系统状态判断
 * 0: 正常运行
 * 1: WIFI已断开
 * 2: MQTT服务器断开
*********************************/
void Status_Display()
{
    if(WiFi.status()==WL_CONNECTED)
    {
        if(!client.connected())
            esp8266_status=2;
        else
            esp8266_status=0;
    }
    else
        esp8266_status=1;
}