GD32 F303RCT6串口通信和IAP升级

#ifndef __CLOUDTASK_H
#define __CLOUDTASK_H

#include "task.h"
#include "queue.h"
#include "timers.h"

#define FRAME_DATA_LEN      512     // 帧数据最大长度

// 帧协议格式定义
typedef struct CloudComFrameStru
{
    uint16_t frameHead;                         // 帧头
    uint16_t frameLen;                          // 帧长度
    uint16_t frameNo;                           // 帧序号
    uint8_t  frameMark;                         // 帧标志
    uint8_t  frameCmd;                          // 帧命令
    uint8_t  frameData[FRAME_DATA_LEN];         // 帧数据
    uint16_t frameDataLen;                      // 帧数据长度
    uint8_t  frameCheck;                        // 帧校验

} CloudComFrame;

// 主控同wifi/bt通信任务
static TaskHandle_t taskHandleCloudCom = NULL;

// 帧流水号
// static uint16_t frameNo = 1; // 用于多帧连续发送

// 重发标志
static volatile uint8_t retryFlag = 0; // 0: 不需要重发  1: 需要重发

// 当前发送帧
static CloudComFrame curr_frame = {0};      

// 主控给wifi/bt发送消息队列
static QueueHandle_t    cloudComSendQueueHandle = NULL;
// Wifi/Bt给主控接收消息队列
// static QueueHandle_t    cloudComRecvQueueHandle = NULL;
// 重发消息队列
static QueueHandle_t    retryQueueHandle = NULL;

// 定时器
static TimerHandle_t    heartPackTimerHandle;   // 心跳定时器
static TimerHandle_t    retryTimerHandle;       // 重发定时器
static TimerHandle_t    itvTimerHandle;         // ITV定时器
static TimerHandle_t    freqItvTimerHandle;     // 频繁itv上报定时器
static TimerHandle_t    reportSwitchTimerHandle;// 上报开关定时器
static volatile uint8_t retryCount = 0;         // 重发计数器
static volatile uint8_t heartPackCount = 0;     // 心跳计数器

// 联网状态和信号强度
typedef struct NetInfoStru
{
    // 0: 未连接路由器 1: 一键配置中 2: AP模式 3: 连接路由器,但是没有连接云 
    // 4:连接路由器,并且连接云 5:连接上路由器,没有得到IP
    uint8_t netStatus;                      // 联网状态  
    // D8: -40dBm
    int16_t signalPower;                    // 信号强度   
} NetInfo;

// 发送产品ID
typedef struct ProductIdStru
{
    // 0: 成功 1: 失败
    uint8_t result;                         // 录入产品ID结果
    char proId[32];                         // 产品ID
} ProductId;

// mac / version
typedef struct MacVerStru
{
    uint8_t mac[6];                         // mac
    uint8_t ver[4];                         // version
} MacVer;

// ITV类型中的I(Index)
typedef enum ENUM_ITV_ID
{
    ITV_ID_DEVIC_TYPE = 1,        ///主控类型              0x01                                            
    ITV_ID_DTC,                   /// Device Type Code    0x02
    ITV_ID_INV_VERH,              ///逆变器固件版本高位     0x03 
    ITV_ID_INV_VERM,              ///逆变器固件版本中位     0x04
    ITV_ID_INV_VERL,              ///逆变器固件版本低位     0x05
    ITV_ID_MAIN_VERH,             ///主控固件版本高位       0x06
    ITV_ID_MAIN_VERM,             ///主控固件版本中位       0x07
    ITV_ID_MAIN_VERL,             ///主控固件版本低位       0x08
    ITV_ID_HARD_VERH,             ///主控硬件版本高位       0x09
    ITV_ID_HARD_VERM,             ///主控硬件版本中位       0x0A
    ITV_ID_HARD_VERL,             ///主控硬件版本低位       0x0B
    ITV_ID_SN,                    ///主控序列号             0X0C
    ITV_ID_INV_TYPE,              ///逆变器类型             0X0D
    ITV_ID_P_RATE,                ///额定功率               0X0E
    ITV_ID_INV_CAP_RATE,          ///逆变器标称容量         0X0F
    ITV_ID_INV_SLEEP_TIME,        ///逆变器休眠时间设置//1-999分钟,默认720分钟 //最大26bit u16     0X10
    ITV_ID_DISP_SLEEP_TIMER,      ///屏幕休眠时间设置//0-60分钟,默认5分钟 u8//TODO 这个时间和背光延时区别?    0X11
    ITV_ID_OUT_TYPE,              ///输出电压类型           0X12               
    ITV_ID_FREQ_TYPE,             ///输出频率类型           0X13
    ITV_ID_CHARGE_IMAX,           ///"Max Charge Current最大充电电流"   //市电一致,没有混合充电    0X14
    ITV_ID_CHARGE_VMAX,           ///"Bulk Charge Volt充电恒压点"   //没有                        0X15  
    ITV_ID_CHARGE_VFLOAT,         ///"Float Charge Volt充电浮充点"  //没有                        0X16
    ITV_ID_ACCHARGE_IMAX,         ///"Max Ac charge current最大市电充电电流"                      0X17
    ITV_ID_CHARGE_PMAX,           ///"Max charge watt充电功率最大值"    //没有                    0X18
    ITV_ID_BAT_OFF,               ///"Bat cut off voltage or SOC关机电压或关机的电池容量" //0.1    0X19
    ITV_ID_BLIGHT_DELAY,          ///背光延时   //和背光熄灭的区别?//TODO                           0X1A

    ITV_ID_DNS,                   ///域名系统   //主控只读          0X1B
    ITV_ID_IP,                    /// IP地址    //主控只读          0X1C
    ITV_ID_PORT,                  ///端口号 //主控只读              0X1D
    ITV_ID_MAC,                   /// Mac地址   //主控只读          0X1E
    ITV_ID_REMOTE_IP,             ///远端IP地址 //主控只读          0X1F
    ITV_ID_REMOTE_PORT,           ///远端端口号 //主控只读          0X20
    ITV_ID_REMOTE_URL,            ///远端域名   //主控只读          0X21
    ITV_ID_SUB_MASK,              ///子网掩码   //主控只读          0X22
    ITV_ID_DEF_GATEWAY,           ///默认网关   //主控只读          0X23
    ITV_ID_BT_MAC,                ///蓝牙的 MAC 地址    //主控只读  0X24
    ITV_ID_BLE_TOKEN,             ///蓝牙密钥   //主控只读          0X25
    ITV_ID_WIFI_SSID,             /// WIFI 模块要链接热点名称   //主控只读      0X26
    ITV_ID_WIFI_PASSWORD,         /// WIFI 模块要链接的热点密钥 //主控只读      0X27
    ITV_ID_WIFI_CONFIG,           /// WIFI 模块相关配置信息 //主控只读          0X28
    ITV_ID_WIFI_MODE,             /// Wifi 模式切换 //主控只读                  0X29
    ITV_ID_DHCP_EN,               /// DHCP 使能 //主控只读                      0X2A
    ITV_ID_WIFI_STATE,            /// Wifi 连接状态 //主控只读                  0X2B
    ITV_ID_OTA_URL,               /// OTA升级路径   //主控只读                  0X2C
    ITV_ID_OTA_STEP,              /// OTA升级进度   //TODO                      0X2D
    ITV_ID_OTA_STATE,             /// OTA升级状态                               0X2E
    ITV_ID_RESET_EN,              ///重启开关使能   //没有                      0X2F
    ITV_ID_FUNC_EN,               ///功能开关   //错误自恢复是没有的  //屏蔽过载是禁止的    //关背光灯?//TODO只能开关蜂鸣器功能   0X30
    ITV_ID_SYS_TIME,              ///系统时间   //主控只读              0X31
    ITV_ID_TIME_OFFSET,           ///时区偏移   //主控只读              0X32
    ITV_ID_FUNC_CMD,              ///工作命令   //1是恢复出厂设置 0不管
    ITV_ID_OUT_EN,                ///输出开关   //TODO bit0 usb bit1 car 这个是模块是否使能           0X34
    ITV_ID_NET_EN,                ///网络开关   //??                0X35
    ITV_ID_SYS_STATE,             ///系统状态机 //BUG               0X36
    ITV_ID_P_IN_SUM,              ///输入总功率     //              0X37
    ITV_ID_V_ACIN,                ///输入AC电源电压 //              0X38
    ITV_ID_FREQ_ACIN,             ///输入AC电源频率 //              0X39
    ITV_ID_P_ACIN,                ///"AC input watt (low)市电输入功率"  //          0X3A
    ITV_ID_V_PV,                  ///"PV1 voltagePV(光伏)电压"  //                  0X3B
    ITV_ID_P_PV_CHARGE,           ///"PV charge powerPV(光伏)充电功率" //           0X3C
    ITV_ID_I_PV_CHARGE,           ///"Buck1 currentPV充电电流"  //                  0X3D
    ITV_ID_V_BAT,                 ///电池电压   //                                  0X3E
    ITV_ID_SOC_BAT,               ///电池容量百分比 //循环老化?                     0X3F
    ITV_ID_P_CHARGE_SUM,          ///充电总功率 //                                  0X40
    ITV_ID_I_CHARGE_SUM,          ///充电总电流 //                                  0X41
    ITV_ID_P_AC_CHARGE,           ///市电充电功率   //                              0X42
    ITV_ID_I_AC_CHARGE,           ///市电充电电流   //                              0X43
    ITV_ID_RELEASE_CHARGE_TIME,   ///剩余充电时间   //                              0X44
    ITV_ID_ELEASE_DISCHARGE_TIME, ///剩余放电时间   //                              0X45
    ITV_ID_P_AC_DISCHARGE,        ///市电放电功率   //                              0X46
    ITV_ID_P_BAT_DISCHARGE,       ///电池放电功率   //                              0X47
    ITV_ID_I_BAT_DISCHARGE,       ///电池放电电流   //                              0X48
    ITV_ID_P_BAT,                 ///电池功率   //                                  0X49
    ITV_ID_P_OUT_SUM,             ///输出总功率 //                                  0X4A
    ITV_ID_I_OUT,                 ///输出电流   //                                  0X4B
    ITV_ID_P_INV_OUT,             ///逆变输出功率   //                              0X4C
    ITV_ID_I_INV_OUT,             ///逆变输出电流   //                              0X4D
    ITV_ID_V_AC_OUT,              ///输出AC电源电压     //                          0X4E
    ITV_ID_FREQ_AC_OUT,           ///输出AC电源频率 //                              0X4F
    ITV_ID_SOC_LOAD,              ///负载百分比 //                                  0X50
    ITV_ID_V_CIGAR_LIGHTER,       ///"Car voltage车充点烟器电压"//没有哦            0X51
    ITV_ID_P_CIGAR_LIGHTER,       ///"Car Watt车充点烟器功率"//没有哦               0X52
    ITV_ID_I_V_CIGAR_LIGHTER,     ///"Car Current车充点烟器电流"                    0X53
    ITV_ID_V_USB1,                /// Usb1电压                                      0X54
    ITV_ID_V_USB2,                /// Usb2电压                                      0X55
    ITV_ID_V_USB3,                /// Usb3电压                                      0X56
    ITV_ID_V_USB4,                /// Usb4电压                                      0X57
    ITV_ID_V_USB5,                /// Usb5电压                                      0X58
    ITV_ID_V_USB6,                /// Usb6电压                                      0X59
    ITV_ID_I_USB1,                /// Usb1电流                                      0X5A
    ITV_ID_I_USB2,                /// Usb2电流                                      0X5B
    ITV_ID_I_USB3,                /// Usb3电流                                      0X5C
    ITV_ID_I_USB4,                /// Usb4电流                                      0X5D
    ITV_ID_I_USB5,                /// Usb5电流                                      0X5E
    ITV_ID_I_USB6,                /// Usb6电流                                      0X5F
    ITV_ID_P_USB1,                /// Usb1功率                                      0X60
    ITV_ID_P_USB2,                /// Usb2功率                                      0X61
    ITV_ID_P_USB3,                /// Usb3功率                                      0X62
    ITV_ID_P_USB4,                /// Usb4功率                                      0X63
    ITV_ID_P_USB5,                /// Usb5功率                                      0X64
    ITV_ID_P_USB6,                /// Usb6功率                                      0X65
    ITV_ID_P_OP_DC,               ///无线充电功率                                   0X66
    ITV_ID_OUT_STATE,             ///输出开关状态   //实际状态                      0X67
    ITV_ID_TEMP_INV,              ///逆变散热片温度                                 0X68
    ITV_ID_TEMP_BAT,              ///电池内部温度                                   0X69
    ITV_ID_TEMP_PD1,              /// PD板1温度                                     0X6A
    ITV_ID_TEMP_PD2,              /// PD板2温度                                     0X6B
    ITV_ID_TEMP_PV,               /// PV(光伏)温度                                  0X6C
    ITV_ID_TEMP_TR,               ///变压器温度                                     0X6D
    ITV_ID_TEMP_DC,               ///"DC-DC Temperature电池散热片温度"              0X6E
    ITV_ID_ERROR,                 ///错误码                                         0X6F
    ITV_ID_WARNING,               ///警告码                                         0X70
    ITV_ID_ERROR_BMS,             /// Bms出错                                       0X71
    ITV_ID_BLIGHT_DUTY,           // 背光占空比                                     0x72
    ITV_ID_POWER_CMD,             // 电源输出命令                                   0x73
    ITV_ID_LED_STATE,             // Led灯状态                                      0x74
    ITV_ID_POWER_BOOST_EN,        // 降额带载使能开关                                0x75
    ITV_ID_MAX,                   ///最大ID号
} ITV_ID;

// ITV帧数据构造定义
typedef struct ITV_INDEX_ARRAY_STRU
{
    uint16_t indexArr[ITV_ID_MAX];    // 存放index数组
    uint16_t indexArrLen;           // index数组长度

} ITV_INDEX_ARRAY;

// ITV中的T(type)
typedef enum ENUM_ITV_TYPE
{
    ITV_TYPE_STRING = 0,        ///string
    ITV_TYPE_U8,                ///u8
    ITV_TYPE_S16,               ///s16
    ITV_TYPE_S32,               ///s32
    ITV_TYPE_U16,               ///u16
    ITV_TYPE_U32,               ///u32
    ITV_TYPE_FLOAT,             ///float
    ITV_TYPE_S8,                ///s8
    ITV_TYPE_S8A,               ///s8[]
    ITV_TYPE_BOOL,              ///bool
} ITV_TYPE;

#pragma pack(push) //数据压站
#pragma pack(1) //按BYTE对齐

typedef char    tStringDef;    ///字符串类型

typedef struct ITV_Stru   ///itv数据结构
{
    /* data */
    uint16_t u16Index;       ///itv的ID号
    uint8_t u8Type;          ///itv的数据类型
    union 
    {
        /* data */
        tStringDef *ptStringData;   ///字符串
        uint8_t *pu8Data;    ///无符号
        int16_t *ps16Data;
        int32_t *ps32Data;
        uint16_t *pu16Data;
        uint32_t *pu32Data;
        float *pfData;       ///浮点型
        int8_t *ps8Data;
        int8_t *pas8Data;    字节数组型
        uint8_t *pbData;    布尔型
    }upData;   ///新数据

    union 
    {
        /* data */
        tStringDef *ptStringData;   ///字符串
        uint8_t *pu8Data;    ///无符号
        int16_t *ps16Data;
        int32_t *ps32Data;
        uint16_t *pu16Data;
        uint32_t *pu32Data;
        float *pfData;       ///浮点型
        int8_t *ps8Data;
        int8_t *pas8Data;    字节数组型
        uint8_t *pbData;    布尔型
    }lastData;   ///旧数据

    union 
    {
        /* data */
        tStringDef *ptStringData;   ///字符串
        uint8_t *pu8Data;    ///无符号
        int16_t *ps16Data;
        int32_t *ps32Data;
        uint16_t *pu16Data;
        uint32_t *pu32Data;
        float *pfData;       ///浮点型
        int8_t *ps8Data;
        int8_t *pas8Data;    字节数组型
        uint8_t *pbData;    布尔型
    }setData;   ///设置数据
} tITVDef;

typedef struct 
{
    /* data */
    uint8_t u8DevicType;              ///主控类型
    uint16_t u16DTC;                  /// Device Type Code
    uint16_t u16InvVerH;              ///逆变器固件版本高位
    uint16_t u16InvVerM;              ///逆变器固件版本中位
    uint16_t u16InvVerL;              ///逆变器固件版本低位
    uint16_t u16MainVerH;             ///主控固件版本高位
    uint16_t u16MainVerM;             ///主控固件版本中位
    uint16_t u16MainVerL;             ///主控固件版本低位
    uint16_t u16HardVerH;             ///主控硬件版本高位
    uint16_t u16HardVerM;             ///主控硬件版本中位
    uint16_t u16HardVerL;             ///主控硬件版本低位
    tStringDef tSN[32];               ///主控序列号
    uint8_t u8InvType;                ///逆变器类型
    uint32_t u32PRate;                ///额定功率
    uint8_t u8InvNomCap;              ///逆变器标称容量
    uint16_t u16InvSleepTime;         ///逆变器休眠时间设置
    uint8_t u8DispSleepTime;          ///屏幕休眠时间设置
    uint8_t u8OutPowerType;           ///输出电压类型
    uint8_t u8OutFreqType;            ///输出频率类型
    uint16_t u16ChargeCurrMax;        ///"Max Charge Current最大充电电流"
    uint16_t u16ChargeVolt;           ///"Bulk Charge Volt充电恒压点"
    uint16_t u16FloatChargeVolt;      ///"Float Charge Volt充电浮充点"
    uint16_t u16IAcChargeCurrMax;     ///"Max Ac charge current最大市电充电电流"
    uint8_t u8PChargePowerMax;        ///"Max charge watt充电功率最大值"
    uint16_t u16SocBatOff;            ///"Bat cut off voltage or SOC关机电压或关机的电池容量"
    float fBLightDelay;               ///背光延时
    tStringDef tDNS[32];              ///域名系统
    tStringDef tIP[32];               /// IP地址
    tStringDef tPort[32];             ///端口号
    tStringDef tMac[32];              /// Mac地址
    tStringDef tRemoteIP[32];         ///远端IP地址
    tStringDef tRemotePort[32];       ///远端端口号
    tStringDef tRemoteURL[32];       ///远端域名
    tStringDef tSubnetMask[32];       ///子网掩码
    tStringDef tDefaultGateway[32];   ///默认网关
    tStringDef tBTMAC[32];            ///蓝牙的 MAC 地址
    tStringDef tBLETOKEN[32];        ///蓝牙密钥
    tStringDef tWiFiSSID[32];        /// WIFI 模块要链接热点名称
    tStringDef tWiFiPassword[32];    /// WIFI 模块要链接的热点密钥
    tStringDef s8WiFiConfig[7];       /// WIFI 模块相关配置信息
    uint8_t u8WifiMode;               /// Wifi 模式切换
    uint8_t u8DHCPEn;                 /// DHCP 使能
    uint8_t u8WIFIConnectState;       /// Wifi 连接状态
    tStringDef tOTAURL[1];          /// OTA升级路径
    uint8_t u8OTAStep;                /// OTA升级进度
    uint8_t u8OTAState;               /// OTA升级状态
    uint8_t u8RestartEn;              ///重启开关使能
    uint8_t u8FuncEN;                 ///功能开关
    uint32_t u32SysTime;              ///系统时间
    int32_t s32TimeOffset;            ///时区偏移
    uint8_t u8CmdFunc;                ///工作命令
    uint8_t u8OutputEn;               ///输出开关
    uint16_t u16WifiBLESw;            ///网络开关
    uint8_t u8SysStatus;              ///系统状态机
    uint16_t u16PInSum;               ///输入总功率
    uint16_t u16VACIn;                ///输入AC电源电压
    uint16_t u16FreqACIn;             ///输入AC电源频率
    uint32_t u32PACIn;                ///"AC input watt (low)市电输入功率"
    uint16_t u16VPv1;                 ///"PV1 voltagePV(光伏)电压"
    uint32_t u32PPvCharge;            ///"PV charge powerPV(光伏)充电功率"
    uint16_t u16IPvCharge;            ///"Buck1 currentPV充电电流"
    uint16_t u16VBat;                 ///电池电压
    uint8_t u8BatSoc;                 ///电池容量百分比
    uint16_t u16PChargeSum;           ///充电总功率
    uint16_t u16IChargeSum;           ///充电总电流
    uint32_t u32PACCharge;            ///市电充电功率
    uint16_t u16IACCharge;            ///市电充电电流
    uint16_t u16ReleaseChargeTime;    ///剩余充电时间
    uint16_t u16ReleaseDisChargeTime; ///剩余放电时间
    uint32_t u32PACDisCharge;         ///市电放电功率
    uint32_t u32PBatDisCharge;        ///电池放电功率
    uint16_t u16IBatDisCharge;        ///电池放电电流
    uint32_t u32Pbat;                 ///电池功率
    uint16_t u16POutSum;              ///输出总功率
    uint16_t u16Iout;                 ///输出电流
    uint32_t u32PInvOut;              ///逆变输出功率
    uint16_t u16IInvOut;              ///逆变输出电流
    uint16_t u16VACOut;               ///输出AC电源电压
    uint16_t u16FreqACOut;            ///输出AC电源频率
    uint16_t u16LoadSoc;              ///负载百分比
    uint16_t u16VCarPower;            ///"Car voltage车充点烟器电压"
    uint16_t u16PCarPower;            ///"Car Watt车充点烟器功率"
    uint16_t u16ICarPower;            ///"Car Current车充点烟器电流"
    uint16_t u16VUsbOut1;             /// Usb1电压
    uint16_t u16VUsbOut2;             /// Usb2电压
    uint16_t u16VUsbOut3;             /// Usb3电压
    uint16_t u16VUsbOut4;             /// Usb4电压
    uint16_t u16VUsbOut5;             /// Usb5电压
    uint16_t u16VUsbOut6;             /// Usb6电压
    uint16_t u16IUsbOut1;             /// Usb1电流
    uint16_t u16IUsbOut2;             /// Usb2电流
    uint16_t u16IUsbOut3;             /// Usb3电流
    uint16_t u16IUsbOut4;             /// Usb4电流
    uint16_t u16IUsbOut5;             /// Usb5电流
    uint16_t u16IUsbOut6;             /// Usb6电流
    uint16_t u16PUsbOut1;             /// Usb1功率
    uint16_t u16PUsbOut2;             /// Usb2功率
    uint16_t u16PUsbOut3;             /// Usb3功率
    uint16_t u16PUsbOut4;             /// Usb4功率
    uint16_t u16PUsbOut5;             /// Usb5功率
    uint16_t u16PUsbOut6;             /// Usb6功率
    uint16_t u16POpDc;                ///无线充电功率
    uint8_t u8OutputStatus;           ///输出开关状态
    int16_t s16TempInv;	  			  ///逆变散热片温度
    int16_t s16TempBat;	  			  ///电池内部温度
    int16_t s16TempPD1;	  			  ///PD板1温度
    int16_t s16TempPD2;	  			  ///PD板2温度
    int16_t s16TempPV;	  			  ///PV(光伏)温度
    int16_t s16TempTr;	  			  ///变压器温度
    int16_t s16TempDc;	  			  ///"DC-DC Temperature电池散热片温度"
    uint32_t u32ErrorBit;             ///错误码
    uint16_t u16ErrorBmsBit;          /// Bms出错
    uint32_t u32WarningBit;           ///警告码
    uint16_t u16BLightDuty;      背光占空比
    uint8_t u8PowerCmd;       电源输出命令
    uint8_t u8LedState;         // Led灯状态    
    uint8_t u8PowerBoostEn;     // 降额带载使能开关
} tVitaDataDef;

#pragma pack(pop) //恢复设置 

// 配置SN
typedef struct SN_Stru
{
    uint8_t     result;     // 配置SN
    char snNo[32];          // 产品SN
} SN;

// 设置主控时间
typedef struct MainCtrlTimeStru
{
    uint32_t utc;   
    int32_t timeOffset;
} MainCtrlTime;

// 配网模式
typedef struct ConfigNetModeStru
{
    uint8_t mode;       // 配网模式
} ConfigNetMode;

// 测试结果
typedef struct TestResultStru
{
    // 0: wifi和ble测试失败      1: wifi测试失败     2:ble测试失败      3:wifi和ble测试成功
    uint8_t  testResult;    // 测试结果
} TestResult;

// 主控版本
typedef struct MainCtrlVerStru
{
    uint8_t softVer[4];       // 软件版本
    uint8_t hardVer[4];       // 硬件版本
    uint16_t devType;         // 设备类型
} MainCtrlVer;

/*-----------------------------主控请求网络模块----------------------------------*/

// 主控发送配网指令
void startSetNet(uint8_t type);

// 查询联网状态和信号强度
void queryNetInfo(void);

// 获取联网状态和信号强度
NetInfo getNetInfo(CloudComFrame* frame);

// 构建产品id
void createProductId(uint8_t (*pd)[32]);

// 发送产品ID
void sendProductId(uint8_t* data, uint16_t dataLen);

// 获取发送产品ID结果
ProductId getProductResult(CloudComFrame* frame);

// 启动测试流程
void startTest(void);

// 重启网络模块
void restartNetModule(uint8_t u8Type);

// 恢复出厂设置
void factoryReset(uint8_t type);

// 查询mac地址和软件版本号
void queryMacAndSoftVer(void);

// 获取mac和version
MacVer getMacAndSoftVer(CloudComFrame* frame);

// 汇报ITV
void sendITV(uint8_t* data, uint16_t dataLen, uint16_t frameNo);

// 用于上报全部ITV数据,分割为FRAME_DATA_LEN的数据上报
ITV_INDEX_ARRAY getITV_Index_ArrayLimitDataLen(void);

/*-----------------------------------------------------------------------------
函数目的: 构造ITV数据
参数:     data         返回ITV数据,所以该数据由调用者自己在外部定义,然后传参
          indexArr     传入ITV_ID类型的数组
          len          传入ITV_ID数组的实际大小

警告:   1、  调用该函数前,需要给ITV静态全局变量tVitaData赋值
        2、 构造ITV数据的大小不能超过FRAME_DATA_LEN,调用者确保最终回传data不会超过FRAME_DATA_LEN
返回值: 成功: 1  失败: 0
-----------------------------------------------------------------------------*/
uint16_t Create_ITV_Data(uint8_t (*data)[FRAME_DATA_LEN], uint16_t indexArr[ITV_ID_MAX], uint16_t len);

// 获取网络时间
MainCtrlTime getNetTimeData(CloudComFrame* frame);

// 获取网络时间
void getNetTime(void);

// 创建SN
void createSN(uint8_t (*data)[32]);

// 配置SN
void setSN(uint8_t* data, uint16_t dataLen);

// 获取配置SN结果
SN getSN_Result(CloudComFrame* frame);

// 发送心跳包
void sendHeartPack(void);

// 查询产品ID
void queryProductId(void);

// 查询产品SN
void queryProductSN(void);

// 创建主控版本数据
void createMainCtrlVer(uint8_t (*data)[10]);

// 主控版本上报
void reportMainCtrlVer(uint8_t* data, uint16_t dataLen);

// 获取支持配网模式
ConfigNetMode getSupportNetMode(CloudComFrame* frame);

// 查询支持配网模式
void querySupportSetNetMode(void);

// 创建主控上行cmd+data
uint16_t createCmdData(uint8_t* srcData, uint8_t* dstData);

// 主控上行cmd+data
void sendMainCtrlCmdData(uint8_t* data, uint16_t dataLen);

// ITV数据状态变化更新上报,分割为FRAME_DATA_LEN的数据上报
ITV_INDEX_ARRAY getFreq_ITV_Index_ArrayLimitData(void);

// 状态变化更新指令
void sendStatusChange(uint8_t* data, uint16_t dataLen, uint16_t frameNo);


/*------------------------------------------网络模块请求主控---------------------------------------*/

// 回复网络模块查询主控状态
void responseForQueryMainCtrlStatus(void);

// 回复网络模块标准属性控制
void responseForPropertyCtrl(uint8_t* data, uint16_t dataLen);

// 解析网络模块ITV数据
ITV_INDEX_ARRAY parseNetModuleITV(uint8_t* data, uint16_t totalDataLen);

// 回复联网状态指令
void responseForNetStatus(void);

// 回复恢复出厂设置
void responseForFactoryReset(void);

// 回复设置主控时间
void responseForSetMainCtrlTime(void);

// 校验cmd+data数据合理性
uint8_t checkCmdDataValid(uint8_t* data, uint16_t dataLen);

// 回复网络模块cmd+data下行
void responseForCmdData(uint8_t result);

// 获取测试结果
TestResult getTestResult(CloudComFrame* frame);

// 回复网络模块发送测试结果
void responseForTest(void);

// 上传云端通信错误
void reportCloudCommErr(void);

// 查询主控版本
void responseMainCtrlVer(uint8_t* data, uint16_t dataLen);

/*---------------------------------------升级流程----------------------------------------------*/

// 通知主控有升级
void responseMainCtrlUpdate(uint8_t* data, uint16_t dataLen);

// 请求开始升级
void requestStartUpdate(uint8_t* data, uint16_t dataLen);

// 升级信息校验
uint8_t checkUpdateInfo(uint8_t *data);

// 请求发送升级数据包
void requestSendUpdatePack(uint8_t* data, uint16_t dataLen, uint16_t frameNo);

// 发送升级结果
void sendUpdateResult(uint8_t result);

// 回复升级结果
void responseUpdateResult(void);

/*-----------------------------------------通信初始化------------------------------------------*/

// 创建通信消息队列
void createCloudComMsgQueue(void);

// 串口0同wifi/bt通信任务
void vCloudComTaskHandleServer(void *pvParameters);

// 云通信任务初始化
void vCloudComTaskInit(void);

// 给wifi/bt发送数据
void sendDataToWifi(void);

// 接收wifi/bt的数据
void receiveWifiData(void);

// 解析帧数据
CloudComFrame parseFrameData(void);

// 帧校验
uint8_t frameCheck(uint8_t *data, uint16_t dataLen);

// 协议解析
void protocolParse(CloudComFrame* frame);

// 心跳包定时回调
void vHeartPackTimer(TimerHandle_t xTimer);

// 消息重发回调
void retryTimer(TimerHandle_t xTimer);

// itv定时回调
void itvTimer(TimerHandle_t xTimer);

// freq itv定时回调
void freqItvTimer(TimerHandle_t xTimer);

// 打开上报定时器开关
void openReportSwitchTimer(TimerHandle_t xTimer);

// 定时器初始化
void cloudComTimerInit(void);

// 打印帧数据
void printfFrame(CloudComFrame* frame, uint8_t isRecv);

// 小端数据转化为大端数据
void smallEndianToBigEndian(uint8_t* data, uint8_t dataLen);

// 帧数据大小端转换
void frameDataToBigEndian(CloudComFrame* frame);

// 上电初始化数据发送
void sendDataForStartUp(void);

// 获取升级类型  0:不在升级中  1: 主控升级中  2: wifi升级中
uint8_t getUpdateType(void);

#endif






/*********************************************************************
 * 内容摘要: 主控同wifi通信处理
 * 其它说明: 无
 * 当前版本: V1.0
 * 作    者:  
 * 完成日期: 2022/11/11
 **********************************************************************/
#include "cloudTask.h"
#include "cloud_uart.h"
#include "log.h"
#include "cloudIap.h"
#include "main.h"


// 在cloudOpe.c中定义
extern void vSetItvData(uint8_t* itvData, uint16_t itvDataLen, uint8_t isResponse);

// ITV全局变量
tVitaDataDef tVitaData = {0};
// 上一次的ITV静态全局变量
static tVitaDataDef  lastVitaData = {0};
// 记录设置的ITV静态全局变量
static tVitaDataDef stSetVitaData = {0};

// 联网信息
NetInfo netInfo = {0};
// 发送产品ID结果
ProductId productId = {0};
// mac和version
MacVer macVer = {0};
// 网络时间
MainCtrlTime netTime = {0};
// SN
SN  sn = {0};
// 配网模式
ConfigNetMode netMode = {0};
// 测试结果
TestResult testResult = {0};
// 主控版本
MainCtrlVer mainCtrlVer = {0};

const tITVDef tITVDataDevice[] = 
{
   /*0x01*/ {ITV_ID_DEVIC_TYPE, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8DevicType)}, {.pu8Data = &(lastVitaData.u8DevicType)}, {.pu8Data = &(stSetVitaData.u8DevicType)}},  ///主控类型
   /*0x02*/ {ITV_ID_DTC, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16DTC)}, {.pu16Data = &(lastVitaData.u16DTC)}, {.pu16Data = &(stSetVitaData.u16DTC)}},  ///Device Type Code
   /*0x03*/ {ITV_ID_INV_VERH, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16InvVerH)}, {.pu16Data = &(lastVitaData.u16InvVerH)}, {.pu16Data = &(stSetVitaData.u16InvVerH)}},  ///逆变器固件版本高位
   /*0x04*/ {ITV_ID_INV_VERM, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16InvVerM)}, {.pu16Data = &(lastVitaData.u16InvVerM)}, {.pu16Data = &(stSetVitaData.u16InvVerM)}},  ///逆变器固件版本中位
   /*0x05*/ {ITV_ID_INV_VERL, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16InvVerL)}, {.pu16Data = &(lastVitaData.u16InvVerL)}, {.pu16Data = &(stSetVitaData.u16InvVerL)}},  ///逆变器固件版本低位
   /*0x06*/ {ITV_ID_MAIN_VERH, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16MainVerH)}, {.pu16Data = &(lastVitaData.u16MainVerH)}, {.pu16Data = &(stSetVitaData.u16MainVerH)}},  ///主控固件版本高位
   /*0x07*/ {ITV_ID_MAIN_VERM, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16MainVerM)}, {.pu16Data = &(lastVitaData.u16MainVerM)}, {.pu16Data = &(stSetVitaData.u16MainVerM)}},  ///主控固件版本中位
   /*0x08*/ {ITV_ID_MAIN_VERL, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16MainVerL)}, {.pu16Data = &(lastVitaData.u16MainVerL)}, {.pu16Data = &(stSetVitaData.u16MainVerL)}},  ///主控固件版本低位
   /*0x09*/ {ITV_ID_HARD_VERH, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16HardVerH)}, {.pu16Data = &(lastVitaData.u16HardVerH)}, {.pu16Data = &(stSetVitaData.u16HardVerH)}},  ///主控硬件版本高位
   /*0x0A*/ {ITV_ID_HARD_VERM, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16HardVerM)}, {.pu16Data = &(lastVitaData.u16HardVerM)}, {.pu16Data = &(stSetVitaData.u16HardVerM)}},  ///主控硬件版本中位
   /*0x0B*/ {ITV_ID_HARD_VERL, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16HardVerL)}, {.pu16Data = &(lastVitaData.u16HardVerL)}, {.pu16Data = &(stSetVitaData.u16HardVerL)}},  ///主控硬件版本低位
   /*0X0C*/ {ITV_ID_SN, ITV_TYPE_STRING, {.ptStringData = tVitaData.tSN}, {.ptStringData = lastVitaData.tSN}, {.ptStringData = stSetVitaData.tSN}},  ///主控序列号
   /*0X0D*/ {ITV_ID_INV_TYPE, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8InvType)}, {.pu8Data = &(lastVitaData.u8InvType)}, {.pu8Data = &(stSetVitaData.u8InvType)}},  ///逆变器类型
   /*0X0E*/ {ITV_ID_P_RATE, ITV_TYPE_U32, {.pu32Data = &(tVitaData.u32PRate)}, {.pu32Data = &(lastVitaData.u32PRate)}, {.pu32Data = &(stSetVitaData.u32PRate)}},  ///额定功率
   /*0X0F*/ {ITV_ID_INV_CAP_RATE, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8InvNomCap)}, {.pu8Data = &(lastVitaData.u8InvNomCap)}, {.pu8Data = &(stSetVitaData.u8InvNomCap)}},  ///逆变器标称容量
   /*0X10*/ {ITV_ID_INV_SLEEP_TIME, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16InvSleepTime)}, {.pu16Data = &(lastVitaData.u16InvSleepTime)}, {.pu16Data = &(stSetVitaData.u16InvSleepTime)}},  ///逆变器休眠时间设置
   /*0X11*/ {ITV_ID_DISP_SLEEP_TIMER, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8DispSleepTime)}, {.pu8Data = &(lastVitaData.u8DispSleepTime)}, {.pu8Data = &(stSetVitaData.u8DispSleepTime)}},  ///屏幕休眠时间设置
   /*0X12*/ {ITV_ID_OUT_TYPE, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8OutPowerType)}, {.pu8Data = &(lastVitaData.u8OutPowerType)}, {.pu8Data = &(stSetVitaData.u8OutPowerType)}},  ///输出电压类型
   /*0X13*/ {ITV_ID_FREQ_TYPE, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8OutFreqType)}, {.pu8Data = &(lastVitaData.u8OutFreqType)}, {.pu8Data = &(stSetVitaData.u8OutFreqType)}},  ///输出频率类型
   /*0X14*/ {ITV_ID_CHARGE_IMAX, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16ChargeCurrMax)}, {.pu16Data = &(lastVitaData.u16ChargeCurrMax)}, {.pu16Data = &(stSetVitaData.u16ChargeCurrMax)}},  ///"Max Charge Current最大充电电流"
   /*0X15*/ {ITV_ID_CHARGE_VMAX, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16ChargeVolt)}, {.pu16Data = &(lastVitaData.u16ChargeVolt)}, {.pu16Data = &(stSetVitaData.u16ChargeVolt)}},  ///"Bulk Charge Volt充电恒压点"
   /*0X16*/ {ITV_ID_CHARGE_VFLOAT, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16FloatChargeVolt)}, {.pu16Data = &(lastVitaData.u16FloatChargeVolt)}, {.pu16Data = &(stSetVitaData.u16FloatChargeVolt)}},  ///"Float Charge Volt充电浮充点"
   /*0X17*/ {ITV_ID_ACCHARGE_IMAX, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16IAcChargeCurrMax)}, {.pu16Data = &(lastVitaData.u16IAcChargeCurrMax)}, {.pu16Data = &(stSetVitaData.u16IAcChargeCurrMax)}},  ///"Max Ac charge current最大市电充电电流"
   /*0X18*/ {ITV_ID_CHARGE_PMAX, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8PChargePowerMax)}, {.pu8Data = &(lastVitaData.u8PChargePowerMax)}, {.pu8Data = &(stSetVitaData.u8PChargePowerMax)}},  ///"Max charge watt充电功率最大值"
   /*0X19*/ {ITV_ID_BAT_OFF, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16SocBatOff)}, {.pu16Data = &(lastVitaData.u16SocBatOff)}, {.pu16Data = &(stSetVitaData.u16SocBatOff)}},  ///"Bat cut off voltage or SOC关机电压或关机的电池容量"
   /*0X1A*/ {ITV_ID_BLIGHT_DELAY, ITV_TYPE_FLOAT, {.pfData = &(tVitaData.fBLightDelay)}, {.pfData = &(lastVitaData.fBLightDelay)}, {.pfData = &(stSetVitaData.fBLightDelay)}},  ///背光延时
   /*0X1B*/ {ITV_ID_DNS, ITV_TYPE_STRING, {.ptStringData = tVitaData.tDNS}, {.ptStringData = lastVitaData.tDNS}, {.ptStringData = stSetVitaData.tDNS}},  ///域名系统
   /*0X1C*/ {ITV_ID_IP, ITV_TYPE_STRING, {.ptStringData = tVitaData.tIP}, {.ptStringData = lastVitaData.tIP}, {.ptStringData = stSetVitaData.tIP}},  ///IP地址
   /*0X1D*/ {ITV_ID_PORT, ITV_TYPE_STRING, {.ptStringData = tVitaData.tPort}, {.ptStringData = lastVitaData.tPort}, {.ptStringData = stSetVitaData.tPort}},  ///端口号
   /*0X1E*/ {ITV_ID_MAC, ITV_TYPE_STRING, {.ptStringData = tVitaData.tMac}, {.ptStringData = lastVitaData.tMac}, {.ptStringData = stSetVitaData.tMac}},  ///Mac地址
   /*0X1F*/ {ITV_ID_REMOTE_IP, ITV_TYPE_STRING, {.ptStringData = tVitaData.tRemoteIP}, {.ptStringData = lastVitaData.tRemoteIP}, {.ptStringData = stSetVitaData.tRemoteIP}},  ///远端IP地址
   /*0X20*/ {ITV_ID_REMOTE_PORT, ITV_TYPE_STRING, {.ptStringData = tVitaData.tRemotePort}, {.ptStringData = lastVitaData.tRemotePort}, {.ptStringData = stSetVitaData.tRemotePort}},  ///远端端口号
   /*0X21*/ {ITV_ID_REMOTE_URL, ITV_TYPE_STRING, {.ptStringData = tVitaData.tRemoteURL}, {.ptStringData = lastVitaData.tRemoteURL}, {.ptStringData = stSetVitaData.tRemoteURL}},  ///远端域名
   /*0X22*/ {ITV_ID_SUB_MASK, ITV_TYPE_STRING, {.ptStringData = tVitaData.tSubnetMask}, {.ptStringData = lastVitaData.tSubnetMask}, {.ptStringData = stSetVitaData.tSubnetMask}},  ///子网掩码 
   /*0X23*/ {ITV_ID_DEF_GATEWAY, ITV_TYPE_STRING, {.ptStringData = tVitaData.tDefaultGateway}, {.ptStringData = lastVitaData.tDefaultGateway}, {.ptStringData = stSetVitaData.tDefaultGateway}},  ///默认网关 
   /*0X24*/ {ITV_ID_BT_MAC, ITV_TYPE_STRING, {.ptStringData = tVitaData.tBTMAC}, {.ptStringData = lastVitaData.tBTMAC}, {.ptStringData = stSetVitaData.tBTMAC}},  ///蓝牙的 MAC 地址 
   /*0X25*/ {ITV_ID_BLE_TOKEN, ITV_TYPE_STRING, {.ptStringData = tVitaData.tBLETOKEN}, {.ptStringData = lastVitaData.tBLETOKEN}, {.ptStringData = stSetVitaData.tBLETOKEN}},  ///蓝牙密钥
   /*0X26*/ {ITV_ID_WIFI_SSID, ITV_TYPE_STRING, {.ptStringData = tVitaData.tWiFiSSID}, {.ptStringData = lastVitaData.tWiFiSSID}, {.ptStringData = stSetVitaData.tWiFiSSID}},  ///WIFI 模块要链接热点名称 
   /*0X27*/ {ITV_ID_WIFI_PASSWORD, ITV_TYPE_STRING, {.ptStringData = tVitaData.tWiFiPassword}, {.ptStringData = lastVitaData.tWiFiPassword}, {.ptStringData = stSetVitaData.tWiFiPassword}},  ///WIFI 模块要链接的热点密钥
   /*0X28*/ {ITV_ID_WIFI_CONFIG, ITV_TYPE_S8A, {.ptStringData = tVitaData.s8WiFiConfig}, {.ptStringData = lastVitaData.s8WiFiConfig}, {.ptStringData = stSetVitaData.s8WiFiConfig}},  ///WIFI 模块相关配置信息 
   /*0X29*/ {ITV_ID_WIFI_MODE, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8WifiMode)}, {.pu8Data = &(lastVitaData.u8WifiMode)}, {.pu8Data = &(stSetVitaData.u8WifiMode)}},  ///Wifi 模式切换 
   /*0X2A*/ {ITV_ID_DHCP_EN, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8DHCPEn )}, {.pu8Data = &(lastVitaData.u8DHCPEn )}, {.pu8Data = &(stSetVitaData.u8DHCPEn )}},  ///DHCP 使能 
   /*0X2B*/ {ITV_ID_WIFI_STATE, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8WIFIConnectState)}, {.pu8Data = &(lastVitaData.u8WIFIConnectState)}, {.pu8Data = &(stSetVitaData.u8WIFIConnectState)}},  ///Wifi 连接状态 
   /*0X2C*/ {ITV_ID_OTA_URL, ITV_TYPE_STRING, {.ptStringData = &(tVitaData.tOTAURL[0])}, {.ptStringData = &(lastVitaData.tOTAURL[0])}, {.ptStringData = &(stSetVitaData.tOTAURL[0])}},  ///OTA升级路径
   /*0X2D*/ {ITV_ID_OTA_STEP, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8OTAStep)}, {.pu8Data = &(lastVitaData.u8OTAStep)}, {.pu8Data = &(stSetVitaData.u8OTAStep)}},  ///OTA升级进度 
   /*0X2E*/ {ITV_ID_OTA_STATE, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8OTAState)}, {.pu8Data = &(lastVitaData.u8OTAState)}, {.pu8Data = &(stSetVitaData.u8OTAState)}},  ///OTA升级状态
   /*0X2F*/ {ITV_ID_RESET_EN, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8RestartEn)}, {.pu8Data = &(lastVitaData.u8RestartEn)}, {.pu8Data = &(stSetVitaData.u8RestartEn)}},  ///重启开关使能
   /*0X30*/ {ITV_ID_FUNC_EN, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8FuncEN)}, {.pu8Data = &(lastVitaData.u8FuncEN)}, {.pu8Data = &(stSetVitaData.u8FuncEN)}},  ///功能开关
   /*0X31*/ {ITV_ID_SYS_TIME, ITV_TYPE_U32, {.pu32Data = &(tVitaData.u32SysTime)}, {.pu32Data = &(lastVitaData.u32SysTime)}, {.pu32Data = &(stSetVitaData.u32SysTime)}},  ///系统时间
   /*0X32*/ {ITV_ID_TIME_OFFSET, ITV_TYPE_S32, {.ps32Data = &(tVitaData.s32TimeOffset)}, {.ps32Data = &(lastVitaData.s32TimeOffset)}, {.ps32Data = &(stSetVitaData.s32TimeOffset)}},  ///时区偏移
   /*0X33*/ {ITV_ID_FUNC_CMD, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8CmdFunc)}, {.pu8Data = &(lastVitaData.u8CmdFunc)}, {.pu8Data = &(stSetVitaData.u8CmdFunc)}},  ///工作命令
   /*0X34*/ {ITV_ID_OUT_EN, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8OutputEn)}, {.pu8Data = &(lastVitaData.u8OutputEn)}, {.pu8Data = &(stSetVitaData.u8OutputEn)}},  ///输出开关
   /*0X35*/ {ITV_ID_NET_EN, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16WifiBLESw)}, {.pu16Data = &(lastVitaData.u16WifiBLESw)}, {.pu16Data = &(stSetVitaData.u16WifiBLESw)}},  ///网络开关
   /*0X36*/ {ITV_ID_SYS_STATE, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8SysStatus)}, {.pu8Data = &(lastVitaData.u8SysStatus)}, {.pu8Data = &(stSetVitaData.u8SysStatus)}},  ///系统状态机
   /*0X37*/ {ITV_ID_P_IN_SUM, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16PInSum)}, {.pu16Data = &(lastVitaData.u16PInSum)}, {.pu16Data = &(stSetVitaData.u16PInSum)}},  ///输入总功率
   /*0X38*/ {ITV_ID_V_ACIN, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16VACIn)}, {.pu16Data = &(lastVitaData.u16VACIn)}, {.pu16Data = &(stSetVitaData.u16VACIn)}},  ///输入AC电源电压
   /*0X39*/ {ITV_ID_FREQ_ACIN, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16FreqACIn)}, {.pu16Data = &(lastVitaData.u16FreqACIn)}, {.pu16Data = &(stSetVitaData.u16FreqACIn)}},  ///输入AC电源频率
   /*0X3A*/ {ITV_ID_P_ACIN, ITV_TYPE_U32, {.pu32Data = &(tVitaData.u32PACIn)}, {.pu32Data = &(lastVitaData.u32PACIn)}, {.pu32Data = &(stSetVitaData.u32PACIn)}},  ///"AC input watt (low)市电输入功率"
   /*0X3B*/ {ITV_ID_V_PV, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16VPv1)}, {.pu16Data = &(lastVitaData.u16VPv1)}, {.pu16Data = &(stSetVitaData.u16VPv1)}},  ///"PV1 voltagePV(光伏)电压"
   /*0X3C*/ {ITV_ID_P_PV_CHARGE, ITV_TYPE_U32, {.pu32Data = &(tVitaData.u32PPvCharge)}, {.pu32Data = &(lastVitaData.u32PPvCharge)}, {.pu32Data = &(stSetVitaData.u32PPvCharge)}},  ///"PV charge powerPV(光伏)充电功率"
   /*0X3D*/ {ITV_ID_I_PV_CHARGE, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16IPvCharge)}, {.pu16Data = &(lastVitaData.u16IPvCharge)}, {.pu16Data = &(stSetVitaData.u16IPvCharge)}},  ///"Buck1 currentPV充电电流"
   /*0X3E*/ {ITV_ID_V_BAT, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16VBat)}, {.pu16Data = &(lastVitaData.u16VBat)}, {.pu16Data = &(stSetVitaData.u16VBat)}},  ///电池电压
   /*0X3F*/ {ITV_ID_SOC_BAT, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8BatSoc)}, {.pu8Data = &(lastVitaData.u8BatSoc)}, {.pu8Data = &(stSetVitaData.u8BatSoc)}},  ///电池容量百分比
   /*0X40*/ {ITV_ID_P_CHARGE_SUM, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16PChargeSum)}, {.pu16Data = &(lastVitaData.u16PChargeSum)}, {.pu16Data = &(stSetVitaData.u16PChargeSum)}},  ///充电总功率
   /*0X41*/ {ITV_ID_I_CHARGE_SUM, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16IChargeSum)}, {.pu16Data = &(lastVitaData.u16IChargeSum)}, {.pu16Data = &(stSetVitaData.u16IChargeSum)}},  ///充电总电流
   /*0X42*/ {ITV_ID_P_AC_CHARGE, ITV_TYPE_U32, {.pu32Data = &(tVitaData.u32PACCharge)}, {.pu32Data = &(lastVitaData.u32PACCharge)}, {.pu32Data = &(stSetVitaData.u32PACCharge)}},  ///市电充电功率
   /*0X43*/ {ITV_ID_I_AC_CHARGE, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16IACCharge)}, {.pu16Data = &(lastVitaData.u16IACCharge)}, {.pu16Data = &(stSetVitaData.u16IACCharge)}},  ///市电充电电流
   /*0X44*/ {ITV_ID_RELEASE_CHARGE_TIME, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16ReleaseChargeTime)}, {.pu16Data = &(lastVitaData.u16ReleaseChargeTime)}, {.pu16Data = &(stSetVitaData.u16ReleaseChargeTime)}},  ///剩余充电时间
   /*0X45*/ {ITV_ID_ELEASE_DISCHARGE_TIME, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16ReleaseDisChargeTime)}, {.pu16Data = &(lastVitaData.u16ReleaseDisChargeTime)}, {.pu16Data = &(stSetVitaData.u16ReleaseDisChargeTime)}},  ///剩余放电时间
   /*0X46*/ {ITV_ID_P_AC_DISCHARGE, ITV_TYPE_U32, {.pu32Data = &(tVitaData.u32PACDisCharge)}, {.pu32Data = &(lastVitaData.u32PACDisCharge)}, {.pu32Data = &(stSetVitaData.u32PACDisCharge)}},  ///市电放电功率
   /*0X47*/ {ITV_ID_P_BAT_DISCHARGE, ITV_TYPE_U32, {.pu32Data = &(tVitaData.u32PBatDisCharge)}, {.pu32Data = &(lastVitaData.u32PBatDisCharge)}, {.pu32Data = &(stSetVitaData.u32PBatDisCharge)}},  ///电池放电功率
   /*0X48*/ {ITV_ID_I_BAT_DISCHARGE, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16IBatDisCharge)}, {.pu16Data = &(lastVitaData.u16IBatDisCharge)}, {.pu16Data = &(stSetVitaData.u16IBatDisCharge)}},  ///电池放电电流
   /*0X49*/ {ITV_ID_P_BAT, ITV_TYPE_U32, {.pu32Data = &(tVitaData.u32Pbat)}, {.pu32Data = &(lastVitaData.u32Pbat)}, {.pu32Data = &(stSetVitaData.u32Pbat)}},  ///电池功率
   /*0X4A*/ {ITV_ID_P_OUT_SUM, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16POutSum)}, {.pu16Data = &(lastVitaData.u16POutSum)}, {.pu16Data = &(stSetVitaData.u16POutSum)}},  ///输出总功率
   /*0X4B*/ {ITV_ID_I_OUT, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16Iout)}, {.pu16Data = &(lastVitaData.u16Iout)}, {.pu16Data = &(stSetVitaData.u16Iout)}},  ///输出电流
   /*0X4C*/ {ITV_ID_P_INV_OUT, ITV_TYPE_U32, {.pu32Data = &(tVitaData.u32PInvOut)}, {.pu32Data = &(lastVitaData.u32PInvOut)}, {.pu32Data = &(stSetVitaData.u32PInvOut)}},  ///逆变输出功率
   /*0X4D*/ {ITV_ID_I_INV_OUT, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16IInvOut)}, {.pu16Data = &(lastVitaData.u16IInvOut)}, {.pu16Data = &(stSetVitaData.u16IInvOut)}},  ///逆变输出电流
   /*0X4E*/ {ITV_ID_V_AC_OUT, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16VACOut)}, {.pu16Data = &(lastVitaData.u16VACOut)}, {.pu16Data = &(stSetVitaData.u16VACOut)}},  ///输出AC电源电压
   /*0X4F*/ {ITV_ID_FREQ_AC_OUT, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16FreqACOut)}, {.pu16Data = &(lastVitaData.u16FreqACOut)}, {.pu16Data = &(stSetVitaData.u16FreqACOut)}},  ///输出AC电源频率
   /*0X50*/ {ITV_ID_SOC_LOAD, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16LoadSoc)}, {.pu16Data = &(lastVitaData.u16LoadSoc)}, {.pu16Data = &(stSetVitaData.u16LoadSoc)}},  ///负载百分比
   /*0X51*/ {ITV_ID_V_CIGAR_LIGHTER, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16VCarPower)}, {.pu16Data = &(lastVitaData.u16VCarPower)}, {.pu16Data = &(stSetVitaData.u16VCarPower)}},  ///"Car voltage车充点烟器电压"
   /*0X52*/ {ITV_ID_P_CIGAR_LIGHTER, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16PCarPower)}, {.pu16Data = &(lastVitaData.u16PCarPower)}, {.pu16Data = &(stSetVitaData.u16PCarPower)}},  ///"Car Watt车充点烟器功率"
   /*0X53*/ {ITV_ID_I_V_CIGAR_LIGHTER, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16ICarPower)}, {.pu16Data = &(lastVitaData.u16ICarPower)}, {.pu16Data = &(stSetVitaData.u16ICarPower)}},  ///"Car Current车充点烟器电流"
   /*0X54*/ {ITV_ID_V_USB1, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16VUsbOut1)}, {.pu16Data = &(lastVitaData.u16VUsbOut1)}, {.pu16Data = &(stSetVitaData.u16VUsbOut1)}},  ///Usb1电压
   /*0X55*/ {ITV_ID_V_USB2, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16VUsbOut2)}, {.pu16Data = &(lastVitaData.u16VUsbOut2)}, {.pu16Data = &(stSetVitaData.u16VUsbOut2)}},  ///Usb2电压
   /*0X56*/ {ITV_ID_V_USB3, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16VUsbOut3)}, {.pu16Data = &(lastVitaData.u16VUsbOut3)}, {.pu16Data = &(stSetVitaData.u16VUsbOut3)}},  ///Usb3电压
   /*0X57*/ {ITV_ID_V_USB4, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16VUsbOut4)}, {.pu16Data = &(lastVitaData.u16VUsbOut4)}, {.pu16Data = &(stSetVitaData.u16VUsbOut4)}},  ///Usb4电压
   /*0X58*/ {ITV_ID_V_USB5, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16VUsbOut5)}, {.pu16Data = &(lastVitaData.u16VUsbOut5)}, {.pu16Data = &(stSetVitaData.u16VUsbOut5)}},  ///Usb5电压
   /*0X59*/ {ITV_ID_V_USB6, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16VUsbOut6)}, {.pu16Data = &(lastVitaData.u16VUsbOut6)}, {.pu16Data = &(stSetVitaData.u16VUsbOut6)}},  ///Usb6电压
   /*0X5A*/ {ITV_ID_I_USB1, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16IUsbOut1)}, {.pu16Data = &(lastVitaData.u16IUsbOut1)}, {.pu16Data = &(stSetVitaData.u16IUsbOut1)}},  ///Usb1电流
   /*0X5B*/ {ITV_ID_I_USB2, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16IUsbOut2)}, {.pu16Data = &(lastVitaData.u16IUsbOut2)}, {.pu16Data = &(stSetVitaData.u16IUsbOut2)}},  ///Usb2电流
   /*0X5C*/ {ITV_ID_I_USB3, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16IUsbOut3)}, {.pu16Data = &(lastVitaData.u16IUsbOut3)}, {.pu16Data = &(stSetVitaData.u16IUsbOut3)}},  ///Usb3电流
   /*0X5D*/ {ITV_ID_I_USB4, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16IUsbOut4)}, {.pu16Data = &(lastVitaData.u16IUsbOut4)}, {.pu16Data = &(stSetVitaData.u16IUsbOut4)}},  ///Usb4电流
   /*0X5E*/ {ITV_ID_I_USB5, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16IUsbOut5)}, {.pu16Data = &(lastVitaData.u16IUsbOut5)}, {.pu16Data = &(stSetVitaData.u16IUsbOut5)}},  ///Usb5电流
   /*0X5F*/ {ITV_ID_I_USB6, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16IUsbOut6)}, {.pu16Data = &(lastVitaData.u16IUsbOut6)}, {.pu16Data = &(stSetVitaData.u16IUsbOut6)}},  ///Usb6电流
   /*0X60*/ {ITV_ID_P_USB1, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16PUsbOut1)}, {.pu16Data = &(lastVitaData.u16PUsbOut1)}, {.pu16Data = &(stSetVitaData.u16PUsbOut1)}},  ///Usb1功率
   /*0X61*/ {ITV_ID_P_USB2, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16PUsbOut2)}, {.pu16Data = &(lastVitaData.u16PUsbOut2)}, {.pu16Data = &(stSetVitaData.u16PUsbOut2)}},  ///Usb2功率
   /*0X62*/ {ITV_ID_P_USB3, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16PUsbOut3)}, {.pu16Data = &(lastVitaData.u16PUsbOut3)}, {.pu16Data = &(stSetVitaData.u16PUsbOut3)}},  ///Usb3功率
   /*0X63*/ {ITV_ID_P_USB4, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16PUsbOut4)}, {.pu16Data = &(lastVitaData.u16PUsbOut4)}, {.pu16Data = &(stSetVitaData.u16PUsbOut4)}},  ///Usb4功率
   /*0X64*/ {ITV_ID_P_USB5, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16PUsbOut5)}, {.pu16Data = &(lastVitaData.u16PUsbOut5)}, {.pu16Data = &(stSetVitaData.u16PUsbOut5)}},  ///Usb5功率
   /*0X65*/ {ITV_ID_P_USB6, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16PUsbOut6)}, {.pu16Data = &(lastVitaData.u16PUsbOut6)}, {.pu16Data = &(stSetVitaData.u16PUsbOut6)}},  ///Usb6功率
   /*0X66*/ {ITV_ID_P_OP_DC, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16POpDc)}, {.pu16Data = &(lastVitaData.u16POpDc)}, {.pu16Data = &(stSetVitaData.u16POpDc)}},  ///无线充电功率
   /*0X67*/ {ITV_ID_OUT_STATE, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8OutputStatus)}, {.pu8Data = &(lastVitaData.u8OutputStatus)}, {.pu8Data = &(stSetVitaData.u8OutputStatus)}},  ///输出开关状态
   /*0X68*/ {ITV_ID_TEMP_INV, ITV_TYPE_S16, {.ps16Data = &(tVitaData.s16TempInv)}, {.ps16Data = &(lastVitaData.s16TempInv)}, {.ps16Data = &(stSetVitaData.s16TempInv)}},  ///逆变散热片温度
   /*0X69*/ {ITV_ID_TEMP_BAT, ITV_TYPE_S16, {.ps16Data = &(tVitaData.s16TempBat)}, {.ps16Data = &(lastVitaData.s16TempBat)}, {.ps16Data = &(stSetVitaData.s16TempBat)}},  ///电池内部温度
   /*0X6A*/ {ITV_ID_TEMP_PD1, ITV_TYPE_S16, {.ps16Data = &(tVitaData.s16TempPD1)}, {.ps16Data = &(lastVitaData.s16TempPD1)}, {.ps16Data = &(stSetVitaData.s16TempPD1)}},  ///PD板1温度
   /*0X6B*/ {ITV_ID_TEMP_PD2, ITV_TYPE_S16, {.ps16Data = &(tVitaData.s16TempPD2)}, {.ps16Data = &(lastVitaData.s16TempPD2)}, {.ps16Data = &(stSetVitaData.s16TempPD2)}},  ///PD板2温度
   /*0X6C*/ {ITV_ID_TEMP_PV, ITV_TYPE_S16, {.ps16Data = &(tVitaData.s16TempPV)}, {.ps16Data = &(lastVitaData.s16TempPV)}, {.ps16Data = &(stSetVitaData.s16TempPV)}},  ///PV(光伏)温度
   /*0X6D*/ {ITV_ID_TEMP_TR, ITV_TYPE_S16, {.ps16Data = &(tVitaData.s16TempTr)}, {.ps16Data = &(lastVitaData.s16TempTr)}, {.ps16Data = &(stSetVitaData.s16TempTr)}},  ///变压器温度
   /*0X6E*/ {ITV_ID_TEMP_DC, ITV_TYPE_S16, {.ps16Data = &(tVitaData.s16TempDc)}, {.ps16Data = &(lastVitaData.s16TempDc)}, {.ps16Data = &(stSetVitaData.s16TempDc)}},  ///"DC-DC Temperature电池散热片温度"
   /*0X6F*/ {ITV_ID_ERROR, ITV_TYPE_U32, {.pu32Data = &(tVitaData.u32ErrorBit)}, {.pu32Data = &(lastVitaData.u32ErrorBit)}, {.pu32Data = &(stSetVitaData.u32ErrorBit)}},  ///错误码
   /*0X70*/ {ITV_ID_WARNING, ITV_TYPE_U32, {.pu32Data = &(tVitaData.u32WarningBit)}, {.pu32Data = &(lastVitaData.u32WarningBit)}, {.pu32Data = &(stSetVitaData.u32WarningBit)}},  ///警告码
   /*0X71*/ {ITV_ID_ERROR_BMS, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16ErrorBmsBit)}, {.pu16Data = &(lastVitaData.u16ErrorBmsBit)}, {.pu16Data = &(stSetVitaData.u16ErrorBmsBit)}},  ///Bms出错
   /*0x72*/ {ITV_ID_BLIGHT_DUTY, ITV_TYPE_U16, {.pu16Data = &(tVitaData.u16BLightDuty)},{.pu16Data = &(lastVitaData.u16BLightDuty)}, {.pu16Data = &(stSetVitaData.u16BLightDuty)}},  背光占空比
   /*0x73*/ {ITV_ID_POWER_CMD, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8PowerCmd)},{.pu8Data = &(lastVitaData.u8PowerCmd)},{.pu8Data = &(stSetVitaData.u8PowerCmd)}},       电源输出命令
   /*0x74*/ {ITV_ID_LED_STATE, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8LedState)},{.pu8Data = &(lastVitaData.u8LedState)},{.pu8Data = &(stSetVitaData.u8LedState)}},      // Led灯状态    
   /*0x75*/ {ITV_ID_POWER_BOOST_EN, ITV_TYPE_U8, {.pu8Data = &(tVitaData.u8PowerBoostEn)},{.pu8Data = &(lastVitaData.u8PowerBoostEn)},{.pu8Data = &(stSetVitaData.u8PowerBoostEn)}},     // 降额带载使能开关
    {0}
};

extern uint8_t UART0_TX_BUF[UART0_TX_LEN];
extern uint8_t DMA_BUF_BUSY;
extern uint8_t UART0_RX_BUF[UART0_RX_LEN];
extern uint8_t UART0_RX_STAT;
extern uint32_t UART0_RX_NUM;

// ITV index
static uint16_t itv_index = ITV_ID_DEVIC_TYPE;          // 用于ITV全部数据分段上报
// 升级信息
static UpdateInfo updateInfo = {0};         // 用于保存升级配置信息
// OTA 升级包接收长度
static uint32_t recvDataLen = 0;
// 升级类型 0: 不在升级中 01:主控 02:wifi
static volatile uint8_t updateType = 0;  // 区分升级类型
// 是否已经擦除app备份区flash
static volatile uint8_t isBackUpAppErased = 0;
// 是否上报itv数据
static uint8_t isReportItv = 1;      // 用于升级停止上报ITV数据

#define COMPARE_PART_DATA       // 用于状态更新上报  0x81指令
#ifdef COMPARE_PART_DATA
static const ITV_INDEX_ARRAY constFreqItvIndexArray = 
{
    .indexArr = 
    {
        ITV_ID_INV_SLEEP_TIME,
        ITV_ID_DISP_SLEEP_TIMER,
        ITV_ID_BLIGHT_DELAY,
        ITV_ID_FREQ_TYPE,
        ITV_ID_CHARGE_PMAX,
        ITV_ID_BAT_OFF,
        ITV_ID_FUNC_EN,
        ITV_ID_FUNC_CMD,
        ITV_ID_NET_EN,
        ITV_ID_OUT_STATE,
        ITV_ID_BLIGHT_DUTY,
        ITV_ID_POWER_CMD,
        ITV_ID_LED_STATE,
        ITV_ID_POWER_BOOST_EN,
        ITV_ID_ERROR,
        ITV_ID_WARNING,
        ITV_ID_ERROR_BMS
    },

    .indexArrLen = 17
};
#endif
// 状态频繁更新上报数组
static ITV_INDEX_ARRAY freqItvIndexArray = {0};
// 状态频繁更新上报数组index
static uint16_t freq_itv_index = 0;

/**********************************************************************
 * 功能描述: 给wifi发送帧数据
 * 输入参数: frame: 帧数据指针
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
static void sendQueueHandle(CloudComFrame *frame)
{
    BaseType_t ret = xQueueSend(cloudComSendQueueHandle, frame, 60/portTICK_PERIOD_MS);  // 队列满,等待60ms
    if (ret != pdTRUE) 
    {
        yl_printf("cloudComSendQueue Full!");
    }
}

/**********************************************************************
 * 功能描述: 给wifi发送帧数据,插入队列头部
 * 输入参数: frame: 帧数据指针
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
static void sendQueueFrontHandle(CloudComFrame *frame)
{
    BaseType_t ret = xQueueSendToFront(cloudComSendQueueHandle, frame, 60/portTICK_PERIOD_MS);  // 队列满,等待60ms
    if (ret != pdTRUE) 
    {
        yl_printf("cloudComSendQueue Full!");
    }
}

/**********************************************************************
 * 功能描述: 主控发送配网指令
 * 输入参数: type:{0x01: 一键配网  0x02: AP配网  0x06: WPS联网方式  0x07: 蓝牙配网}
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void startSetNet(uint8_t type)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 10;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x01;
    frame.frameData[0] = type;
    frame.frameDataLen = 1;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 查询联网状态和信号强度
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void queryNetInfo(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x02;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 获取联网状态和信号强度
 * 输入参数: frame: 帧数据指针
 * 输出参数: 无
 * 返 回 值: 网络状态信息
 * 其它说明: 无
 ***********************************************************************/
NetInfo getNetInfo(CloudComFrame* frame)
{
    NetInfo data = {0};
    if (frame->frameDataLen != 2) 
    {
        yl_printf("getNetInfo Failed By Err: %d \r\n", frame->frameDataLen);
        return data;
    }

    data.netStatus = frame->frameData[0];
    data.signalPower = frame->frameData[1];
    return data;
}

/**********************************************************************
 * 功能描述: 构建产品id
 * 输入参数: 无
 * 输出参数: pd: 产品id字节数组
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void createProductId(uint8_t (*pd)[32])
{
    if (pd == NULL) 
    {
        return;
    }

    memcpy(&(*pd)[0], productId.proId, sizeof(productId.proId));
}

/**********************************************************************
 * 功能描述: 发送产品ID
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void sendProductId(uint8_t* data, uint16_t dataLen)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+dataLen;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x03;
    memset(frame.frameData, 0, sizeof(frame.frameData));
    memcpy(frame.frameData, data, dataLen);
    frame.frameDataLen = dataLen;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 获取发送产品ID结果
 * 输入参数: frame:帧数据指针
 * 输出参数: 无
 * 返 回 值: 产品ID信息
 * 其它说明: 无
 ***********************************************************************/
ProductId getProductResult(CloudComFrame* frame)
{
    ProductId data = {0};
    if (frame->frameDataLen != 1) 
    {
        yl_printf("getProductResult Failed By Err: %d \r\n", frame->frameDataLen);
        return data;
    }

    data.result = frame->frameData[0];
    return data;
}

/**********************************************************************
 * 功能描述: 启动测试流程
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void startTest(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x04;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 重启网络模块
 * 输入参数: u8Type, 1: 重启网络模块  2:整机关机 3: wifi关机
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void restartNetModule(uint8_t u8Type)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+1;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x05;
    frame.frameData[0] = u8Type;
    frame.frameDataLen = 1;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueFrontHandle(&frame);
}

/**********************************************************************
 * 功能描述: 恢复出厂设置
 * 输入参数: type: {bit0: 清除连接路由配置,连接默认路由;bit1: 清除网络模块云端绑定关系;
 *                  bit2:清空网络模块下面子设备}
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void factoryReset(uint8_t type)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+1;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x06;
    frame.frameData[0] = type;
    frame.frameDataLen = 1;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 查询mac地址和软件版本号
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void queryMacAndSoftVer(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x07;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 获取mac和version
 * 输入参数: frame:帧数据指针
 * 输出参数: 无
 * 返 回 值: Mac地址和版本信息
 * 其它说明: 无
 ***********************************************************************/
MacVer getMacAndSoftVer(CloudComFrame* frame)
{
    MacVer data = {0};
    if (frame->frameDataLen != 10) 
    {
        yl_printf("getMacAndSoftVer Failed By Err: %d \r\n", frame->frameDataLen);
        return data;
    }

    memcpy(data.mac, frame->frameData, sizeof(data.mac));
    memcpy(data.ver, &(frame->frameData[6]), sizeof(data.ver));
    return data;
}

/**********************************************************************
 * 功能描述: 汇报ITV
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度; frameNo: 帧序号
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void sendITV(uint8_t* data, uint16_t dataLen, uint16_t frameNo)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+dataLen;
    frame.frameNo = frameNo;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x08;
    memset(frame.frameData, 0, sizeof(frame.frameData));
    memcpy(frame.frameData, data, dataLen);
    frame.frameDataLen = dataLen;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: ITV数据状态变化更新上报,分割为FRAME_DATA_LEN的数据上报
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: itv下标数组和数组实际长度
 * 其它说明: 无
 ***********************************************************************/
ITV_INDEX_ARRAY getFreq_ITV_Index_ArrayLimitData(void)
{
    ITV_INDEX_ARRAY info = {0};
    uint16_t offset = 0;
    for (; freq_itv_index < freqItvIndexArray.indexArrLen; ++freq_itv_index) 
    {
        // index
        uint16_t index = freqItvIndexArray.indexArr[freq_itv_index];
        if ((offset+sizeof(index)) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
        { 
            return info;
        }

        offset += 2;

        // type
        uint8_t type = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].u8Type;
        if ((offset+sizeof(type)) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
        { 
            return info;
        }

        offset += 1;

        // value
        uint16_t dataLen = 0;
        switch (type) {
            case ITV_TYPE_STRING:
                // 数据长度
                dataLen = strlen(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ptStringData);
                if ((offset+sizeof(dataLen)) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                offset += 2;

                // 数据
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_U8:
                dataLen = sizeof(uint8_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_S16:
                dataLen = sizeof(int16_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_S32:
                dataLen = sizeof(int32_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_U16:
                dataLen = sizeof(uint16_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_U32:
                dataLen = sizeof(uint32_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_FLOAT:
                dataLen = sizeof(float);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_S8:
                dataLen = sizeof(int8_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_S8A:
                dataLen = strlen(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ptStringData);
                if ((offset+sizeof(dataLen)) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                offset += 2;

                // 数据
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_BOOL:
                dataLen = sizeof(uint8_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            default:
                break;
        }
    }

    return info;
}

/**********************************************************************
 * 功能描述: 用于上报全部ITV数据,分割为FRAME_DATA_LEN的数据上报
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: itv下标数组和数组实际长度
 * 其它说明: 无
 ***********************************************************************/
ITV_INDEX_ARRAY getITV_Index_ArrayLimitDataLen(void)
{
    ITV_INDEX_ARRAY info = {0};
    uint16_t offset = 0;
    for (; itv_index < ITV_ID_MAX; ++itv_index) 
    {
        // index
        if ((offset+sizeof(itv_index)) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
        { 
            return info;
        }

        offset += 2;

        // type
        uint8_t type = tITVDataDevice[itv_index-ITV_ID_DEVIC_TYPE].u8Type;
        if ((offset+sizeof(type)) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
        { 
            return info;
        }

        offset += 1;

        // value
        uint16_t dataLen = 0;
        switch (type) {
            case ITV_TYPE_STRING:
                // 数据长度
                dataLen = strlen(tITVDataDevice[itv_index-ITV_ID_DEVIC_TYPE].upData.ptStringData);
                if ((offset+sizeof(dataLen)) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                offset += 2;

                // 数据
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = itv_index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_U8:
                dataLen = sizeof(uint8_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = itv_index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_S16:
                dataLen = sizeof(int16_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = itv_index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_S32:
                dataLen = sizeof(int32_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = itv_index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_U16:
                dataLen = sizeof(uint16_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = itv_index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_U32:
                dataLen = sizeof(uint32_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = itv_index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_FLOAT:
                dataLen = sizeof(float);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = itv_index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_S8:
                dataLen = sizeof(int8_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = itv_index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_S8A:
                dataLen = strlen(tITVDataDevice[itv_index-ITV_ID_DEVIC_TYPE].upData.ptStringData);
                if ((offset+sizeof(dataLen)) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                offset += 2;

                // 数据
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = itv_index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            case ITV_TYPE_BOOL:
                dataLen = sizeof(uint8_t);
                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return info;
                }

                info.indexArr[info.indexArrLen] = itv_index;
                info.indexArrLen++;

                offset += dataLen;
                break;
            default:
                break;
        }
    }

    return info;
}

/*-----------------------------------------------------------------------------
函数目的: 构造ITV数据
参数:     data         返回ITV数据,所以该数据由调用者自己在外部定义,然后传参
          indexArr     传入ITV_ID类型的数组
          len          传入ITV_ID数组的实际大小

警告:     构造ITV数据的大小不能超过FRAME_DATA_LEN
返回值: 成功: 1  失败: 0
-----------------------------------------------------------------------------*/
uint16_t Create_ITV_Data(uint8_t (*data)[FRAME_DATA_LEN], uint16_t indexArr[ITV_ID_MAX], uint16_t len)
{
    if (*data == NULL) 
    {
        yl_printf("Create_ITV_Data Call Failed! \r\n");
        return 0;
    }

    uint16_t i = 0;
    uint16_t offset = 0;
    memset(*data, 0, FRAME_DATA_LEN);
    uint8_t arr[4] = {0};
    for (; i < len; ++i) 
    {
        uint16_t index = indexArr[i];
        if (index < ITV_ID_DEVIC_TYPE || index >= ITV_ID_MAX) 
        {
            return 0;
        }

        uint8_t type = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].u8Type;

        if ((offset+sizeof(index)) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
        { 
            return 0;
        }
        // index大小端转换
        memcpy(arr, &index, sizeof(index));
        smallEndianToBigEndian(arr, sizeof(index));
        memcpy(&(*data)[offset], arr, sizeof(index));

        offset += 2;

        if ((offset+sizeof(type)) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
        { 
            return 0;
        }
        memcpy(&(*data)[offset], &type, sizeof(type));

        offset += 1;

        uint16_t dataLen = 0;
        switch (type) {
            case ITV_TYPE_STRING:
                dataLen = strlen(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ptStringData);
                if ((offset+sizeof(dataLen)) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return 0;
                }

                memcpy(arr, &dataLen, sizeof(dataLen));
                smallEndianToBigEndian(arr, sizeof(dataLen));
                memcpy(&(*data)[offset], arr, sizeof(dataLen));

                offset += 2;

                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return 0;
                }
                memcpy(&(*data)[offset], tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ptStringData, dataLen);
               
                offset += dataLen;
                break;
            case ITV_TYPE_U8:
                dataLen = sizeof(uint8_t);

                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return 0;
                }
                memcpy(&(*data)[offset], tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pu8Data, dataLen);
               
                offset += dataLen;
                break;
            case ITV_TYPE_S16:
                dataLen = sizeof(int16_t);

                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return 0;
                }

                memcpy(arr, tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ps16Data, dataLen);
                smallEndianToBigEndian(arr, dataLen);
                memcpy(&(*data)[offset], arr, dataLen);
               
                offset += dataLen;
                break;
            case ITV_TYPE_S32:
                dataLen = sizeof(int32_t);

                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return 0;
                }

                memcpy(arr, tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ps32Data, dataLen);
                smallEndianToBigEndian(arr, dataLen);
                memcpy(&(*data)[offset], arr, dataLen);
                
                offset += dataLen;
                break;
            case ITV_TYPE_U16:
                dataLen = sizeof(uint16_t);

                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return 0;
                }

                memcpy(arr, tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pu16Data, dataLen);
                smallEndianToBigEndian(arr, dataLen);
                memcpy(&(*data)[offset], arr, dataLen);
                
                offset += dataLen;
                break;
            case ITV_TYPE_U32:
                dataLen = sizeof(uint32_t);

                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return 0;
                }

                memcpy(arr, tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pu32Data, dataLen);
                smallEndianToBigEndian(arr, dataLen);
                memcpy(&(*data)[offset], arr, dataLen);
               
                offset += dataLen;
                break;
            case ITV_TYPE_FLOAT:
                dataLen = sizeof(float);

                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return 0;
                }

                memcpy(arr, tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pfData, dataLen);
                smallEndianToBigEndian(arr, dataLen);
                memcpy(&(*data)[offset], arr, dataLen);
               
                offset += dataLen;
                break;
            case ITV_TYPE_S8:
                dataLen = sizeof(int8_t);

                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return 0;
                }
                memcpy(&(*data)[offset], tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ps8Data, dataLen);
                
                offset += dataLen;
                break;
            case ITV_TYPE_S8A: // 将字节数组转化为字符串,用于确定数据长度
                dataLen = strlen(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ptStringData);
                if ((offset+sizeof(dataLen)) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return 0;
                }
                
                memcpy(arr, &dataLen, sizeof(dataLen));
                smallEndianToBigEndian(arr, sizeof(dataLen));
                memcpy(&(*data)[offset], arr, sizeof(dataLen));

                offset += 2;

                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return 0;
                }
                memcpy(&(*data)[offset], tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ptStringData, dataLen);
               
                offset += dataLen;
                break;
            case ITV_TYPE_BOOL:
                dataLen = sizeof(uint8_t);

                if ((offset+dataLen) > FRAME_DATA_LEN) // 构造ITV数据的大小超过FRAME_DATA_LEN
                { 
                    return 0;
                }
                memcpy(&(*data)[offset], tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pbData, dataLen);
               
                offset += dataLen;
                break;
            default:
                break;
        }
    }

    return offset;
}

/**********************************************************************
 * 功能描述: 获取网络时间
 * 输入参数: frame:帧数据指针
 * 输出参数: 无
 * 返 回 值: 网络时间信息
 * 其它说明: 无
 ***********************************************************************/
MainCtrlTime getNetTimeData(CloudComFrame* frame)
{
    MainCtrlTime data = {0};
    if (frame->frameDataLen != 8) 
    {
        yl_printf("getNetTimeData Failed By Err: %d \r\n", frame->frameDataLen);
        return data;
    }

    data.utc = (frame->frameData[0] << 24) + (frame->frameData[1] << 16) + 
                (frame->frameData[2] << 8) + frame->frameData[3];
    data.timeOffset = (frame->frameData[4] << 24) + (frame->frameData[5] << 16) + 
                        (frame->frameData[6] << 8) + frame->frameData[7];
    return data;
}

/**********************************************************************
 * 功能描述: 获取网络时间
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void getNetTime(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x09;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 创建SN
 * 输入参数: 无
 * 输出参数: data:SN字节数组
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void createSN(uint8_t (*data)[32])
{
    if (data == NULL) 
    {
        return;
    }

    memcpy(&(*data)[0], sn.snNo, sizeof(sn.snNo));
}

/**********************************************************************
 * 功能描述: 配置SN
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void setSN(uint8_t* data, uint16_t dataLen)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+dataLen;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x0b;
    memset(frame.frameData, 0, sizeof(frame.frameData));
    memcpy(frame.frameData, data, dataLen);
    frame.frameDataLen = dataLen;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 获取配置SN结果
 * 输入参数: frame:帧数据指针
 * 输出参数: 无
 * 返 回 值: SN号
 * 其它说明: 无
 ***********************************************************************/
SN getSN_Result(CloudComFrame* frame)
{
    SN data = {0};
    if (frame->frameDataLen != 1) 
    {
        yl_printf("getSN_Result Failed By Err: %d \r\n", frame->frameDataLen);
        return data;
    }

    data.result = frame->frameData[0];
    return data;
}

/**********************************************************************
 * 功能描述: 发送心跳包
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void sendHeartPack(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x0C;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 查询产品ID
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void queryProductId(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x10;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 查询产品SN
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void queryProductSN(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x11;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 创建主控版本数据
 * 输入参数: 无
 * 输出参数: data:主控版本字节数组
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void createMainCtrlVer(uint8_t (*data)[10])
{
    if (data == NULL) 
    {
        return;
    }

    uint8_t len = sizeof(mainCtrlVer.softVer);
    memcpy(&(*data)[0], mainCtrlVer.softVer, len);
    memcpy(&(*data)[len], mainCtrlVer.hardVer, sizeof(mainCtrlVer.hardVer));
    len += sizeof(mainCtrlVer.hardVer);
    // 设备类型大小端转化
    uint8_t arr[2] = {0};
    memcpy(arr, &mainCtrlVer.devType, 2);
    smallEndianToBigEndian(arr, 2);
    memcpy(&(*data)[len], arr, 2);
}

/**********************************************************************
 * 功能描述: 主控版本上报
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void reportMainCtrlVer(uint8_t* data, uint16_t dataLen)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+dataLen;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x13;
    memset(frame.frameData, 0, sizeof(frame.frameData));
    memcpy(frame.frameData, data, dataLen);
    frame.frameDataLen = dataLen;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 获取支持配网模式
 * 输入参数: frame:帧数据指针
 * 输出参数: 无
 * 返 回 值: 产品ID信息
 * 其它说明: 无
 ***********************************************************************/
ConfigNetMode getSupportNetMode(CloudComFrame* frame)
{
    ConfigNetMode data = {0};
    if (frame->frameDataLen != 1) 
    {
        yl_printf("getSupportNetMode Failed By Err: %d \r\n", frame->frameDataLen);
        return data;
    }

    data.mode = frame->frameData[0];
    return data;
}

/**********************************************************************
 * 功能描述: 查询支持配网模式
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void querySupportSetNetMode(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x15;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 创建主控上行cmd+data
 * 输入参数: srcData: 查询/设置参数itv字节数组
 * 输出参数: dstData: itv实际数据字节数组
 * 返 回 值: dstData字节数组长度
 * 其它说明: 无
 ***********************************************************************/
uint16_t createCmdData(uint8_t* srcData, uint8_t* dstData)
{
    uint8_t cmd = srcData[0];
    uint16_t dstDataLen = 0;
    ITV_INDEX_ARRAY indexArray = {0};
    if (cmd == 0x03 || cmd == 0x04) // 查询itv数据
    { 
        uint8_t paraNum = srcData[1];
        // 填充index
        uint8_t i = 0;
        for (; i < paraNum; ++i) 
        {
            indexArray.indexArr[i] = (srcData[2+i*2] << 8) + srcData[3+i*2];
        }

        indexArray.indexArrLen = paraNum;
    } 
    else if (cmd == 0x06) // 设置单个itv数据
    {   
        indexArray.indexArr[0] = (srcData[2]<<8)+srcData[3];
        indexArray.indexArrLen = 1;
    }

    // 构造目标数据
    uint8_t data[FRAME_DATA_LEN] = {0};
    uint16_t dataLen = Create_ITV_Data(&data, indexArray.indexArr, indexArray.indexArrLen);
    if ((dataLen > 0) && (dataLen < FRAME_DATA_LEN)) // 构造数据成功
    { 
        dstData[0] = cmd;
        memcpy(&dstData[1], data, dataLen);
        dstDataLen = dataLen+1;
    } 
    else // 需要查询/设置的index数据过多
    {  
        yl_printf("0x80 srcData index num over!");
    }

    return dstDataLen;
}

/**********************************************************************
 * 功能描述: 主控上行cmd+data
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void sendMainCtrlCmdData(uint8_t* data, uint16_t dataLen)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+dataLen;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x80;
    memset(frame.frameData, 0, sizeof(frame.frameData));
    memcpy(frame.frameData, data, dataLen);
    frame.frameDataLen = dataLen;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 状态变化更新指令
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度; frameNo: 帧序号
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void sendStatusChange(uint8_t* data, uint16_t dataLen, uint16_t frameNo)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+dataLen;
    frame.frameNo = frameNo;
    frame.frameMark = 0x80;
    frame.frameCmd = 0x81;
    memset(frame.frameData, 0, sizeof(frame.frameData));
    memcpy(frame.frameData, data, dataLen);
    frame.frameDataLen = dataLen;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}





/**********************************************************************
 * 功能描述: 回复网络模块查询主控状态
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void responseForQueryMainCtrlStatus(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x40;
    frame.frameCmd = 0x82;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 解析网络模块ITV数据
 * 输入参数: data: 帧数据字节数组; totalDataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: itv下标数组和数组实际长度
 * 其它说明: 无
 ***********************************************************************/
ITV_INDEX_ARRAY parseNetModuleITV(uint8_t* data, uint16_t totalDataLen)
{
    uint16_t offset = 0;
    uint16_t dataLen = 0;
    ITV_INDEX_ARRAY indexArrInfo = {0};

    while (offset < totalDataLen) 
    {
        if ((offset+sizeof(uint16_t)) > totalDataLen) 
        {
            return indexArrInfo;
        }

        // itv index
        uint16_t index = (data[offset] << 8) + data[offset+1];
        if (index < ITV_ID_DEVIC_TYPE || index >= ITV_ID_MAX) 
        {
            return indexArrInfo;
        }

        offset += 2;
        
        if ((offset+sizeof(uint8_t)) > totalDataLen) 
        {
            return indexArrInfo;
        }

        uint8_t type = data[offset];
        offset += 1;

        uint8_t arr[4] = {0};
        switch (type) {
            case ITV_TYPE_STRING:
                dataLen = (data[offset] << 8) + data[offset+1];
                if ((offset+sizeof(dataLen)) > totalDataLen) 
                {
                    return indexArrInfo;
                }

                offset += 2;

                if ((offset+dataLen) > totalDataLen) 
                {
                    return indexArrInfo;
                }

                memcpy(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].setData.ptStringData, &data[offset], dataLen);
                offset += dataLen;

                // 存储index
                indexArrInfo.indexArr[indexArrInfo.indexArrLen] = index;
                indexArrInfo.indexArrLen++;

                break;
            case ITV_TYPE_U8:
                dataLen = sizeof(uint8_t);
                if ((offset+dataLen) > totalDataLen) 
                {
                    return indexArrInfo;
                }

                memcpy(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].setData.pu8Data, &data[offset], dataLen);
                offset += dataLen;

                // 存储index
                indexArrInfo.indexArr[indexArrInfo.indexArrLen] = index;
                indexArrInfo.indexArrLen++;

                break;
            case ITV_TYPE_S16:
                dataLen = sizeof(int16_t);
                if ((offset+dataLen) > totalDataLen) 
                {
                    return indexArrInfo;
                }

                memcpy(arr, &data[offset], dataLen);
                smallEndianToBigEndian(arr, dataLen);
                memcpy(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].setData.ps16Data, arr, dataLen);
                offset += dataLen;

                // 存储index
                indexArrInfo.indexArr[indexArrInfo.indexArrLen] = index;
                indexArrInfo.indexArrLen++;

                break;
            case ITV_TYPE_S32:
                dataLen = sizeof(int32_t);
                if ((offset+dataLen) > totalDataLen) 
                {
                    return indexArrInfo;
                }

                memcpy(arr, &data[offset], dataLen);
                smallEndianToBigEndian(arr, dataLen);
                memcpy(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].setData.ps32Data, arr, dataLen);
                offset += dataLen;

                // 存储index
                indexArrInfo.indexArr[indexArrInfo.indexArrLen] = index;
                indexArrInfo.indexArrLen++;

                break;
            case ITV_TYPE_U16:
                dataLen = sizeof(uint16_t);
                if ((offset+dataLen) > totalDataLen) 
                {
                    return indexArrInfo;
                }

                memcpy(arr, &data[offset], dataLen);
                smallEndianToBigEndian(arr, dataLen);
                memcpy(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].setData.pu16Data, arr, dataLen);
                offset += dataLen;

                // 存储index
                indexArrInfo.indexArr[indexArrInfo.indexArrLen] = index;
                indexArrInfo.indexArrLen++;

                break;
            case ITV_TYPE_U32:
                dataLen = sizeof(uint32_t);
                if ((offset+dataLen) > totalDataLen) 
                {
                    return indexArrInfo;
                }

                memcpy(arr, &data[offset], dataLen);
                smallEndianToBigEndian(arr, dataLen);
                memcpy(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].setData.pu32Data, arr, dataLen);
                offset += dataLen;

                // 存储index
                indexArrInfo.indexArr[indexArrInfo.indexArrLen] = index;
                indexArrInfo.indexArrLen++;

                break;
            case ITV_TYPE_FLOAT:
                dataLen = sizeof(float);
                if ((offset+dataLen) > totalDataLen) 
                {
                    return indexArrInfo;
                }

                memcpy(arr, &data[offset], dataLen);
                smallEndianToBigEndian(arr, dataLen);
                memcpy(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].setData.pfData, arr, dataLen);
                offset += dataLen;

                // 存储index
                indexArrInfo.indexArr[indexArrInfo.indexArrLen] = index;
                indexArrInfo.indexArrLen++;

                break;
            case ITV_TYPE_S8:
                dataLen = sizeof(int8_t);
                if ((offset+dataLen) > totalDataLen) 
                {
                    return indexArrInfo;
                }

                memcpy(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].setData.ps8Data, &data[offset], dataLen);
                offset += dataLen;

                // 存储index
                indexArrInfo.indexArr[indexArrInfo.indexArrLen] = index;
                indexArrInfo.indexArrLen++;

                break;
            case ITV_TYPE_S8A:
                dataLen = (data[offset] << 8) + data[offset+1];
                if ((offset+sizeof(dataLen)) > totalDataLen) 
                {
                    return indexArrInfo;
                }

                offset += 2;

                if ((offset+dataLen) > totalDataLen) 
                {
                    return indexArrInfo;
                }

                memcpy(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].setData.ptStringData, &data[offset], dataLen);
                offset += dataLen;

                // 存储index
                indexArrInfo.indexArr[indexArrInfo.indexArrLen] = index;
                indexArrInfo.indexArrLen++;

                break;
            case ITV_TYPE_BOOL:
                dataLen = sizeof(uint8_t);
                if ((offset+dataLen) > totalDataLen) 
                {
                    return indexArrInfo;
                }

                memcpy(tITVDataDevice[index-ITV_ID_DEVIC_TYPE].setData.pbData, &data[offset], dataLen);
                offset += dataLen;

                // 存储index
                indexArrInfo.indexArr[indexArrInfo.indexArrLen] = index;
                indexArrInfo.indexArrLen++;

                break;
            default:
                break;
        }
    }

    return indexArrInfo;
}

/**********************************************************************
 * 功能描述: 回复网络模块标准属性控制
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void responseForPropertyCtrl(uint8_t* data, uint16_t dataLen)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+dataLen;
    frame.frameNo = 1;
    frame.frameMark = 0x40;
    frame.frameCmd = 0x83;
    memset(frame.frameData, 0, sizeof(frame.frameData));
    memcpy(frame.frameData, data, dataLen);
    frame.frameDataLen = dataLen;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 回复联网状态指令
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void responseForNetStatus(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x40;
    frame.frameCmd = 0x84;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 回复恢复出厂设置
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void responseForFactoryReset(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x40;
    frame.frameCmd = 0x86;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 回复设置主控时间
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void responseForSetMainCtrlTime(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x40;
    frame.frameCmd = 0x87;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 校验cmd+data数据合理性
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 0: 成功  1:无相关cmd  2:data长度错误
 * 其它说明: 无
 ***********************************************************************/ 
uint8_t checkCmdDataValid(uint8_t* data, uint16_t dataLen)
{
    if (dataLen < 4) // 数据长度不对
    { 
        return 2;   // data长度错误
    }

    uint8_t cmd = data[0];
    uint8_t paraNum = data[1];
    if ((cmd == 0x03) || (cmd == 0x04)) // 查询ITV参数
    { 
        if (1+1+paraNum*2 != dataLen) 
        {
            return 2;
        }
    } 
    else if (cmd == 0x06) // 设置ITV数据
    { 
        if (1+1+paraNum != dataLen) {
            return 2;
        }

        vSetItvData(&data[2], (uint16_t)paraNum, 0);
    } 
    else 
    {
        return 1;   // 无相关cmd
    }

    return 0;   // 成功
}

/**********************************************************************
 * 功能描述: 回复网络模块cmd+data下行
 * 输入参数: result:{0: 成功  1:无相关cmd  2:data长度错误}
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void responseForCmdData(uint8_t result)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 10;
    frame.frameNo = 1;
    frame.frameMark = 0x40;
    frame.frameCmd = 0x8A;
    frame.frameData[0] = result;
    frame.frameDataLen = 1;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 获取测试结果
 * 输入参数: frame:帧数据指针
 * 输出参数: 无
 * 返 回 值: 测试结果
 * 其它说明: 无
 ***********************************************************************/
TestResult getTestResult(CloudComFrame* frame)
{
    TestResult data = {0};
    if (frame->frameDataLen != 1) 
    {
        yl_printf("getTestResult Failed By Err: %d \r\n", frame->frameDataLen);
        return data;
    }

    data.testResult = frame->frameData[0];
    return data;
}

/**********************************************************************
 * 功能描述: 回复网络模块发送测试结果
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void responseForTest(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x40;
    frame.frameCmd = 0x8C;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 上传云端通信错误
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void reportCloudCommErr(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 1;
    frame.frameMark = 0x40;
    frame.frameCmd = 0x8F;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 查询主控版本
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void responseMainCtrlVer(uint8_t* data, uint16_t dataLen)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+dataLen;
    frame.frameNo = 1;
    frame.frameMark = 0x40;
    frame.frameCmd = 0x91;
    memset(frame.frameData, 0, sizeof(frame.frameData));
    memcpy(frame.frameData, data, dataLen);
    frame.frameDataLen = dataLen;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 通知主控有升级
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void responseMainCtrlUpdate(uint8_t* data, uint16_t dataLen)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+dataLen;
    frame.frameNo = 1;
    frame.frameMark = 0x40;
    frame.frameCmd = 0x8D;
    memset(frame.frameData, 0, sizeof(frame.frameData));
    memcpy(frame.frameData, data, dataLen);
    frame.frameDataLen = dataLen;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 升级信息校验
 * 输入参数: data: 帧数据字节数组
 * 输出参数: 无
 * 返 回 值: 0:成功 1:失败
 * 其它说明: 无
 ***********************************************************************/
uint8_t checkUpdateInfo(uint8_t *data)
{
    updateInfo.headFlag = (data[0] << 24) + (data[1] << 16) +
                        (data[2] << 8) + data[3];
    updateInfo.totalFrameNum = (data[4] << 24) + (data[5] << 16) +
                        (data[6] << 8) + data[7];
    updateInfo.updateFileSize = (data[8] << 24) + (data[9] << 16) +
                        (data[10] << 8) + data[11];
    updateInfo.crc32Check = (data[12] << 24) + (data[13] << 16) +
                        (data[14] << 8) + data[15];
    // 判断头部标识,记录升级配置信息
    if (updateInfo.headFlag == UPDATE_HEAD_FLAG) 
    {
        updateInfo.flag = 1;
        return 0;   // 成功
    }

    return 1;       // 失败
}

/**********************************************************************
 * 功能描述: 请求开始升级
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void requestStartUpdate(uint8_t* data, uint16_t dataLen)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+dataLen;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0xE0;
    memset(frame.frameData, 0, sizeof(frame.frameData));
    memcpy(frame.frameData, data, dataLen);
    frame.frameDataLen = dataLen;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 请求发送升级数据包
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度; frameNo: 帧序号
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void requestSendUpdatePack(uint8_t* data, uint16_t dataLen, uint16_t frameNo)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9+dataLen;
    frame.frameNo = frameNo;
    frame.frameMark = 0x80;
    frame.frameCmd = 0xE1;
    memset(frame.frameData, 0, sizeof(frame.frameData));
    memcpy(frame.frameData, data, dataLen);
    frame.frameDataLen = dataLen;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 发送升级结果
 * 输入参数: result: {0:升级成功  1:升级失败,校验失败  2: 升级失败,数据长度不对}
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void sendUpdateResult(uint8_t result)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 10;
    frame.frameNo = 1;
    frame.frameMark = 0x80;
    frame.frameCmd = 0xE2;
    frame.frameData[0] = result;
    frame.frameDataLen = 1;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}

/**********************************************************************
 * 功能描述: 回复升级结果
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void responseUpdateResult(void)
{
    CloudComFrame frame;
    frame.frameHead = 0xAA55;
    frame.frameLen = 9;
    frame.frameNo = 0;
    frame.frameMark = 0x40;
    frame.frameCmd = 0xE2;
    frame.frameDataLen = 0;
    frame.frameCheck = frameCheck((uint8_t*)&frame, frame.frameLen);

    sendQueueHandle(&frame);
}




/**********************************************************************
 * 功能描述: 帧数据大小端转换
 * 输入参数: frame:帧数据指针
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void frameDataToBigEndian(CloudComFrame* frame)
{
    uint8_t arr[4];
    uint16_t offset = 0;
    // 帧头
    uint16_t len = sizeof(frame->frameHead);
    memcpy(arr, &frame->frameHead, len);
    smallEndianToBigEndian(arr, len);
    memcpy(UART0_TX_BUF+offset, arr, len);
    offset += len;

    // 帧长度 
    len = sizeof(frame->frameLen);
    memcpy(arr, &frame->frameLen, len);
    smallEndianToBigEndian(arr, len);
    memcpy(UART0_TX_BUF+offset, arr, len);
    offset += len;

    // 帧序号
    len = sizeof(frame->frameNo);
    memcpy(arr, &frame->frameNo, len);
    smallEndianToBigEndian(arr, len);
    memcpy(UART0_TX_BUF+offset, arr, len);
    offset += len;

    // 帧标志
    len = sizeof(frame->frameMark);
    memcpy(UART0_TX_BUF+offset, &frame->frameMark, len);
    offset += len;

    // 帧命令
    len = sizeof(frame->frameCmd);
    memcpy(UART0_TX_BUF+offset, &frame->frameCmd, len);
    offset += len;

    // 帧数据
    len = frame->frameDataLen;
    memcpy(UART0_TX_BUF+offset, frame->frameData, len);
    offset += len;

    // 帧校验字
    len = sizeof(frame->frameCheck);
    memcpy(UART0_TX_BUF+offset, &frame->frameCheck, len);
    offset += len;
}

/**********************************************************************
 * 功能描述: 给wifi/bt发送数据
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void sendDataToWifi(void)
{
    if (DMA_BUF_BUSY == 0) // Uart0 Tx空闲
    { 
        BaseType_t xReturn = pdFALSE;
        CloudComFrame frame = {0};
        // 如果上一条数据发送成功,则取出队列中的下一条数据, 否则重发数据
        if (retryFlag == 0) 
        {
            xReturn = xQueueReceive(cloudComSendQueueHandle, &frame, 0);
            if (xReturn == pdTRUE) 
            {
                DMA_BUF_BUSY = 1;
                // 帧数据大小端转换
                memset(UART0_TX_BUF, 0, sizeof(UART0_TX_BUF));
                frameDataToBigEndian(&frame);

                /* 设置DMA传输 */
                dma_channel_disable(DMA0, DMA_CH3);		/* 关闭DMA传输才可以进行设置 */
                dma_memory_address_config(DMA0, DMA_CH3, (uint32_t)UART0_TX_BUF);
                dma_transfer_number_config(DMA0, DMA_CH3, frame.frameLen);
                dma_channel_enable(DMA0, DMA_CH3);		/* 开启DMA传输 */

                if (frame.frameCmd != 0x0C &&  
                    frame.frameCmd != 0x82 && 
                    frame.frameCmd != 0x83 && 
                    frame.frameCmd != 0x84 && 
                    frame.frameCmd != 0x86 && 
                    frame.frameCmd != 0x87 &&
                    frame.frameCmd != 0x8A &&
                    frame.frameCmd != 0x8C &&
                    frame.frameCmd != 0x8F &&
                    frame.frameCmd != 0x91 &&
                    frame.frameCmd != 0x8D &&
                    frame.frameCmd != 0xE0 &&
                    frame.frameCmd != 0xE1 &&
                    frame.frameCmd != 0xE2) // 非心跳包、非回复包
                { 
                    retryFlag = 1;
                    // 将当前发送帧保存
                    memcpy(&curr_frame, &frame, sizeof(frame));

                    // 开启重发定时器
                    retryCount = 0;
                    BaseType_t xReturn = xTimerStart(retryTimerHandle, 0);
                    if (xReturn == pdFALSE) 
                    {
                        yl_printf("Start Retry Timer Failed!");
                    }
                }
            }
        } 
        else // 定时器里不能做耗时发送动作,所以创建重发消息队列
        { 
            xReturn = xQueueReceive(retryQueueHandle, &frame, 0);
            if (xReturn == pdTRUE) 
            {
                frameDataToBigEndian(&frame);
                /* 设置DMA传输 */
                dma_channel_disable(DMA0, DMA_CH3);		/* 关闭DMA传输才可以进行设置 */
                dma_memory_address_config(DMA0, DMA_CH3, (uint32_t)UART0_TX_BUF);
                dma_transfer_number_config(DMA0, DMA_CH3, frame.frameLen);
                dma_channel_enable(DMA0, DMA_CH3);		/* 开启DMA传输 */
            }
        }

        // 打印发送帧数据
        if (frame.frameHead == 0xAA55) 
        {
            printfFrame(&frame, 0);
        }
    }
}

/**********************************************************************
 * 功能描述: 协议解析
 * 输入参数: frame: 帧数据指针
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void protocolParse(CloudComFrame* frame)
{
    printfFrame(frame, 1);

    if (frame->frameCmd == 0x0C) // 心跳包
    { 
        heartPackCount = 0;
        return;
    } 
    else if (frame->frameCmd == 0x01) // 配网指令
    { 
        return;
    } 
    else if (frame->frameCmd == 0x02) // 查询联网状态及信号强度
    { 
        NetInfo data = getNetInfo(frame);
        netInfo.netStatus = data.netStatus;
        netInfo.signalPower = data.signalPower;
    } 
    else if (frame->frameCmd == 0x03) // 发送产品ID
    { 
        ProductId data = getProductResult(frame);
        productId.result = data.result;
    } 
    else if (frame->frameCmd == 0x04) // 启动测试流程
    { 
        return;
    } 
    else if (frame->frameCmd == 0x05) // 重启网络模块
    { 
        return;
    } 
    else if (frame->frameCmd == 0x06) // 恢复出厂设置
    { 
        return;
    } 
    else if (frame->frameCmd == 0x07) // 查询mac地址和软件版本号
    { 
        MacVer data = getMacAndSoftVer(frame);
        memcpy(macVer.mac, data.mac, sizeof(data.mac));
        memcpy(macVer.ver, data.ver, sizeof(data.ver));
    } 
    else if (frame->frameCmd == 0x08) // 汇报ITV
    { 
        // 汇报后续ITV
        if (itv_index >= ITV_ID_MAX) 
        {
            return;
        }
        // 汇报第 N 条ITV数据
        uint8_t data[FRAME_DATA_LEN] = {0};
        ITV_INDEX_ARRAY info = getITV_Index_ArrayLimitDataLen();
        uint16_t dataLen = Create_ITV_Data(&data, info.indexArr, info.indexArrLen);
        if (dataLen > 0) 
        {
            uint16_t frameNo = frame->frameNo;
            sendITV(data, dataLen, frameNo+1);
        }
        return;
    } 
    else if (frame->frameCmd == 0x09) // 获取网络时间
    { 
        MainCtrlTime data = getNetTimeData(frame);
        netTime.utc = data.utc;
        netTime.timeOffset = data.timeOffset;
    } 
    else if (frame->frameCmd == 0x0B) // 配置SN
    { 
        SN data = getSN_Result(frame);
        sn.result = data.result;
    } 
    else if (frame->frameCmd == 0x10) // 查询产品ID
    { 
        memcpy(productId.proId, frame->frameData, frame->frameDataLen);
    } 
    else if (frame->frameCmd == 0x11) // 查询产品SN
    { 
        memcpy(sn.snNo, frame->frameData, frame->frameDataLen);
    } 
    else if (frame->frameCmd == 0x13) // 主控版本上报
    { 
        return;
    } 
    else if (frame->frameCmd == 0x15) // 查询支持的配网模式
    { 
        ConfigNetMode data = getSupportNetMode(frame);
        netMode.mode = data.mode;
    } 
    else if (frame->frameCmd == 0x80) // 主控上行cmd+data
    { 
        return;
    } 
    else if (frame->frameCmd == 0x81) // 状态变化更新指令
    { 
        // 上报后续状态更新ITV数据
        if (freq_itv_index >= freqItvIndexArray.indexArrLen) 
        {
            return;
        }

        // 上报第 N 条 频繁上报itv数据
        uint8_t data[FRAME_DATA_LEN] = {0};
        ITV_INDEX_ARRAY info = getFreq_ITV_Index_ArrayLimitData();
        uint16_t dataLen = Create_ITV_Data(&data, info.indexArr, info.indexArrLen);
        if (dataLen > 0) 
        {
            uint16_t frameNo = frame->frameNo;
            sendStatusChange(data, dataLen, frameNo+1);
        }

        return;
    } 
    else if (frame->frameCmd == 0x82) // 网络模块获取主控状态
    { 
        // 初始化 itv_index
        itv_index = ITV_ID_DEVIC_TYPE;
        // 回复收到指令
        responseForQueryMainCtrlStatus(); 
        // 汇报第一条ITV数据
        uint8_t data[FRAME_DATA_LEN] = {0};
        ITV_INDEX_ARRAY info = getITV_Index_ArrayLimitDataLen();
        uint16_t dataLen = Create_ITV_Data(&data, info.indexArr, info.indexArrLen);
        if (dataLen > 0) 
        {
            sendITV(data, dataLen, 1);
        }

        return;
    } 
    else if (frame->frameCmd == 0x83) // 网络模块标准属性控制指令
    { 
        if (frame->frameDataLen > FRAME_DATA_LEN) 
        {
            yl_printf("0x83 data Len more than %d", FRAME_DATA_LEN);
            return;
        }

        vSetItvData(frame->frameData, frame->frameDataLen, 1);
    } 
    else if (frame->frameCmd == 0x84) // 联网状态指令
    { 
        // 更新联网状态对象
        NetInfo data = getNetInfo(frame);
        netInfo.netStatus = data.netStatus;
        netInfo.signalPower = data.signalPower;
        // 回复收到指令
        responseForNetStatus();
    } 
    else if (frame->frameCmd == 0x86) // 恢复出厂设置
    { 
        responseForFactoryReset();
    } 
    else if (frame->frameCmd == 0x87) // 设置主控时间
    { 
        // 更新网络时间
        MainCtrlTime data = getNetTimeData(frame);
        netTime.utc = data.utc;
        netTime.timeOffset = data.timeOffset;
        // 回复收到指令
        responseForSetMainCtrlTime();
    } 
    else if (frame->frameCmd == 0x8A) // 网络模块cmd+data下行
    { 
        // 校验数据,回复收到指令
        uint8_t result = checkCmdDataValid(frame->frameData, frame->frameDataLen);
        responseForCmdData(result);
        // 根据cmd类型回复对应数据
        if (result == 0) // cmd数据正确
        { 
            uint8_t dstData[FRAME_DATA_LEN];
            uint16_t dstDataLen = createCmdData(frame->frameData, dstData);
            if (dstDataLen > 0) 
            {
                sendMainCtrlCmdData(dstData, dstDataLen);
            }
        }
    } 
    else if (frame->frameCmd == 0x8C) // 网络模块发送测试结果
    { 
        // 更新测试结果对象
        TestResult data = getTestResult(frame);
        testResult.testResult = data.testResult;
        // 回复收到指令
        responseForTest();
    } 
    else if (frame->frameCmd == 0x8F) // 上传云端通信错误
    { 
        reportCloudCommErr();
    } 
    else if (frame->frameCmd == 0x91) //  查询主控版本
    { 
        uint8_t data[10] = {0};
        createMainCtrlVer(&data);
        responseMainCtrlVer(data, sizeof(data));
        return;
    } 
    else if (frame->frameCmd == 0x8D) // 通知主控有升级
    { 
        isBackUpAppErased = 0;  // 重新擦除App备份区flash
        if (frame->frameDataLen < 1) 
        {
            yl_printf("0x8D dataLen: %d less than 1!");
            return;
        }

        updateType = frame->frameData[0]; 
        extern uint8_t isCanUpdate(void);
        uint8_t ret = isCanUpdate();
        // 主控允许升级或者wifi有升级,电量充足
        if ((ret == 0 && updateType == 1) || (updateType == 2 && ret != 1)) 
        { 
            // 关闭上报定时器
            isReportItv = 0;
            // 重新打开上报开关超时定时器
            xTimerStart(reportSwitchTimerHandle, 0);
        } 
        else 
        {
            updateType = 0; // 不在升级中
        }

        if (updateType == 2) // wifi升级只有0,1两种状态
        {  
            if (ret != 1) // 非电量低,wifi允许升级
            {  
                ret = 0;
            }
        }

        responseMainCtrlUpdate(&ret, 1);
    } 
    else if (frame->frameCmd == 0xE0) // 请求开始升级
    { 
        if (frame->frameDataLen < 16) 
        {
            yl_printf("0xE0 request update dataLen < 16 !");
            return;
        }

        uint8_t ret = checkUpdateInfo(frame->frameData);
        if (ret == 0) // 校验成功
        { 
            // 短时间重复擦除flash会导致mcu重启
            if (isBackUpAppErased == 0) // App备份区flash未被擦除过
            { 
                // 擦除flash
                fmcErasePages(FMC_WRITE_START_ADDR, FMC_WRITE_END_ADDR);
                ret = checkEraseFlash(FMC_WRITE_START_ADDR, FMC_WRITE_END_ADDR);
                if (ret == 1) // 擦除flash失败
                {  
                    yl_printf("Erase BackUp APP Failed!");
                }

                isBackUpAppErased = 1;
                resetWriteStartAddr();  // 为写升级数据做准备
            }
        } 
        else 
        {  // 校验失败
            updateType = 0; // 不在升级中
            // 开启上报定时器
            isReportItv = 1;
        }

        requestStartUpdate(&ret, 1);
    } 
    else if (frame->frameCmd == 0xE1) // 请求发送升级数据包
    { 
        if (frame->frameNo == 1) { // 第一帧
            recvDataLen = 0;
        }

        // 防止多帧重复发送,导致计算升级帧字节长度出错
        float lastFrameNo = recvDataLen * 1.0 / FRAME_DATA_LEN;
        if (frame->frameNo >= (lastFrameNo + 1)) 
        {
            recvDataLen += frame->frameDataLen;
        }
        
        uint8_t ret;
        if (frame->frameNo < updateInfo.totalFrameNum) // 前n帧升级数据包
        { 
            if (frame->frameDataLen != FRAME_DATA_LEN) 
            {
                yl_printf("frameNo: %d, frameDataLen: %d is less than %d", frame->frameNo, frame->frameDataLen, FRAME_DATA_LEN);
                ret = 1;
                requestSendUpdatePack(&ret, 1, frame->frameNo);
                return;
            }

            ret = 0;
            // 写升级包flash
            fmcWriteFromRecvUart(frame->frameData, frame->frameDataLen);
            requestSendUpdatePack(&ret, 1, frame->frameNo);
        } 
        else if (frame->frameNo == updateInfo.totalFrameNum) // 最后一帧升级数据包
        { 
            ret = 0;
            // 写升级包flash
            fmcWriteFromRecvUart(frame->frameData, frame->frameDataLen);
            requestSendUpdatePack(&ret, 1, frame->frameNo);

            yl_printf("____recvLen:%d______totalSize:%d_______", recvDataLen, updateInfo.updateFileSize);
            if (recvDataLen != updateInfo.updateFileSize) 
            { 
                sendUpdateResult(2);    // 升级数据长度不对
                updateType = 0; // 不在升级中
                return;
            }

            // 开始校验升级结果
            uint32_t result = 0;
            crc32Check(FMC_WRITE_START_ADDR, updateInfo.updateFileSize, &result);
            if (result == updateInfo.crc32Check) // crc32校验成功
            { 
                // 写升级配置flash
                fmcErasePages(UPDATE_CONFIG_START_ADDR, UPDATE_CONFIG_END_ADDR);
                ret = checkEraseFlash(UPDATE_CONFIG_START_ADDR, UPDATE_CONFIG_END_ADDR);
                if (ret == 1) 
                {
                    yl_printf("Erase Update Info Failed!");
                }

                fmcWrite(UPDATE_CONFIG_START_ADDR, (uint8_t *)&updateInfo, sizeof(UpdateInfo));
                UpdateInfo info = (*(volatile UpdateInfo *)UPDATE_CONFIG_START_ADDR);
                if (memcmp(&updateInfo, &info, sizeof(UpdateInfo)) == 0) // 升级配置信息写入成功
                {  
                    yl_printf("/-----------------Update Success----------------/");
                    sendUpdateResult(0);    // 升级成功
                } 
                else
                {
                    yl_printf("write update info failed!");
                    sendUpdateResult(1);    // 升级失败,校验失败
                    updateType = 0; // 不在升级中
                }
            } 
            else 
            {  
                yl_printf("crc check failed!");
                sendUpdateResult(1);    // 升级失败,校验失败
                updateType = 0; // 不在升级中
            }
        } 
        else // 帧序号大于总帧数
        {    
            ret = 1;
            yl_printf("frameNo: %d more than totalFrameNum: %d !", frame->frameNo, updateInfo.totalFrameNum);
            requestSendUpdatePack(&ret, 1, frame->frameNo);
        }
    } 
    else if (frame->frameCmd == 0xE2) // 发送升级结果
    { 
        responseUpdateResult();
        if (frame->frameDataLen > 0) 
        {
            uint8_t result = frame->frameData[0];
            if (updateType == 1 && result == 0) // 主控升级成功
            {  
                // 重启主控
                __set_FAULTMASK(1);
                //NVIC_SystemReset();
                //修改为正常关机,因为硬件问题,复位和正常关机时一样的
                vApiMachineModeQueueWrite(eIdxMachineWorkModeDown);
            } 
            else 
            {
                isReportItv = 1;
            }
        }

        updateType = 0; // 不在升级中
    }

    


    // 通知底层,数据有变化
    // xQueueSend(cloudComRecvQueueHandle, frame, 0);
}

/**********************************************************************
 * 功能描述: 帧校验
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 帧校验结果
 * 其它说明: 无
 ***********************************************************************/
uint8_t frameCheck(uint8_t *data, uint16_t dataLen)
{
    uint16_t i = 0;
    uint8_t checkCode = 0;
    for (; i < dataLen-1; ++i) 
    {
        checkCode += data[i];
    }

    return (checkCode ^ 0x33);
}

/**********************************************************************
 * 功能描述: 解析帧数据
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 帧数据
 * 其它说明: 无
 ***********************************************************************/
CloudComFrame parseFrameData(void)
{
    CloudComFrame frame;
    frame.frameHead = (UART0_RX_BUF[0] << 8) + UART0_RX_BUF[1];
    frame.frameLen = (UART0_RX_BUF[2] << 8) + UART0_RX_BUF[3];
    frame.frameNo =  (UART0_RX_BUF[4] << 8) + UART0_RX_BUF[5];
    frame.frameMark = UART0_RX_BUF[6];
    frame.frameCmd = UART0_RX_BUF[7];
    if ((UART0_RX_NUM - 9) > 0) 
    {
        frame.frameDataLen = UART0_RX_NUM-9;
        memset(frame.frameData, 0, sizeof(frame.frameData));
        memcpy(frame.frameData, &UART0_RX_BUF[8], frame.frameDataLen);
    } 
    else 
    {
        frame.frameDataLen = 0;
    }
    
    frame.frameCheck = UART0_RX_BUF[UART0_RX_NUM-1];
    return frame;
}

/**********************************************************************
 * 功能描述: 接收wifi/bt的数据
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void receiveWifiData(void)
{
    if (UART0_RX_STAT > 0) // 接收到数据
    { 
        UART0_RX_STAT = 0;

        // printfHex(UART0_RX_BUF, UART0_RX_NUM);

        if (UART0_RX_NUM >= 9 && UART0_RX_NUM <= (FRAME_DATA_LEN+9)) // 指令长度9-1024
        { 
            CloudComFrame frame = parseFrameData();
            uint16_t frameHead = frame.frameHead;
            uint8_t frameCode = frame.frameCheck;
            uint8_t frameCheckCode = frameCheck(UART0_RX_BUF, UART0_RX_NUM);

            if (frameHead == 0xAA55 && frameCode == frameCheckCode) 
            {
                retryFlag = 0;  // 成功接收到数据,不需要重发
                BaseType_t xReturn = pdFALSE;
                xReturn = xTimerStop(retryTimerHandle, 0);
                if (xReturn == pdFALSE) // 重发定时器停止失败
                { 
                    yl_printf("Stop Retry Timer Failed!");
                }

                // 协议解析
                protocolParse(&frame);
            } 
            else 
            {
                if (frameHead != 0xAA55) 
                { 
                    yl_printf("Cloud Comm Parse Protocol Failed! Frame Head Err: 0X%04X \r\n", frameHead);
                }

                if (frameCode != frameCheckCode) 
                {
                    yl_printf("Cloud Comm Parse Protocol Failed! Frame Check Err: frameCode: 0x%02X, frameCheck: 0x%02X \r\n", frameCode, frameCheckCode);
                }
            }
        } 
        else 
        {
            yl_printf("Recv Uart0 DataLen Error! Err: %d", UART0_RX_NUM);
        }
    }
}


/**********************************************************************
 * 功能描述: 串口0同wifi/bt通信任务
 * 输入参数: pvParameters: 系统参数不使用
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void vCloudComTaskHandleServer(void *pvParameters)
{
    while (1)
    {
        sendDataToWifi();
        receiveWifiData();
        vTaskDelay(60/portTICK_PERIOD_MS);          // 延时60ms
        // size_t freeHeapSize = xPortGetFreeHeapSize();
        // UBaseType_t taskFreeSize = uxTaskGetStackHighWaterMark(taskHandleCloudCom);
        // yl_printf("____freeHeap:%d_________", freeHeapSize);
        // yl_printf("____freeTask:%d_________", taskFreeSize);
    }
}


/**********************************************************************
 * 功能描述: 云通讯任务初始化
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void vCloudComTaskInit(void)
{
    BaseType_t xReturned;

    xReturned = xTaskCreate((TaskFunction_t)vCloudComTaskHandleServer, 
                            (const char *)"cloud communication server",
                            (uint16_t)800,
                            (void *)NULL,
                            (UBaseType_t)4,
                            (TaskHandle_t *)&taskHandleCloudCom);

    if (xReturned == pdFAIL) 
    {
        yl_printf("Cloud Communication Task Create Failed! \r\n");
    }
}

/**********************************************************************
 * 功能描述: 创建通信消息队列
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void createCloudComMsgQueue(void)
{
    cloudComSendQueueHandle = xQueueCreate(4, sizeof(CloudComFrame));
    if (cloudComSendQueueHandle == NULL) 
    {
        yl_printf("Create Cloud Comm Send MsgQueue Failed! \r\n");
    }

    // cloudComRecvQueueHandle = xQueueCreate(4, sizeof(CloudComFrame));
    // if (cloudComRecvQueueHandle == NULL) {
    //     yl_printf("Create Cloud Comm Recv MsgQueue Failed! \r\n");
    // }

    retryQueueHandle = xQueueCreate(3, sizeof(CloudComFrame));
    if (retryQueueHandle == NULL) 
    {
        yl_printf("Create Cloud Comm Retry MsgQueue Failed! \r\n");
    }
}

/**********************************************************************
 * 功能描述: 心跳包定时回调 10s发送一次
 * 输入参数: xTimer: 系统参数不使用
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void vHeartPackTimer(TimerHandle_t xTimer)
{
    if (heartPackCount >= 2) // 串口通信异常
    { 
        BaseType_t xReturn = xTimerStop(heartPackTimerHandle, 0);
        if (xReturn == pdFALSE) // 重发定时器停止失败
        { 
            yl_printf("Stop Heart Pack Timer Failed!");
        }
    }

    // 升级过程中,不发心跳包
    if (isReportItv == 0) 
    {
        return;
    }

    sendHeartPack();
    heartPackCount++;
}

/**********************************************************************
 * 功能描述: 消息重发回调
 * 输入参数: xTimer: 系统参数不使用
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void retryTimer(TimerHandle_t xTimer)
{
    if (retryCount < 2) 
    { // 重发2次
        retryCount++;
        xQueueSendToFront(retryQueueHandle, &curr_frame, 0);
    } 
    else 
    {
        retryCount = 0;
        retryFlag = 0;
        BaseType_t xReturn = xTimerStop(retryTimerHandle, 0);
        if (xReturn == pdFALSE) // 重发定时器停止失败
        { 
            yl_printf("Stop Retry Timer Failed!");
        }
    }
}

/**********************************************************************
 * 功能描述: itv定时回调
 * 输入参数: xTimer: 系统参数不使用
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void itvTimer(TimerHandle_t xTimer)
{
    if (isReportItv == 0) 
    {
        return;
    }

    // 初始化 itv_index
    itv_index = ITV_ID_DEVIC_TYPE;
    // 汇报第一条ITV数据
    uint8_t data[FRAME_DATA_LEN] = {0};
    ITV_INDEX_ARRAY info = getITV_Index_ArrayLimitDataLen();
    uint16_t dataLen = Create_ITV_Data(&data, info.indexArr, info.indexArrLen);
    if (dataLen > 0) 
    {
        sendITV(data, dataLen, 1);
    }
}

/**********************************************************************
 * 功能描述: itv全局数据改变
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: itv设置参数是否改变 ,1:改变 0:未改变
 * 其它说明: 无
 ***********************************************************************/
static uint8_t isChangedForFreqItvData(void)
{
#ifdef COMPARE_PART_DATA
    uint16_t i = 0;
    for (; i < constFreqItvIndexArray.indexArrLen; ++i) 
    {
        uint16_t index = constFreqItvIndexArray.indexArr[i];
        uint8_t type = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].u8Type;
        void* newVal = NULL;
        void* oldVal = NULL;
        // 用于浮点数据类型
        float fNewVal = 0;
        float fOldVal = 0;
        uint32_t u32NewVal = 0;
        uint32_t u32OldVal = 0;
        switch (type) {
            case ITV_TYPE_STRING:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ptStringData;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.ptStringData;
                if (strcmp((char*)newVal, (char*)oldVal) != 0)  // 当前值不同
                { 
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    strcpy((char*)oldVal, (char*)newVal);
                }

                break;
            case ITV_TYPE_U8:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pu8Data;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.pu8Data;
                if ((*(uint8_t*)newVal) != (*(uint8_t*)oldVal)) 
                {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(uint8_t*)oldVal) = (*(uint8_t*)newVal);
                }
                break;
            case ITV_TYPE_S16:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ps16Data;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.ps16Data;
                if ((*(int16_t*)newVal) != (*(int16_t*)oldVal)) 
                {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(int16_t*)oldVal) = (*(int16_t*)newVal);
                }
                break;
            case ITV_TYPE_S32:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ps32Data;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.ps32Data;
                if ((*(int32_t*)newVal) != (*(int32_t*)oldVal)) 
                {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(int32_t*)oldVal) = (*(int32_t*)newVal);
                }
                break;
            case ITV_TYPE_U16:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pu16Data;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.pu16Data;

                if ((*(uint16_t*)newVal) != (*(uint16_t*)oldVal)) 
                {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(uint16_t*)oldVal) = (*(uint16_t*)newVal);
                }
                break;
            case ITV_TYPE_U32:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pu32Data;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.pu32Data;
                if ((*(uint32_t*)newVal) != (*(uint32_t*)oldVal)) 
                {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(uint32_t*)oldVal) = (*(uint32_t*)newVal);
                }
                break;
            case ITV_TYPE_FLOAT:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pfData;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.pfData;

                memcpy(&fNewVal, newVal, sizeof(float));
                memcpy(&fOldVal, oldVal, sizeof(float));

                u32NewVal = fNewVal*100;
                u32OldVal = fOldVal*100;
                if ((u32NewVal-u32OldVal) != 0) 
                {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    memcpy(oldVal, newVal, sizeof(float));
                }

                break;
            case ITV_TYPE_S8:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ps8Data;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.ps8Data;
                if ((*(int8_t*)newVal) != (*(int8_t*)oldVal)) 
                {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(int8_t*)oldVal) = (*(int8_t*)newVal);
                }
                break;
            case ITV_TYPE_S8A:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ptStringData;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.ptStringData;
                if (strcmp((char*)newVal, (char*)oldVal) != 0)  // 当前值不同
                { 
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    strcpy((char*)oldVal, (char*)newVal);
                }

                break;
            case ITV_TYPE_BOOL:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pbData;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.pbData;
                if ((*(uint8_t*)newVal) != (*(uint8_t*)oldVal)) 
                {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(uint8_t*)oldVal) = (*(uint8_t*)newVal);
                }
                break;
            default:
                break;
        }
    };

    if (freqItvIndexArray.indexArrLen > 0) 
    {    // itv数据有变化
        return 1;
    }
    
    return 0;
#else 
    uint16_t index = ITV_ID_DEVIC_TYPE;
    for (; index < ITV_ID_MAX; ++index) {
        uint8_t type = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].u8Type;
        void* newVal = NULL;
        void* oldVal = NULL;
        // 用于浮点数据类型
        float fNewVal = 0;
        float fOldVal = 0;
        uint32_t u32NewVal = 0;
        uint32_t u32OldVal = 0;
        switch (type) {
            case ITV_TYPE_STRING:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ptStringData;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.ptStringData;
                if (strcmp((char*)newVal, (char*)oldVal) != 0) { // 当前值不同
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    strcpy((char*)oldVal, (char*)newVal);
                }

                break;
            case ITV_TYPE_U8:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pu8Data;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.pu8Data;
                if ((*(uint8_t*)newVal) != (*(uint8_t*)oldVal)) {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(uint8_t*)oldVal) = (*(uint8_t*)newVal);
                }
                break;
            case ITV_TYPE_S16:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ps16Data;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.ps16Data;
                if ((*(int16_t*)newVal) != (*(int16_t*)oldVal)) {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(int16_t*)oldVal) = (*(int16_t*)newVal);
                }
                break;
            case ITV_TYPE_S32:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ps32Data;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.ps32Data;
                if ((*(int32_t*)newVal) != (*(int32_t*)oldVal)) {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(int32_t*)oldVal) = (*(int32_t*)newVal);
                }
                break;
            case ITV_TYPE_U16:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pu16Data;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.pu16Data;
                if ((*(uint16_t*)newVal) != (*(uint16_t*)oldVal)) {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(uint16_t*)oldVal) = (*(uint16_t*)newVal);
                }
                break;
            case ITV_TYPE_U32:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pu32Data;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.pu32Data;
                if ((*(uint32_t*)newVal) != (*(uint32_t*)oldVal)) {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(uint32_t*)oldVal) = (*(uint32_t*)newVal);
                }
                break;
            case ITV_TYPE_FLOAT:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pfData;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.pfData;

                memcpy(&fNewVal, newVal, sizeof(float));
                memcpy(&fOldVal, oldVal, sizeof(float));

                u32NewVal = fNewVal*100;
                u32OldVal = fOldVal*100;
                if ((u32NewVal-u32OldVal) != 0) 
                {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    memcpy(oldVal, newVal, sizeof(float));
                }

                break;
            case ITV_TYPE_S8:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ps8Data;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.ps8Data;
                if ((*(int8_t*)newVal) != (*(int8_t*)oldVal)) {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(int8_t*)oldVal) = (*(int8_t*)newVal);
                }
                break;
            case ITV_TYPE_S8A:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.ptStringData;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.ptStringData;
                if (strcmp((char*)newVal, (char*)oldVal) != 0) { // 当前值不同
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    strcpy((char*)oldVal, (char*)newVal);
                }

                break;
            case ITV_TYPE_BOOL:
                newVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].upData.pbData;
                oldVal = tITVDataDevice[index-ITV_ID_DEVIC_TYPE].lastData.pbData;
                if ((*(uint8_t*)newVal) != (*(uint8_t*)oldVal)) {
                    freqItvIndexArray.indexArr[freqItvIndexArray.indexArrLen] = index;
                    freqItvIndexArray.indexArrLen++;

                    (*(uint8_t*)oldVal) = (*(uint8_t*)newVal);
                }
                break;
            default:
                break;
        }
    }

    if (freqItvIndexArray.indexArrLen > 0) {    // itv数据有变化
        return 1;
    };

    return 0;
#endif
}

/**********************************************************************
 * 功能描述: freq itv定时回调
 * 输入参数: xTimer: 系统定时器回调参数,不使用
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void freqItvTimer(TimerHandle_t xTimer)
{
    if (isReportItv == 0) 
    {
        return;
    }

    memset(&freqItvIndexArray, 0, sizeof(ITV_INDEX_ARRAY));
    if (isChangedForFreqItvData() == 1) // itv全局数据改变
    {   
        // 初始化 freq_itv_index
        freq_itv_index = 0;
        // 汇报第一条ITV数据
        uint8_t data[FRAME_DATA_LEN] = {0};
        ITV_INDEX_ARRAY info = getFreq_ITV_Index_ArrayLimitData();
        uint16_t dataLen = Create_ITV_Data(&data, info.indexArr, info.indexArrLen);
        if (dataLen > 0) 
        {
            sendStatusChange(data, dataLen, 1);
        }
    }
}

/**********************************************************************
 * 功能描述: 打开上报定时器开关
 * 输入参数: xTimer: 系统定时器回调参数,不使用
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void openReportSwitchTimer(TimerHandle_t xTimer)
{
    updateType = 0; // 不在升级中
    // 开启itv上报定时器
    isReportItv = 1;
}

/**********************************************************************
 * 功能描述: 定时器初始化
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void cloudComTimerInit(void)
{
    // 心跳包
    heartPackTimerHandle = xTimerCreate("heart pack", 
                                        (TickType_t)(10 * 1000 / portTICK_PERIOD_MS), // 10s 默认
                                        pdTRUE, //周期
                                        (void *)4,
                                        (TimerCallbackFunction_t)vHeartPackTimer);
    if (heartPackTimerHandle == NULL) 
    {
        yl_printf("heart pack xTimerCreate Create Failed!");
    } 
    else 
    {
        xTimerStart(heartPackTimerHandle, 0);
    }

    // 重发包 
    retryTimerHandle = xTimerCreate("retry", 
                                    (TickType_t)(1000 / portTICK_PERIOD_MS), // 1s 默认
                                    pdTRUE, //周期
                                    (void *)5,
                                    (TimerCallbackFunction_t)retryTimer);
    if (retryTimerHandle == NULL) 
    {
        yl_printf("retry xTimerCreate Create Failed!");
    }

    // itv定时包
    itvTimerHandle = xTimerCreate("itv", 
                                    (TickType_t)(3 * 1000 / portTICK_PERIOD_MS), // 3s 默认
                                    pdTRUE, //周期
                                    (void *)6,
                                    (TimerCallbackFunction_t)itvTimer);
    if (itvTimerHandle == NULL) 
    {
        yl_printf("itv xTimerCreate Create Failed!");
    } 
    else 
    {
        xTimerStart(itvTimerHandle, 0);
    }

    // 频繁上报itv数据包
    freqItvTimerHandle = xTimerCreate("freq itv", 
                                    (TickType_t)(1 * 1000 / portTICK_PERIOD_MS), // 1s 默认
                                    pdTRUE, //周期
                                    (void *)7,
                                    (TimerCallbackFunction_t)freqItvTimer);
    if (freqItvTimerHandle == NULL) 
    {
        yl_printf("freq itv xTimerCreate Create Failed!");
    } 
    else 
    {
        xTimerStart(freqItvTimerHandle, 0);
    }

    // 用于升级计时5min后,打开上报定时器
    reportSwitchTimerHandle = xTimerCreate("report switch", 
                                    (TickType_t)(5 * 60 * 1000 / portTICK_PERIOD_MS), // 5min 默认
                                    pdFALSE, //单次
                                    (void *)8,
                                    (TimerCallbackFunction_t)openReportSwitchTimer);
    if (reportSwitchTimerHandle == NULL) 
    {
        yl_printf("report switch xTimerCreate Create Failed!");
    }
}

/**********************************************************************
 * 功能描述: 打印帧数据
 * 输入参数: frame:帧数据  isRecv:是否是接收帧还是发送帧
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void printfFrame(CloudComFrame* frame, uint8_t isRecv)
{
#if LOG_YL_ENABLE
    if (isRecv == 1) 
    {
        printf("Recv Data:    ");
    } 
    else 
    {
        printf("Send Data:    ");
    }

    printf("frameHead: 0x%04X, frameLen: %d, frameNo: %d, frameMark: 0x%02X, frameCmd: 0x%02X, frameDataLen: %d, frameData:", 
            frame->frameHead, frame->frameLen, 
            frame->frameNo, frame->frameMark, frame->frameCmd, frame->frameDataLen);


    uint16_t i = 0;
    for (; i < frame->frameDataLen; ++i) 
    {
        printf(" %02X", frame->frameData[i]);
    }


    printf(", frameCheck: 0x%02X \r\n", frame->frameCheck);
#endif
}

/**********************************************************************
 * 功能描述: 小端数据转化为大端数据
 * 输入参数: dataLen:帧数据字节数组长度
 * 输出参数: data: 帧数据字节数组
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void smallEndianToBigEndian(uint8_t* data, uint8_t dataLen)
{
    uint8_t i = 0;
    uint8_t arr[4] = {0};
    memcpy(arr, data, dataLen);
    for (; i < dataLen; ++i) 
    {
        data[i] = arr[dataLen-1-i];
    }
}

/**********************************************************************
 * 功能描述: 获取升级类型
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 0:不在升级中  1: 主控升级中  2: wifi升级中
 * 其它说明: 无
 ***********************************************************************/
uint8_t getUpdateType(void)
{
    return updateType;
}

/**********************************************************************
 * 功能描述: 上电初始化数据发送
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void sendDataForStartUp(void)
{
    extern void vApiWifiDataWriteAll(void);
    vApiWifiDataWriteAll();
    // 初始化 itv_index
    itv_index = ITV_ID_DEVIC_TYPE;
    // 汇报第一条ITV数据
    uint8_t data[FRAME_DATA_LEN] = {0};
    ITV_INDEX_ARRAY info = getITV_Index_ArrayLimitDataLen();
    uint16_t dataLen = Create_ITV_Data(&data, info.indexArr, info.indexArrLen);
    if (dataLen > 0) 
    {
        sendITV(data, dataLen, 1);
    }
}


#ifndef __CLOUDIAP_H
#define __CLOUDIAP_H

#include "gd32f30x.h"

#define     FMC_PAGE_SIZE                       ((uint16_t)0x800U)          // 单页2k字节
#define  	APP_START_ADDR						((uint32_t)0x08007800U)		// app起始地址
#define 	APP_END_ADDR						((uint32_t)0x08021FFFU)		// app结束地址
#define     FMC_WRITE_START_ADDR                ((uint32_t)0x08022000U)     // app备份区起始地址
#define     FMC_WRITE_END_ADDR                  ((uint32_t)0x0803C7FFU)     // app备份区结束地址
#define   	UPDATE_CONFIG_START_ADDR			((uint32_t)0x0803C800U) 	// 升级配置信息起始地址
#define 	UPDATE_CONFIG_END_ADDR              ((uint32_t)0x0803D7FFU) 	// 升级配置信息结束地址

#pragma pack(push) //数据压栈
#pragma pack(1) //按BYTE对齐
typedef struct UpdateInfoStru
{
    uint32_t    headFlag;           // 头部标识
    uint32_t    totalFrameNum;      // 升级包总帧数
    uint32_t    updateFileSize;     // 升级文件大小
    uint32_t    crc32Check;         // crc32校验
    uint8_t     flag;               // 1: 有更新  其他: 无更新

} UpdateInfo;
#pragma pack(pop) //恢复设置 

// 擦除flash
void fmcErasePages(uint32_t startAddr, uint32_t endAddr);

// 重置flash写起始地址
void resetWriteStartAddr(void);

// 接收串口数据写flash      注:前面的帧数据必须是4的整数倍,最后一帧可以不是4的整数倍
void fmcWriteFromRecvUart(uint8_t *data, uint32_t dataLen);

// 写flash
void fmcWrite(uint32_t startAddr, uint8_t *data, uint32_t dataLen);

// 擦除flash校验
uint8_t checkEraseFlash(uint32_t startAddr, uint32_t endAddr);

// Md5校验
void md5Check(uint32_t checkStartAddr, uint32_t dataLen, uint8_t* result);

// crc32校验
void crc32Check(uint32_t checkStartAddr, uint32_t dataLen, uint32_t* result);

#endif
/*********************************************************************
 * 内容摘要: 主控升级
 * 其它说明: 无
 * 当前版本: V1.0
 * 作    者: 
 * 完成日期: 2022/11/11
 **********************************************************************/
#include "cloudIap.h"
#include "log.h"
#include "cloudMd5.h"

// 备份区起始地址
static uint32_t writeStartAddr = FMC_WRITE_START_ADDR;

#define     CRC32_InitalValue       0xFFFFFFFF
#define     CRC32_Polynomial        0x04C11DB7
#define     CRC32_XOROUT            0xFFFFFFFF

const uint32_t CRC32_table[] =
{
    0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
    0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
    0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
    0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
    0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
    0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
    0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
    0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
    0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
    0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
    0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
    0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
    0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
    0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
    0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
    0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
    0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
    0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
    0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
    0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
    0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
    0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
    0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
    0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
    0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
    0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
    0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
    0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
    0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
    0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
    0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
    0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
    0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
    0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
    0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
    0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
    0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
    0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
    0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
    0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
    0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
    0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
    0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
    0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
    0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
    0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
    0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
    0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
    0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
    0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
    0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
    0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
    0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
    0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
    0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
    0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
    0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
    0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
    0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
    0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
    0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
    0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
    0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};

/**********************************************************************
 * 功能描述: 擦除flash
 * 输入参数: startAddr: flahs起始地址  endAddr:flash结束地址
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void fmcErasePages(uint32_t startAddr, uint32_t endAddr)
{
    fmc_unlock();

    fmc_flag_clear(FMC_FLAG_BANK0_END);
    fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
    fmc_flag_clear(FMC_FLAG_BANK0_PGERR);

    uint32_t PageNum = (endAddr - startAddr) / FMC_PAGE_SIZE;
    uint32_t EraseCounter = 0;
    fmc_state_enum flashStatus = FMC_READY;
    for (; (EraseCounter < PageNum) && (flashStatus == FMC_READY); EraseCounter++) 
    {
        flashStatus = fmc_page_erase(startAddr + (FMC_PAGE_SIZE * EraseCounter));
        fmc_flag_clear(FMC_FLAG_BANK0_END);
        fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
        fmc_flag_clear(FMC_FLAG_BANK0_PGERR);

        // 看门狗喂狗
        fwdgt_write_enable();
        fwdgt_counter_reload();
    }

    fmc_lock();
}

/**********************************************************************
 * 功能描述: 重置flash写起始地址
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void resetWriteStartAddr(void)
{   
    writeStartAddr = FMC_WRITE_START_ADDR;
}

/**********************************************************************
 * 功能描述: 接收串口数据写flash
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 前面的帧数据必须是4的整数倍,最后一帧可以不是4的整数倍
 ***********************************************************************/
void fmcWriteFromRecvUart(uint8_t *data, uint32_t dataLen)
{
    fmc_unlock();

    fmc_flag_clear(FMC_FLAG_BANK0_END);
    fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
    fmc_flag_clear(FMC_FLAG_BANK0_PGERR);

    uint8_t remain = dataLen % 4;
    uint32_t i = 4;
    uint32_t *addr = (uint32_t *)data;
    fmc_state_enum flashStatus = FMC_READY;
    for (; (i <= dataLen) && (flashStatus == FMC_READY); i = i + 4) 
    {
        flashStatus = fmc_word_program(writeStartAddr, *addr);
        writeStartAddr += 4;
        addr++;
        fmc_flag_clear(FMC_FLAG_BANK0_END);
        fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
        fmc_flag_clear(FMC_FLAG_BANK0_PGERR);

        // 看门狗喂狗
        fwdgt_write_enable();
        fwdgt_counter_reload();
    }

    if (remain != 0) // 写最后一帧数据用  0x010203  -> 0xff030201  小端
    { 
        uint32_t word = 0;
        for (i = 0; i < remain; ++i) 
        {
            word += (data[dataLen-remain-i] << (8*i));
        }

        for (i = remain; i < 4; ++i) 
        {
            word += (0xff << (8*i));
        }

        if (flashStatus == FMC_READY) 
        {
            fmc_word_program(writeStartAddr, word);
            writeStartAddr += 4;
            fmc_flag_clear(FMC_FLAG_BANK0_END);
            fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
            fmc_flag_clear(FMC_FLAG_BANK0_PGERR);

            // 看门狗喂狗
            fwdgt_write_enable();
            fwdgt_counter_reload();
        }
    }

    fmc_lock();
}
 
/**********************************************************************
 * 功能描述: 写flash
 * 输入参数: startAddr: flash起始地址 data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void fmcWrite(uint32_t startAddr, uint8_t *data, uint32_t dataLen)
{
    fmc_unlock();

    fmc_flag_clear(FMC_FLAG_BANK0_END);
    fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
    fmc_flag_clear(FMC_FLAG_BANK0_PGERR);

    uint8_t remain = dataLen % 4;
    uint32_t i = 4;
    uint32_t *addr = (uint32_t *)data;
    fmc_state_enum flashStatus = FMC_READY;
    for (; (i <= dataLen) && (flashStatus == FMC_READY); i = i + 4) 
    {
        flashStatus = fmc_word_program(startAddr, *addr);
        startAddr += 4;
        addr++;
        fmc_flag_clear(FMC_FLAG_BANK0_END);
        fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
        fmc_flag_clear(FMC_FLAG_BANK0_PGERR);

        // 看门狗喂狗
        fwdgt_write_enable();
        fwdgt_counter_reload();
    }

    if (remain != 0) // 写最后一帧数据用  0x010203  -> 0xff030201  小端
    { 
        uint32_t word = 0;
        for (i = 0; i < remain; ++i) 
        {
            word += (data[dataLen-remain-i] << (8*i));
        }

        for (i = remain; i < 4; ++i) 
        {
            word += (0xff << (8*i));
        }

        if (flashStatus == FMC_READY) 
        {
            fmc_word_program(startAddr, word);
            startAddr += 4;
            fmc_flag_clear(FMC_FLAG_BANK0_END);
            fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
            fmc_flag_clear(FMC_FLAG_BANK0_PGERR);

            // 看门狗喂狗
            fwdgt_write_enable();
            fwdgt_counter_reload();
        }
    }

    fmc_lock();
}

/**********************************************************************
 * 功能描述: 擦除flash校验
 * 输入参数: startAddr: flash起始地址 endAddr:flash结束地址
 * 输出参数: 无
 * 返 回 值: 0: 成功  1: 失败
 * 其它说明: 无
 ***********************************************************************/
uint8_t checkEraseFlash(uint32_t startAddr, uint32_t endAddr)
{
    uint32_t WordNum = ((endAddr - startAddr) >> 2);
    uint32_t *ptrd = (uint32_t *)startAddr;
    uint32_t i = 0;
    for (i = 0; i < WordNum; ++i) 
    {
        if (0xffffffff != *ptrd) 
        {
            yl_printf("Erase Flash Failed!");
            return 1;
        } 
        else 
        {
            ptrd++;
        }
    }

    return 0;
}

/**********************************************************************
 * 功能描述: Md5校验
 * 输入参数: checkStartAddr: flash校验起始地址 dataLen:校验数据的长度
 * 输出参数: result:校验结果保存在字节数组里
 * 返 回 值: 0: 成功  1: 失败
 * 其它说明: 无
 ***********************************************************************/
void md5Check(uint32_t checkStartAddr, uint32_t dataLen, uint8_t* result)
{
    uint8_t* startAddr = (uint8_t*)checkStartAddr;
    md5(startAddr, dataLen, result);
}

/**********************************************************************
 * 功能描述: crc32校验公式
 * 输入参数: init_value: 初始项 buf:校验数据  buf_len:校验数据长度
 * 输出参数: 无
 * 返 回 值: 校验结果
 * 其它说明: 无
 ***********************************************************************/
static uint32_t crc_compute_crc32_ext(uint32_t init_value, uint8_t *buf, uint32_t buf_len)
{
    uint32_t CRC32_data = init_value;

    uint32_t i = 0;
    for (; i < buf_len; i++)
    {
        uint32_t t = (CRC32_data ^ buf[i]) & 0xFF;
        CRC32_data = ((CRC32_data >> 8) & 0xFFFFFF) ^ CRC32_table[t];
    }

    return (uint32_t)(CRC32_data ^ CRC32_XOROUT);
}

/**********************************************************************
 * 功能描述: crc32校验
 * 输入参数: checkStartAddr: 校验flash起始地址 dataLen:校验数据长度
 * 输出参数: result: 校验结果
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void crc32Check(uint32_t checkStartAddr, uint32_t dataLen, uint32_t* result)
{
    uint8_t *buf = (uint8_t *)checkStartAddr;
    *result = crc_compute_crc32_ext(CRC32_InitalValue, buf, dataLen);
}

/*********************************************************************
 * 内容摘要: 主控与wifi串口通信配置
 * 其它说明: 无
 * 当前版本: V1.0
 * 作    者:  
 * 完成日期: 2022/11/11
 **********************************************************************/
#include "cloud_uart.h"
#include "log.h"

uint8_t UART0_TX_BUF[UART0_TX_LEN] = {0}; 		/* 发送缓冲区 */
volatile uint8_t DMA_BUF_BUSY = 0 ;			    /* 缓冲区是否已被占用 */

uint8_t UART0_RX_BUF[UART0_RX_LEN] = {0};       /* 双接收缓冲区 */
volatile uint8_t UART0_RX_STAT = 0;			    /* 接受状态 0x01:已接收到数据*/
uint32_t UART0_RX_NUM = 0;					    /* 接收到的数据个数 */


/**********************************************************************
 * 功能描述: USART0 中断服务函数
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void USART0_IRQHandler(void)	
{
    if(SET == usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)) // 空闲中断触发
	{
		usart_interrupt_flag_clear(USART0,USART_INT_FLAG_IDLE);	/* 清除空闲中断标志位 */
		usart_data_receive(USART0);								/* 清除接收完成标志位 */
		dma_channel_disable(DMA0, DMA_CH4);						/* 关闭DMA传输 */
		
        uint32_t size = dma_transfer_number_get(DMA0,DMA_CH4);
		UART0_RX_NUM = sizeof(UART0_RX_BUF) - size; // 获取接收数据大小
		
//        UART0_RX_BUF[UART0_RX_NUM] = '\0';
//        yl_printf("Recv Data: %s, size: %d, len: %d", UART0_RX_BUF, UART0_RX_NUM, size);

		UART0_RX_STAT = 0x01;				/* 接受状态 0x01:已接收到数据 */
		
		/* 重新设置DMA传输 */
		dma_memory_address_config(DMA0,DMA_CH4,(uint32_t)UART0_RX_BUF);
		dma_transfer_number_config(DMA0,DMA_CH4,sizeof(UART0_RX_BUF));
		dma_channel_enable(DMA0, DMA_CH4);		/* 开启DMA传输 */
    }
}

/**********************************************************************
 * 功能描述: DMA0 通道3 中断服务函数 内存到串口0发送过程
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void DMA0_Channel3_IRQHandler(void)
{
	/* 清除DMA0 通道3 中断标志位 */
	dma_interrupt_flag_clear(DMA0, DMA_CH3, DMA_INT_FLAG_G);
	/* 进入中断,表示已经传输完成缓冲区,释放缓冲区 */
	if(DMA_BUF_BUSY == 1) DMA_BUF_BUSY = 0;
}

/**********************************************************************
 * 功能描述: DMA0 通道4 中断服务函数 串口0到内存接收过程
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void DMA0_Channel4_IRQHandler(void)
{
	/* 清除DMA0 通道4 中断标志位 */
	dma_interrupt_flag_clear(DMA0, DMA_CH4, DMA_INT_FLAG_G);
}

/**********************************************************************
 * 功能描述: USART0串口初始化函数
 * 输入参数: bound 波特率
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void uart0_init(uint32_t bound)
{
    /* 使能 GPIOA 时钟 */
    rcu_periph_clock_enable(RCU_GPIOA);

    /* 使能 USART0 时钟 */
    rcu_periph_clock_enable(RCU_USART0);

    /* PA9  复用为 USART0_Tx */
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_9);

    /* PA10 复用为 USART0_Rx */
    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ,GPIO_PIN_10);

    /* USART0 初始化配置 */
    usart_deinit(USART0);
    usart_baudrate_set(USART0, bound);						/* 设置波特率 */
    usart_receive_config(USART0, USART_RECEIVE_ENABLE);		/* 使能接收 */
    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);	/* 使能发送 */	
	usart_enable(USART0);									/* 使能串口0 */




    /* 定义一个DMA配置结构体 */
	dma_parameter_struct dma_init_struct;

    /* 使能 DMA 时钟 */
    rcu_periph_clock_enable(RCU_DMA0);



    /* 初始化 DMA0 通道3 用于串口发送*/
    dma_deinit(DMA0, DMA_CH3);
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;		/* 存储器到外设方向 */
    dma_init_struct.memory_addr = (uint32_t)UART0_TX_BUF;		/* 存储器基地址 */
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;	/* 存储器地址自增 */
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;		/* 存储器位宽为8位 */
    dma_init_struct.number = UART0_TX_LEN;						/* 传输数据个数 */
    dma_init_struct.periph_addr = ((uint32_t)0x40013804);		/* 外设基地址,即USART数据寄存器地址 */
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;	/* 外设地址固定不变 */
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;	/* 外设数据位宽为8位 */
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;			/* 软件优先级为极高*/
    dma_init(DMA0, DMA_CH3, &dma_init_struct);

    /* DMA循环模式配置,不使用循环模式 */
    dma_circulation_disable(DMA0, DMA_CH3);
	/* DMA存储器到存储器模式模式配置,不使用存储器到存储器模式*/
    dma_memory_to_memory_disable(DMA0, DMA_CH3);

    /* USART DMA 发送使能 */
    usart_dma_transmit_config(USART0, USART_DENT_ENABLE);
	/* DMA0 通道3 中断优先级设置并使能 */
	nvic_irq_enable(DMA0_Channel3_IRQn, 0, 0);
	/* 使能 DMA0 通道3 传输完成、传输错误中断 */
    dma_interrupt_enable(DMA0, DMA_CH3, DMA_INT_FTF|DMA_INT_ERR);
    /* 使能 DMA0 通道3 */
    dma_channel_enable(DMA0, DMA_CH3);



    /* 初始化 DMA0 通道4 用于串口接收*/
    dma_deinit(DMA0, DMA_CH4);
    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;		/* 外设到存储器方向 */
    dma_init_struct.memory_addr = (uint32_t)UART0_RX_BUF;		/* 存储器基地址 */
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;	/* 存储器地址自增 */
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;		/* 存储器位宽为8位 */
    dma_init_struct.number = UART0_RX_LEN;					    /* 传输数据个数 */
    dma_init_struct.periph_addr = ((uint32_t)0x40013804);		/* 外设基地址,即USART数据寄存器地址 */
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;	/* 外设地址固定不变 */
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;	/* 外设数据位宽为8位 */
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;			/* 软件优先级为极高*/
    dma_init(DMA0, DMA_CH4, &dma_init_struct);
    
    /* DMA循环模式配置,不使用循环模式 */
    dma_circulation_disable(DMA0, DMA_CH4);
	/* DMA存储器到存储器模式模式配置,不使用存储器到存储器模式*/
    dma_memory_to_memory_disable(DMA0, DMA_CH4);

    /* USART DMA 发送和接收使能 */
    usart_dma_transmit_config(USART0, USART_DENT_ENABLE|USART_DENR_ENABLE);
	/* DMA0 通道4 中断优先级设置并使能 */
	nvic_irq_enable(DMA0_Channel4_IRQn, 0, 0);
	/* 使能 DMA0 通道4 半传输、传输完成、传输错误中断 */
    dma_interrupt_enable(DMA0, DMA_CH4, DMA_INT_FTF|DMA_INT_HTF|DMA_INT_ERR);
    /* 使能 DMA0 通道4 */
    dma_channel_enable(DMA0, DMA_CH4);
	
	/* USART中断设置,抢占优先级0,子优先级0 */
	nvic_irq_enable(USART0_IRQn, 0, 0); 
	/* 使能USART0空闲中断 */
    usart_interrupt_enable(USART0, USART_INT_IDLE);
}

/**********************************************************************
 * 功能描述: 打印十六进制数
 * 输入参数: data: 帧数据字节数组; dataLen:帧数据字节数组长度
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
void printfHex(uint8_t *data, uint16_t dateLen)
{
#if LOG_YL_ENABLE
	printf("Recv Data:    Hex:");
	uint16_t i = 0;
	for(; i < dateLen; ++i) 
    {
	   printf(" %02X", data[i]);
	}

	printf("\r\n");
#endif
}


/*********************************************************************
 * 内容摘要: BootLoader
 * 其它说明: 无
 * 当前版本: V1.0
 * 作    者:  
 * 完成日期: 2022/11/11
 **********************************************************************/
#include "gd32f30x.h"
#include "log.h"
#include "cloudIap.h"
#include "systick.h"

#define 	USART4_BUAD 	115200U		// 波特率


typedef void (*func_app)(void);
volatile func_app func;//func需定义为全局变量
 
/**********************************************************************
 * 功能描述: 跳转app
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
static void run_app(void)
{
	yl_printf("/-----------------Start Jump To App----------------/");
    unsigned long app_addr = APP_START_ADDR; 
   
    //跳转前关闭bootloader打开的中断,防止中断使能而应用程序没有相应的中断处理函数
	rcu_deinit();
    NVIC_DisableIRQ(UART4_IRQn);

	/* 关闭滴答定时器,复位到默认值 */
	SysTick->CTRL = 0;
	SysTick->LOAD = 0;
	SysTick->VAL = 0;

	/* 关闭全局中断 */
    __set_PRIMASK(0);

	 /* 关闭所有中断,清除所有中断挂起标志 */
	for (uint32_t i = 0; i < 8; i++)
	{
		NVIC->ICER[i]=0xFFFFFFFF;
		NVIC->ICPR[i]=0xFFFFFFFF;
	} 

	/* 使能全局中断 */
	__set_PRIMASK(1);
    
    unsigned long stack_pointer = *(unsigned long*)app_addr;
    
    //栈顶地址是否合法(假定sram大小为48k,栈顶指针不会超过0xBFFF)
    //举例,应用程序栈顶指针 = 0x20006958   
    if((stack_pointer & 0x2fff0000) == 0x20000000)
    {
        func = (func_app)(*(volatile unsigned long*)(app_addr + 4));        
      
        __set_MSP(stack_pointer);//将应用程序的栈顶指针保存到寄存器,需放在跳转前最后一步!
		
		__set_CONTROL(0);
		
        func();       
    }
}

/**********************************************************************
 * 功能描述: 串口4初始化
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
static void uart4Init(void)
{
	rcu_periph_clock_enable(RCU_GPIOC);
	rcu_periph_clock_enable(RCU_GPIOD);
	gpio_init(GPIOC, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
	gpio_init(GPIOD, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_2);

	nvic_irq_enable(UART4_IRQn, 8, 0);
	/* enable USART clock */
	rcu_periph_clock_enable(RCU_UART4);
	/* USART configure */
	usart_deinit(UART4);
	usart_baudrate_set(UART4, USART4_BUAD);
	usart_parity_config(UART4, USART_PM_NONE);
	usart_word_length_set(UART4, USART_WL_8BIT);
	usart_stop_bit_set(UART4, USART_STB_1BIT);
	usart_receive_config(UART4, USART_RECEIVE_ENABLE);
	usart_transmit_config(UART4, USART_TRANSMIT_ENABLE);
	usart_enable(UART4);
}

/**********************************************************************
 * 功能描述: 电源自锁
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 ***********************************************************************/
static void lockPower(void)
{
	rcu_periph_clock_enable(RCU_GPIOA);
	gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
	gpio_bit_set(GPIOA, GPIO_PIN_12);
}

int main(void)
{
	nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
	systick_config();
	
	lockPower();
	uart4Init();

	yl_printf("/------------------Boot StartUp--------------/");
	// 检测App是否更新
	UpdateInfo updateInfo = (*(volatile UpdateInfo *)UPDATE_CONFIG_START_ADDR);
	if (updateInfo.flag == 1) // 有更新
	{ 
		uint8_t eraseResult = 0;
		uint32_t firewareSize = 0;
		uint32_t result = 0;
		while (1) 
		{
			yl_printf("/----------------Enetr Update Process--------------------/");
			fmcErasePages(APP_START_ADDR, APP_END_ADDR);	// 先擦除App
			eraseResult = checkEraseFlash(APP_START_ADDR, APP_END_ADDR);	// 校验flash是否擦除完成 
			if (eraseResult == 1) // 擦除失败
			{ 
				yl_printf("/----------------Erase App Failed--------------------/\r\n\r\n\r\n");
				delay_1ms(1000);          			// 延时1s
				continue;
			} 

			yl_printf("/----------------Erase App Success--------------------/");
			// 将备份区flash数据拷贝到app
			firewareSize = updateInfo.updateFileSize;
			yl_printf("/---------------Start Copy BackUp_App To App--------------------/");
			fmcWrite(APP_START_ADDR, (uint8_t *)FMC_WRITE_START_ADDR, firewareSize);
			yl_printf("/----------------Start Crc32 Check--------------------/");
			crc32Check(APP_START_ADDR, firewareSize, &result);
			if (result != updateInfo.crc32Check) // md5校验失败
			{ 
				yl_printf("/----------------Check Crc32 Failed--------------------/\r\n\r\n\r\n");
				delay_1ms(1000);          			// 延时1s
				continue;
			}

			yl_printf("/----------------Check Crc32 Success--------------------/");
			// 更新固件信息跳转到APP
			updateInfo.flag = 0;
			// 擦除升级配置信息
			fmcErasePages(UPDATE_CONFIG_START_ADDR, UPDATE_CONFIG_END_ADDR);
			fmcWrite(UPDATE_CONFIG_START_ADDR, (uint8_t *)&updateInfo, sizeof(UpdateInfo));
			// 检验配置信息写入是否成功
			UpdateInfo info = (*(volatile UpdateInfo *)UPDATE_CONFIG_START_ADDR);
			if (memcmp(&updateInfo, &info, sizeof(UpdateInfo)) == 0) 
			{
				break;
			} 
			else 
			{
				yl_printf("/----------------Version Config Update Failed--------------------/\r\n\r\n\r\n");
				delay_1ms(1000);
			}
		}
	}

	// 跳转APP
	run_app();

	while (1) 
	{
		yl_printf("Jump To App Failed!");
		delay_1ms(1000);  	// 延时1s
	}
}

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值