这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » 【原创】ONENET点灯--from恶龙咆哮

共32条 1/4 1 2 3 4 跳转至

【原创】ONENET点灯--from恶龙咆哮

工程师
2023-03-20 17:45:13   被打赏 50 分(兑奖)     打赏

今天和大家分享STM32F103C8T6与ESP8266-01S连接ONENET云平台,实现数据的上传和下发。

首先我们要准备的元器件有STM32F103C8T6最小系统板,ESP826601S,USB转TTL模块。

2.jpg

首先需要给WIFI模块烧录AT固件,很多人喜欢用下面这种模块烧录,很方便,但是经常下载不进去,我也是尝试过很多次失败后才发现,在下载固件的时候,ESP8266的RST引脚需要先拉高再拉底,否则经常下载失败,所以在下载固件的时候还是用USB转TTL模块,手动给RST引脚拉高再拉低就好。

3.jpg

下载固件的软件和固件的文件可以直接去安信可的官网上下载就可以,这里就不再分享了。

当下载好固件后,我们登录onenet官网,在控制台里面新建一个产品,选择MQTT协议;

4.png

创建好后需要在产品详情里面找到鉴权信息,这个是需要我们自己填写的,在后续联网需要使用到。

5.png

接下来就是代码方面了,先打开一个工程,将我们需要的几个文件移植进去,分别是ESP8266的C文件:

//==========================================================
 
//单片机头文件
#include "stm32f10x.h"
 
//网络设备驱动
#include "esp8266.h"
 
//硬件驱动
#include "delay.h"
#include "usart.h"
 
//C库
#include <string.h>
#include <stdio.h>
 
 
#define ESP8266_WIFI_INFO            "AT+CWJAP=\"nova\",\"12345678\"\r\n"
 
#define ESP8266_ONENET_INFO            "AT+CIPSTART=\"TCP\",\"183.230.40.39\",6002\r\n"
 
 
 
unsigned char esp8266_buf[256];
unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;
 
 
//==========================================================
//    函数名称:   ESP8266_Clear
//
//    函数功能:   清空缓存
//
//    入口参数:   无
//
//    返回参数:   无
//
//    说明:         
//==========================================================
void ESP8266_Clear(void)
{
 
       memset(esp8266_buf, 0, sizeof(esp8266_buf));
       esp8266_cnt = 0;
 
}
 
//==========================================================
//    函数名称:   ESP8266_WaitRecive
//
//    函数功能:   等待接收完成
//
//    入口参数:   无
//
//    返回参数:   REV_OK-接收完成             REV_WAIT-接收超时未完成
//
//    说明:          循环调用检测是否接收完成
//==========================================================
_Bool ESP8266_WaitRecive(void)
{
 
       if(esp8266_cnt == 0)                                            //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
              return REV_WAIT;
             
       if(esp8266_cnt == esp8266_cntPre)                      //如果上一次的值和这次相同,则说明接收完毕
       {
              esp8266_cnt = 0;                                           //清0接收计数
                    
              return REV_OK;                                                     //返回接收完成标志
       }
             
       esp8266_cntPre = esp8266_cnt;                                   //置为相同
      
       return REV_WAIT;                                                  //返回接收未完成标志
 
}
 
//==========================================================
//    函数名称:   ESP8266_SendCmd
//
//    函数功能:   发送命令
//
//    入口参数:   cmd:命令
//                         res:需要检查的返回指令
//
//    返回参数:   0-成功   1-失败
//
//    说明:         
//==========================================================
_Bool ESP8266_SendCmd(char *cmd, char *res)
{
      
       unsigned char timeOut = 200;
 
       Usart_SendString(USART2, (unsigned char *)cmd, strlen((const char *)cmd));
      
       while(timeOut--)
       {
              if(ESP8266_WaitRecive() == REV_OK)                                                //如果收到数据
              {
                     if(strstr((const char *)esp8266_buf, res) != NULL)           //如果检索到关键词
                     {
                            ESP8266_Clear();                                                          //清空缓存
                           
                            return 0;
                     }
              }
             
              delay_ms(10);
       }
      
       return 1;
 
}
 
//==========================================================
//    函数名称:   ESP8266_SendData
//
//    函数功能:   发送数据
//
//    入口参数:   data:数据
//                         len:长度
//
//    返回参数:   无
//
//    说明:         
//==========================================================
void ESP8266_SendData(unsigned char *data, unsigned short len)
{
 
       char cmdBuf[32];
      
       ESP8266_Clear();                                                   //清空接收缓存
      
       //先发送要发送数据的指令做准备
       sprintf(cmdBuf, "AT+CIPSEND=%d\r\n", len);          //发送命令
       if(!ESP8266_SendCmd(cmdBuf, ">"))                      //收到‘>’时可以发送数据
       {
              //既然准备完毕即可开始发送数据
              Usart_SendString(USART2, data, len);              //发送设备连接请求数据
       }
 
}
 
//==========================================================
//    函数名称:   ESP8266_GetIPD
//
//    函数功能:   获取平台返回的数据
//
//    入口参数:   等待的时间(乘以10ms)
//
//    返回参数:   平台返回的原始数据
//
//    说明:          不同网络设备返回的格式不同,需要去调试
//                         如ESP8266的返回格式为  "+IPD,x:yyy"   x代表数据长度,yyy是数据内容
//==========================================================
unsigned char *ESP8266_GetIPD(unsigned short timeOut)
{
 
       char *ptrIPD = NULL;
      
       do
       {
              if(ESP8266_WaitRecive() == REV_OK)                                                       //如果接收完成
              {
                     ptrIPD = strstr((char *)esp8266_buf, "IPD,");                         //搜索“IPD”头
                     if(ptrIPD == NULL)                                                                             //如果没找到,可能是IPD头的延迟,还是需要等待一会,但不会超过设定的时间
                     {
                            //UsartPrintf(USART_DEBUG, "\"IPD\" not found\r\n");
                     }
                     else
                     {
                            ptrIPD = strchr(ptrIPD, ':');                                             //找到':'
                            if(ptrIPD != NULL)
                            {
                                   ptrIPD++;
                                   return (unsigned char *)(ptrIPD);
                            }
                            else
                                   return NULL;
                           
                     }
              }
             
              delay_ms(5);                                                                                      //延时等待
       } while(timeOut--);
      
       return NULL;                                                                                             //超时还未找到,返回空指针
 
}
 
//==========================================================
//    函数名称:   ESP8266_Init
//
//    函数功能:   初始化ESP8266
//
//    入口参数:   无
//
//    返回参数:   无
//
//    说明:         
//==========================================================
void ESP8266_Init(void)
{
      
       GPIO_InitTypeDef GPIO_Initure;
      
       RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 
       //ESP8266复位引脚
       GPIO_Initure.GPIO_Mode = GPIO_Mode_Out_PP;
       GPIO_Initure.GPIO_Pin = GPIO_Pin_11;                               //GPIOC13-复位
       GPIO_Initure.GPIO_Speed = GPIO_Speed_50MHz;
       GPIO_Init(GPIOA, &GPIO_Initure);
      
       GPIO_WriteBit(GPIOA, GPIO_Pin_11, Bit_RESET);
       delay_ms(250);
       GPIO_WriteBit(GPIOA, GPIO_Pin_11, Bit_SET);
       delay_ms(500);
      
       ESP8266_Clear();
      
       printf("1. AT\r\n");
       while(ESP8266_SendCmd("AT\r\n", "OK"))//AT测试
              delay_ms(500);
      
       printf("2. CWMODE\r\n");
       while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))//设置为STA模式
              delay_ms(500);
      
       printf( "3. AT+CWDHCP\r\n");
       while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK"))//开启DHCP
              delay_ms(500);
      
       printf("4. CWJAP\r\n");
       while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))//链接路由器
              delay_ms(500);
      
       printf( "5. CIPSTART\r\n");
       while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT"))//连接TCP服务器
              delay_ms(500);
      
       printf("6. ESP8266 Init OK\r\n");//打印一下信息表明8266初试化成功
 
}
//==========================================================
ESP8266的H文件:
#ifndef _ESP8266_H_
#define _ESP8266_H_
 
#define REV_OK          0     //接收完成标志
#define REV_WAIT       1     //接收未完成标志
 
void ESP8266_Init(void);
 
void ESP8266_Clear(void);
 
void ESP8266_SendData(unsigned char *data, unsigned short len);
 
unsigned char *ESP8266_GetIPD(unsigned short timeOut);
 
 
#endif
//==========================================================
ONENET的C文件:
 
//单片机头文件
#include "stm32f10x.h"
 
//网络设备
#include "esp8266.h"
 
//协议文件
#include "onenet.h"
#include "mqttkit.h"
 
//硬件驱动
#include "usart.h"
#include "delay.h"
#include "bsp_led.h"
//#include "sht20.h"
 
//C库
#include <string.h>
#include <stdio.h>
#include "cJSON.h"
 
#define PROID     "574882"                                //产品ID
 
#define AUTH_INFO    "824894798"                                          //鉴权信息
 
#define DEVID      "1050635876"                          //设备ID
 
extern u8 humidityH;     //湿度整数部分
extern u8 humidityL;      //湿度小数部分
extern u8 temperatureH;   //温度整数部分
extern u8 temperatureL;   //温度小数部分
u8 LED0_FLAG;
unsigned char OneNet_FillBuf(char *buf)
{
 char text[32];
 
LED0_FLAG=GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12);//读取LED的开关状态(即对应引脚的)
 
 
printf("LED0_FLAG_TYPE=%d\n",sizeof(LED0_FLAG));
 
memset(text, 0, sizeof(text));
 
strcpy(buf, ",;");
 
memset(text, 0, sizeof(text));
sprintf(text, "Tempreture,%d.%d;",temperatureH,temperatureL);
strcat(buf, text);
 
memset(text, 0, sizeof(text));
sprintf(text, "Humidity,%d.%d;", humidityH,humidityL);
strcat(buf, text);
 
memset(text, 0, sizeof(text));
sprintf(text, "LED0,%d;", LED0_FLAG);
strcat(buf, text);
 
printf("buf_mqtt=%s\r\n",buf);
return strlen(buf);
  }
 
 
//==========================================================
//    函数名称:   OneNet_SendData
//
//    函数功能:   上传数据到平台
//
//    入口参数:   type:发送数据的格式
//
//    返回参数:   无
//
//    说明:         
//==========================================================
void OneNet_SendData(void)
{
      
       MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};                                                                                //协议包
 
char buf[128];
 
short body_len = 0, i = 0;
 
printf( "Tips:   OneNet_SendData-MQTT\r\n");
 
memset(buf, 0, sizeof(buf));//清空数组内容
 
body_len = OneNet_FillBuf(buf);                                                                                                                       //获取当前需要发送的数据流的总长度
 
if(body_len)
{
       if(MQTT_PacketSaveData(DEVID, body_len, NULL, 5, &mqttPacket) == 0)                                           //封包
       {
              for(; i < body_len; i++)
                     mqttPacket._data[mqttPacket._len++] = buf[i];
             
              ESP8266_SendData(mqttPacket._data, mqttPacket._len);                                                             //上传数据到平台
              printf( "Send %d Bytes\r\n", mqttPacket._len);
             
              MQTT_DeleteBuffer(&mqttPacket);                                                                                                     //删包
       }
       else
              printf(  "WARN:   EDP_NewBuffer Failed\r\n");
}
 
}
 
//==========================================================
//    函数名称:   OneNet_RevPro
//
//    函数功能:   平台返回数据检测
//
//    入口参数:   dataPtr:平台返回的数据
//
//    返回参数:   无
//
//    说明:         
//==========================================================
void OneNet_RevPro(unsigned char *cmd)
{
 
MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};                                                    //协议包
 
char *req_payload = NULL;
char *cmdid_topic = NULL;
 
unsigned short req_len = 0;
unsigned char type = 0;
 
short result = 0;
 
char *dataPtr = NULL;
char numBuf[10];
int num = 0;
 
 
cJSON *json , *json_value;
cJSON *json1, *json_value1;
 
 
type = MQTT_UnPacketRecv(cmd);
switch(type)
{
       case MQTT_PKT_CMD:                                                                                                  //命令下发
             
              result = MQTT_UnPacketCmd(cmd, &cmdid_topic, &req_payload, &req_len);  //解出topic和消息体
              if(result == 0)
              {
                     //打印收到的信息
                     printf(  "cmdid: %s, req: %s, req_len: %d\r\n", cmdid_topic, req_payload, req_len);
                    
                     // 对数据包req_payload进行JSON格式解析
                     json = cJSON_Parse(req_payload);
                    
                     if (!json)//如果json内容为空,则打印错误信息
                            printf("Error before: [%s]\n",cJSON_GetErrorPtr());
                     else
                     {
                            json_value = cJSON_GetObjectItem(json , "LED0");//提取对应属性的数值
                     //    printf("json_value: [%s]\r\n",json_value->string);//转化为字符串数值
                     //    printf("json_value: [%d]\r\n",json_value->valueint);//转化为数值型数值
                           
                            if((json_value->valueint)==1)
                            {
                                   LED_G(OFF);
                            }
                                  
                            else if((json_value->valueint)==0)                                
                            {
                                   LED_G(ON);
                            }
                           
 
                     }
                     //同上
//                  json1 = cJSON_Parse(req_payload);
//                  if (!json1)
//                         printf("Error before: [%s]\n",cJSON_GetErrorPtr());
//                  else
//                  {
//                         json_value1 = cJSON_GetObjectItem(json1 , "LED1");
 
//                         if((json_value1->valueint)==1)//整数值
//                               
//                         LED1=1;
//                         else if((json_value1->valueint)==0)
//                 
//                         LED1=0;
//                 
//                  }
                    
                     if(MQTT_PacketCmdResp(cmdid_topic, req_payload, &mqttPacket) == 0) //命令回复组包
                     {
                            printf( "Tips:   Send CmdResp\r\n");
                           
                            ESP8266_SendData(mqttPacket._data, mqttPacket._len);                   //回复命令
                            MQTT_DeleteBuffer(&mqttPacket);                                                           //删包
                     }
                     cJSON_Delete(json);//释放位于堆中cJSON结构体内存
                     cJSON_Delete(json1);
              }
      
       break;
             
       case MQTT_PKT_PUBACK:                                                                                             //发送Publish消息,平台回复的Ack
      
              if(MQTT_UnPacketPublishAck(cmd) == 0)
                     printf(  "Tips:       MQTT Publish Send OK\r\n");
             
       break;
      
       default:
              result = -1;
       break;
}
 
ESP8266_Clear();                                                          //清空缓存
 
if(result == -1)
       return;
 
dataPtr = strchr(req_payload, '}');                                  //搜索'}'
 
if(dataPtr != NULL && result != -1)                              //如果找到了
{
       dataPtr++;
      
       while(*dataPtr >= '0' && *dataPtr <= '9')             //判断是否是下发的命令控制数据
       {
              numBuf[num++] = *dataPtr++;
       }
       numBuf[num] = 0;
      
       num = atoi((const char *)numBuf);                         //转为数值形式
}
 
 
 
if(type == MQTT_PKT_CMD || type == MQTT_PKT_PUBLISH)
{
       MQTT_FreeBuffer(cmdid_topic);
       MQTT_FreeBuffer(req_payload);
}
}
 
 
//==========================================================
//    函数名称:   OneNet_DevPro
//
//    函数功能:   平台返回数据检测
//
//    入口参数:   dataPtr:平台返回的数据
//
//    返回参数:   无
//
//    说明:         
//==========================================================
_Bool OneNet_DevLink(void)
{
 
MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};                               //协议包
 
unsigned char *dataPtr;
 
_Bool status = 1;
//打印一下信息产品id,鉴权信息,设备ID
printf("OneNet_DevLink\r\nPROID: %s,     AUIF: %s, DEVID:%s\r\n", PROID, AUTH_INFO, DEVID);
 
if(MQTT_PacketConnect(PROID, AUTH_INFO, DEVID, 256, 0, MQTT_QOS_LEVEL0, NULL, NULL, 0, &mqttPacket) == 0)
{
       ESP8266_SendData(mqttPacket._data, mqttPacket._len);                   //上传平台
      
       dataPtr = ESP8266_GetIPD(250);                                                              //等待平台响应
       if(dataPtr != NULL)//如果平台返回数据不为空则
       {
              if(MQTT_UnPacketRecv(dataPtr) == MQTT_PKT_CONNACK)//   MQTT数据接收类型判断(connack报文)
              {
                     switch(MQTT_UnPacketConnectAck(dataPtr))//打印是否连接成功及连接失败的原因
                     {
                            case 0:printf( "Tips:      连接成功\r\n");status = 0;break;
                           
                            case 1:printf(  "WARN:      连接失败:协议错误\r\n");break;
                            case 2:printf(  "WARN:      连接失败:非法的clientid\r\n");break;
                            case 3:printf(  "WARN:      连接失败:服务器失败\r\n");break;
                            case 4:printf(  "WARN:      连接失败:用户名或密码错误\r\n");break;
                            case 5:printf(  "WARN:      连接失败:非法链接(比如token非法)\r\n");break;
                           
                            default:printf(  "ERR:   连接失败:未知错误\r\n");break;
                     }
              }
       }
      
       MQTT_DeleteBuffer(&mqttPacket);                                                    //删包
}
else
       printf( "WARN:     MQTT_PacketConnect Failed\r\n");
 
     return status;     
 }
//==========================================================
 
ONENET的H文件:
 
#ifndef _ONENET_H_
#define _ONENET_H_
 
 
 
 
 
void OneNet_SendData(void);
 
void OneNet_RevPro(unsigned char *dataPtr);
 
_Bool OneNet_DevLink(void);
 
#endif
//==========================================================
 
MQTT协议C文件:
 
/**
       ************************************************************
       ************************************************************
       ************************************************************
       *      文件名:      MqttKit.c
       *
       *      作者:         张继瑞
       *
       *      日期:         2018-04-27
       *
       *      版本:         V1.6
       *
       *      说明:         MQTT协议
       *
       *      修改记录:   V1.1:解决MQTT_PacketSubscribe订阅不为2个topic
       *                                         个数时协议错误的bug
       *                           V1.2:修复MQTT_PacketCmdResp的bug
       *                           V1.3:将strncpy替换为memcpy,解决潜在bug
       *                           V1.4:修复   MQTT_PacketPublishAck
       *                                                MQTT_PacketPublishRel
       *                                                函数封包错误的bug
       *                           V1.5:增加   MQTT_UnPacketCmd
       *                                                MQTT_UnPacketPublish
       *                                                接口对消息内容长度的提取参数
       *                           V1.6:增加二进制文件上传接口
       ************************************************************
       ************************************************************
       ************************************************************
**/
 
//协议头文件
#include "MqttKit.h"
 
//C库
#include <string.h>
#include <stdio.h>
 
 
#define CMD_TOPIC_PREFIX            "$creq"
 
 
//==========================================================
//    函数名称:   EDP_NewBuffer
//
//    函数功能:   申请内存
//
//    入口参数:   edpPacket:包结构体
//                         size:大小
//
//    返回参数:   无
//
//    说明:          1.可使用动态分配来分配内存
//                         2.可使用局部或全局数组来指定内存
//==========================================================
void MQTT_NewBuffer(MQTT_PACKET_STRUCTURE *mqttPacket, uint32 size)
{
      
       uint32 i = 0;
 
       if(mqttPacket->_data == NULL)
       {
              mqttPacket->_memFlag = MEM_FLAG_ALLOC;
             
              mqttPacket->_data = (uint8 *)MQTT_MallocBuffer(size);
              if(mqttPacket->_data != NULL)
              {
                     mqttPacket->_len = 0;
                    
                     mqttPacket->_size = size;
                    
                     for(; i < mqttPacket->_size; i++)
                            mqttPacket->_data[i] = 0;
              }
       }
       else
       {
              mqttPacket->_memFlag = MEM_FLAG_STATIC;
             
              for(; i < mqttPacket->_size; i++)
                     mqttPacket->_data[i] = 0;
             
              mqttPacket->_len = 0;
             
              if(mqttPacket->_size < size)
                     mqttPacket->_data = NULL;
       }
 
}
 
//==========================================================
//    函数名称:   MQTT_DeleteBuffer
//
//    函数功能:   释放数据内存
//
//    入口参数:   edpPacket:包结构体
//
//    返回参数:   无
//
//    说明:         
//==========================================================
void MQTT_DeleteBuffer(MQTT_PACKET_STRUCTURE *mqttPacket)
{
 
       if(mqttPacket->_memFlag == MEM_FLAG_ALLOC)
              MQTT_FreeBuffer(mqttPacket->_data);
      
       mqttPacket->_data = NULL;
       mqttPacket->_len = 0;
       mqttPacket->_size = 0;
       mqttPacket->_memFlag = MEM_FLAG_NULL;
 
}
 
int32 MQTT_DumpLength(size_t len, uint8 *buf)
{
      
       int32 i = 0;
      
       for(i = 1; i <= 4; ++i)
       {
              *buf = len % 128;
              len >>= 7;
              if(len > 0)
              {
                     *buf |= 128;
                     ++buf;
              }
              else
              {
                     return i;
              }
       }
 
       return -1;
}
 
int32 MQTT_ReadLength(const uint8 *stream, int32 size, uint32 *len)
{
      
       int32 i;
       const uint8 *in = stream;
       uint32 multiplier = 1;
 
       *len = 0;
       for(i = 0; i < size; ++i)
       {
              *len += (in[i] & 0x7f) * multiplier;
 
              if(!(in[i] & 0x80))
              {
                     return i + 1;
              }
 
              multiplier <<= 7;
              if(multiplier >= 2097152)          //128 * *128 * *128
              {
                     return -2;                                  // error, out of range
              }
       }
 
       return -1;                                                // not complete
 
}
 
//==========================================================
//    函数名称:   MQTT_UnPacketRecv
//
//    函数功能:   MQTT数据接收类型判断
//
//    入口参数:   dataPtr:接收的数据指针
//
//    返回参数:   0-成功          其他-失败原因
//
//    说明:         
//==========================================================
uint8 MQTT_UnPacketRecv(uint8 *dataPtr)
{
      
       uint8 status = 255;
       uint8 type = dataPtr[0] >> 4;                         //类型检查
      
       if(type < 1 || type > 14)
              return status;
      
       if(type == MQTT_PKT_PUBLISH)
       {
              uint8 *msgPtr;
              uint32 remain_len = 0;
             
              msgPtr = dataPtr + MQTT_ReadLength(dataPtr + 1, 4, &remain_len) + 1;
             
              if(remain_len < 2 || dataPtr[0] & 0x01)                                 //retain
                     return 255;
             
              if(remain_len < ((uint16)msgPtr[0] << 8 | msgPtr[1]) + 2)
                     return 255;
             
              if(strstr((int8 *)msgPtr + 2, CMD_TOPIC_PREFIX) != NULL)    //如果是命令下发
                     status = MQTT_PKT_CMD;
              else
                     status = MQTT_PKT_PUBLISH;
       }
       else
              status = type;
      
       return status;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketConnect
//
//    函数功能:   连接消息组包
//
//    入口参数:   user:用户名:产品ID
//                         password:密码:鉴权信息或apikey
//                         devid:设备ID
//                         cTime:连接保持时间
//                         clean_session:离线消息清除标志
//                         qos:重发标志
//                         will_topic:异常离线topic
//                         will_msg:异常离线消息
//                         will_retain:消息推送标志
//                         mqttPacket:包指针
//
//    返回参数:   0-成功          其他-失败
//
//    说明:         
//==========================================================
uint8 MQTT_PacketConnect(const int8 *user, const int8 *password, const int8 *devid,
                                          uint16 cTime, uint1 clean_session, uint1 qos,
                                          const int8 *will_topic, const int8 *will_msg, int32 will_retain,
                                          MQTT_PACKET_STRUCTURE *mqttPacket)
{
      
       uint8 flags = 0;
       uint8 will_topic_len = 0;
       uint16 total_len = 15;
       int16 len = 0, devid_len = strlen(devid);
      
       if(!devid)
              return 1;
      
       total_len += devid_len + 2;
      
       //断线后,是否清理离线消息:1-清理  0-不清理--------------------------------------------
       if(clean_session)
       {
              flags |= MQTT_CONNECT_CLEAN_SESSION;
       }
      
       //异常掉线情况下,服务器发布的topic------------------------------------------------------
       if(will_topic)
       {
              flags |= MQTT_CONNECT_WILL_FLAG;
              will_topic_len = strlen(will_topic);
              total_len += 4 + will_topic_len + strlen(will_msg);
       }
      
       //qos级别--主要用于PUBLISH(发布态)消息的,保证消息传递的次数-----------------------------
       switch((unsigned char)qos)
       {
              case MQTT_QOS_LEVEL0:
                     flags |= MQTT_CONNECT_WILL_QOS0;                                             //最多一次
              break;
             
              case MQTT_QOS_LEVEL1:
                     flags |= (MQTT_CONNECT_WILL_FLAG | MQTT_CONNECT_WILL_QOS1); //最少一次
              break;
             
              case MQTT_QOS_LEVEL2:
                     flags |= (MQTT_CONNECT_WILL_FLAG | MQTT_CONNECT_WILL_QOS2); //只有一次
              break;
             
              default:
              return 2;
       }
      
       //主要用于PUBLISH(发布态)的消息,表示服务器要保留这次推送的信息,如果有新的订阅者出现,就把这消息推送给它。如果不设那么推送至当前订阅的就释放了
       if(will_retain)
       {
              flags |= (MQTT_CONNECT_WILL_FLAG | MQTT_CONNECT_WILL_RETAIN);
       }
      
       //账号为空 密码为空---------------------------------------------------------------------
       if(!user || !password)
       {
              return 3;
       }
       flags |= MQTT_CONNECT_USER_NAME | MQTT_CONNECT_PASSORD;
      
       total_len += strlen(user) + strlen(password) + 4;
      
       //分配内存-----------------------------------------------------------------------------
       MQTT_NewBuffer(mqttPacket, total_len);
       if(mqttPacket->_data == NULL)
              return 4;
      
       memset(mqttPacket->_data, 0, total_len);
      
/*************************************固定头部***********************************************/
      
       //固定头部----------------------连接请求类型---------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_CONNECT << 4;
      
       //固定头部----------------------剩余长度值-----------------------------------------------
       len = MQTT_DumpLength(total_len - 5, mqttPacket->_data + mqttPacket->_len);
       if(len < 0)
       {
              MQTT_DeleteBuffer(mqttPacket);
              return 5;
       }
       else
              mqttPacket->_len += len;
      
/*************************************可变头部***********************************************/
      
       //可变头部----------------------协议名长度 和 协议名--------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = 0;
       mqttPacket->_data[mqttPacket->_len++] = 4;
       mqttPacket->_data[mqttPacket->_len++] = 'M';
       mqttPacket->_data[mqttPacket->_len++] = 'Q';
       mqttPacket->_data[mqttPacket->_len++] = 'T';
       mqttPacket->_data[mqttPacket->_len++] = 'T';
      
       //可变头部----------------------protocol level 4-----------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = 4;
      
       //可变头部----------------------连接标志(该函数开头处理的数据)-----------------------------
    mqttPacket->_data[mqttPacket->_len++] = flags;
      
       //可变头部----------------------保持连接的时间(秒)----------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(cTime);
       mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(cTime);
        
/*************************************消息体************************************************/
 
       //消息体----------------------------devid长度、devid-------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(devid_len);
       mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(devid_len);
      
       strncat((int8 *)mqttPacket->_data + mqttPacket->_len, devid, devid_len);
       mqttPacket->_len += devid_len;
      
       //消息体----------------------------will_flag 和 will_msg---------------------------------
       if(flags & MQTT_CONNECT_WILL_FLAG)
       {
              unsigned short mLen = 0;
             
              if(!will_msg)
                     will_msg = "";
             
              mLen = strlen(will_topic);
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(mLen);
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(mLen);
              strncat((int8 *)mqttPacket->_data + mqttPacket->_len, will_topic, mLen);
              mqttPacket->_len += mLen;
             
              mLen = strlen(will_msg);
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(mLen);
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(mLen);
              strncat((int8 *)mqttPacket->_data + mqttPacket->_len, will_msg, mLen);
              mqttPacket->_len += mLen;
       }
      
       //消息体----------------------------use---------------------------------------------------
       if(flags & MQTT_CONNECT_USER_NAME)
       {
              unsigned short user_len = strlen(user);
             
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(user_len);
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(user_len);
              strncat((int8 *)mqttPacket->_data + mqttPacket->_len, user, user_len);
              mqttPacket->_len += user_len;
       }
 
       //消息体----------------------------password----------------------------------------------
       if(flags & MQTT_CONNECT_PASSORD)
       {
              unsigned short psw_len = strlen(password);
             
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(psw_len);
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(psw_len);
              strncat((int8 *)mqttPacket->_data + mqttPacket->_len, password, psw_len);
              mqttPacket->_len += psw_len;
       }
 
       return 0;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketDisConnect
//
//    函数功能:   断开连接消息组包
//
//    入口参数:   mqttPacket:包指针
//
//    返回参数:   0-成功          1-失败
//
//    说明:         
//==========================================================
uint1 MQTT_PacketDisConnect(MQTT_PACKET_STRUCTURE *mqttPacket)
{
 
       MQTT_NewBuffer(mqttPacket, 2);
       if(mqttPacket->_data == NULL)
              return 1;
      
/*************************************固定头部***********************************************/
      
       //固定头部----------------------头部消息-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_DISCONNECT << 4;
      
       //固定头部----------------------剩余长度值-----------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = 0;
      
       return 0;
 
}
 
//==========================================================
//    函数名称:   MQTT_UnPacketConnectAck
//
//    函数功能:   连接消息解包
//
//    入口参数:   rev_data:接收的数据
//
//    返回参数:   1、255-失败       其他-平台的返回码
//
//    说明:         
//==========================================================
uint8 MQTT_UnPacketConnectAck(uint8 *rev_data)
{
 
       if(rev_data[1] != 2)
              return 1;
      
       if(rev_data[2] == 0 || rev_data[2] == 1)
              return rev_data[3];
       else
              return 255;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketSaveData
//
//    函数功能:   数据点上传组包
//
//    入口参数:   devid:设备ID(可为空)
//                         send_buf:json缓存buf
//                         send_len:json总长
//                         type_bin_head:bin文件的消息头
//                         type:类型
//
//    返回参数:   0-成功          1-失败
//
//    说明:         
//==========================================================
uint1 MQTT_PacketSaveData(const int8 *devid, int16 send_len, int8 *type_bin_head, uint8 type, MQTT_PACKET_STRUCTURE *mqttPacket)
{
 
       if(MQTT_PacketPublish(MQTT_PUBLISH_ID, "$dp", NULL, send_len + 3, MQTT_QOS_LEVEL1, 0, 1, mqttPacket) == 0)
       {
              mqttPacket->_data[mqttPacket->_len++] = type;                            //类型
             
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(send_len);
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(send_len);
       }
       else
              return 1;
      
       return 0;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketSaveBinData
//
//    函数功能:   为禁止文件上传组包
//
//    入口参数:   name:数据流名字
//                         file_len:文件长度
//                         mqttPacket:包指针
//
//    返回参数:   0-成功          1-失败
//
//    说明:         
//==========================================================
uint1 MQTT_PacketSaveBinData(const int8 *name, int16 file_len, MQTT_PACKET_STRUCTURE *mqttPacket)
{
 
       uint1 result = 1;
       int8 *bin_head = NULL;
       uint8 bin_head_len = 0;
       int8 *payload = NULL;
       int32 payload_size = 0;
      
       bin_head = (int8 *)MQTT_MallocBuffer(13 + strlen(name));
       if(bin_head == NULL)
              return result;
      
       sprintf(bin_head, "{\"ds_id\":\"%s\"}", name);
      
       bin_head_len = strlen(bin_head);
       payload_size = 7 + bin_head_len + file_len;
      
       payload = (int8 *)MQTT_MallocBuffer(payload_size - file_len);
       if(payload == NULL)
       {
              MQTT_FreeBuffer(bin_head);
             
              return result;
       }
      
       payload[0] = 2;                                        //类型
             
       payload[1] = MOSQ_MSB(bin_head_len);
       payload[2] = MOSQ_LSB(bin_head_len);
      
       memcpy(payload + 3, bin_head, bin_head_len);
      
       payload[bin_head_len + 3] = (file_len >> 24) & 0xFF;
       payload[bin_head_len + 4] = (file_len >> 16) & 0xFF;
       payload[bin_head_len + 5] = (file_len >> 8) & 0xFF;
       payload[bin_head_len + 6] = file_len & 0xFF;
      
       if(MQTT_PacketPublish(MQTT_PUBLISH_ID, "$dp", payload, payload_size, MQTT_QOS_LEVEL1, 0, 1, mqttPacket) == 0)
              result = 0;
      
       MQTT_FreeBuffer(bin_head);
       MQTT_FreeBuffer(payload);
      
       return result;
 
}
 
//==========================================================
//    函数名称:   MQTT_UnPacketCmd
//
//    函数功能:   命令下发解包
//
//    入口参数:   rev_data:接收的数据指针
//                         cmdid:cmdid-uuid
//                         req:命令
//
//    返回参数:   0-成功          其他-失败原因
//
//    说明:         
//==========================================================
uint8 MQTT_UnPacketCmd(uint8 *rev_data, int8 **cmdid, int8 **req, uint16 *req_len)
{
 
       int8 *dataPtr = strchr((int8 *)rev_data + 6, '/');       //加6是跳过头信息
      
       uint32 remain_len = 0;
      
       if(dataPtr == NULL)                                                             //未找到'/'
              return 1;
       dataPtr++;                                                                          //跳过'/'
      
       MQTT_ReadLength(rev_data + 1, 4, &remain_len);       //读取剩余字节
      
       *cmdid = (int8 *)MQTT_MallocBuffer(37);                     //cmdid固定36字节,多分配一个结束符的位置
       if(*cmdid == NULL)
              return 2;
      
       memset(*cmdid, 0, 37);                                                        //全部清零
       memcpy(*cmdid, (const int8 *)dataPtr, 36);                   //复制cmdid
       dataPtr += 36;
      
       *req_len = remain_len - 44;                                                 //命令长度 = 剩余长度(remain_len) - 2 - 5($creq) - 1(\) - cmdid长度
       *req = (int8 *)MQTT_MallocBuffer(*req_len + 1);           //分配命令长度+1
       if(*req == NULL)
       {
              MQTT_FreeBuffer(*cmdid);
              return 3;
       }
      
       memset(*req, 0, *req_len + 1);                                      //清零
       memcpy(*req, (const int8 *)dataPtr, *req_len);        //复制命令
      
       return 0;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketCmdResp
//
//    函数功能:   命令回复组包
//
//    入口参数:   cmdid:cmdid
//                         req:命令
//                         mqttPacket:包指针
//
//    返回参数:   0-成功          1-失败
//
//    说明:         
//==========================================================
uint1 MQTT_PacketCmdResp(const int8 *cmdid, const int8 *req, MQTT_PACKET_STRUCTURE *mqttPacket)
{
      
       uint16 cmdid_len = strlen(cmdid);
       uint16 req_len = strlen(req);
       _Bool status = 0;
      
       int8 *payload = MQTT_MallocBuffer(cmdid_len + 7);
       if(payload == NULL)
              return 1;
      
       memset(payload, 0, cmdid_len + 7);
       memcpy(payload, "$crsp/", 6);
       strncat(payload, cmdid, cmdid_len);
 
       if(MQTT_PacketPublish(MQTT_PUBLISH_ID, payload, req, strlen(req), MQTT_QOS_LEVEL0, 0, 1, mqttPacket) == 0)
              status = 0;
       else
              status = 1;
      
       MQTT_FreeBuffer(payload);
      
       return status;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketSubscribe
//
//    函数功能:   Subscribe消息组包
//
//    入口参数:   pkt_id:pkt_id
//                         qos:消息重发次数
//                         topics:订阅的消息
//                         topics_cnt:订阅的消息个数
//                         mqttPacket:包指针
//
//    返回参数:   0-成功          其他-失败
//
//    说明:         
//==========================================================
uint8 MQTT_PacketSubscribe(uint16 pkt_id, enum MqttQosLevel qos, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket)
{
      
       uint32 topic_len = 0, remain_len = 0;
       int16 len = 0;
       uint8 i = 0;
      
       if(pkt_id == 0)
              return 1;
      
       //计算topic长度-------------------------------------------------------------------------
       for(; i < topics_cnt; i++)
       {
              if(topics[i] == NULL)
                     return 2;
             
              topic_len += strlen(topics[i]);
       }
      
       //2 bytes packet id + topic filter(2 bytes topic + topic length + 1 byte reserve)------
       remain_len = 2 + 3 * topics_cnt + topic_len;
      
       //分配内存------------------------------------------------------------------------------
       MQTT_NewBuffer(mqttPacket, remain_len + 5);
       if(mqttPacket->_data == NULL)
              return 3;
      
/*************************************固定头部***********************************************/
      
       //固定头部----------------------头部消息-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_SUBSCRIBE << 4 | 0x02;
      
       //固定头部----------------------剩余长度值-----------------------------------------------
       len = MQTT_DumpLength(remain_len, mqttPacket->_data + mqttPacket->_len);
       if(len < 0)
       {
              MQTT_DeleteBuffer(mqttPacket);
              return 4;
       }
       else
              mqttPacket->_len += len;
      
/*************************************payload***********************************************/
      
       //payload----------------------pkt_id---------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(pkt_id);
       mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(pkt_id);
      
       //payload----------------------topic_name-----------------------------------------------
       for(i = 0; i < topics_cnt; i++)
       {
              topic_len = strlen(topics[i]);
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(topic_len);
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(topic_len);
             
              strncat((int8 *)mqttPacket->_data + mqttPacket->_len, topics[i], topic_len);
              mqttPacket->_len += topic_len;
             
              mqttPacket->_data[mqttPacket->_len++] = qos & 0xFF;
       }
 
       return 0;
 
}
 
//==========================================================
//    函数名称:   MQTT_UnPacketSubscrebe
//
//    函数功能:   Subscribe的回复消息解包
//
//    入口参数:   rev_data:接收到的信息
//
//    返回参数:   0-成功          其他-失败
//
//    说明:         
//==========================================================
uint8 MQTT_UnPacketSubscribe(uint8 *rev_data)
{
      
       uint8 result = 255;
 
       if(rev_data[2] == MOSQ_MSB(MQTT_SUBSCRIBE_ID) && rev_data[3] == MOSQ_LSB(MQTT_SUBSCRIBE_ID))
       {
              switch(rev_data[4])
              {
                     case 0x00:
                     case 0x01:
                     case 0x02:
                            //MQTT Subscribe OK
                            result = 0;
                     break;
                    
                     case 0x80:
                            //MQTT Subscribe Failed
                            result = 1;
                     break;
                    
                     default:
                            //MQTT Subscribe UnKnown Err
                            result = 2;
                     break;
              }
       }
      
       return result;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketUnSubscribe
//
//    函数功能:   UnSubscribe消息组包
//
//    入口参数:   pkt_id:pkt_id
//                         qos:消息重发次数
//                         topics:订阅的消息
//                         topics_cnt:订阅的消息个数
//                         mqttPacket:包指针
//
//    返回参数:   0-成功          其他-失败
//
//    说明:         
//==========================================================
uint8 MQTT_PacketUnSubscribe(uint16 pkt_id, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket)
{
      
       uint32 topic_len = 0, remain_len = 0;
       int16 len = 0;
       uint8 i = 0;
      
       if(pkt_id == 0)
              return 1;
      
       //计算topic长度-------------------------------------------------------------------------
       for(; i < topics_cnt; i++)
       {
              if(topics[i] == NULL)
                     return 2;
             
              topic_len += strlen(topics[i]);
       }
      
       //2 bytes packet id, 2 bytes topic length + topic + 1 byte reserve---------------------
       remain_len = 2 + (topics_cnt << 1) + topic_len;
      
       //分配内存------------------------------------------------------------------------------
       MQTT_NewBuffer(mqttPacket, remain_len + 5);
       if(mqttPacket->_data == NULL)
              return 3;
      
/*************************************固定头部***********************************************/
      
       //固定头部----------------------头部消息-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_UNSUBSCRIBE << 4 | 0x02;
      
       //固定头部----------------------剩余长度值-----------------------------------------------
       len = MQTT_DumpLength(remain_len, mqttPacket->_data + mqttPacket->_len);
       if(len < 0)
       {
              MQTT_DeleteBuffer(mqttPacket);
              return 4;
       }
       else
              mqttPacket->_len += len;
      
/*************************************payload***********************************************/
      
       //payload----------------------pkt_id---------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(pkt_id);
       mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(pkt_id);
      
       //payload----------------------topic_name-----------------------------------------------
       for(i = 0; i < topics_cnt; i++)
       {
              topic_len = strlen(topics[i]);
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(topic_len);
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(topic_len);
             
              strncat((int8 *)mqttPacket->_data + mqttPacket->_len, topics[i], topic_len);
              mqttPacket->_len += topic_len;
       }
 
       return 0;
 
}
 
//==========================================================
//    函数名称:   MQTT_UnPacketUnSubscribe
//
//    函数功能:   UnSubscribe的回复消息解包
//
//    入口参数:   rev_data:接收到的信息
//
//    返回参数:   0-成功          其他-失败
//
//    说明:         
//==========================================================
uint1 MQTT_UnPacketUnSubscribe(uint8 *rev_data)
{
      
       uint1 result = 1;
 
       if(rev_data[2] == MOSQ_MSB(MQTT_UNSUBSCRIBE_ID) && rev_data[3] == MOSQ_LSB(MQTT_UNSUBSCRIBE_ID))
       {
              result = 0;
       }
      
       return result;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketPublish
//
//    函数功能:   Pulish消息组包
//
//    入口参数:   pkt_id:pkt_id
//                         topic:发布的topic
//                         payload:消息体
//                         payload_len:消息体长度
//                         qos:重发次数
//                         retain:离线消息推送
//                         own:
//                         mqttPacket:包指针
//
//    返回参数:   0-成功          其他-失败
//
//    说明:         
//==========================================================
uint8 MQTT_PacketPublish(uint16 pkt_id, const int8 *topic,
                                          const int8 *payload, uint32 payload_len,
                                          enum MqttQosLevel qos, int32 retain, int32 own,
                                          MQTT_PACKET_STRUCTURE *mqttPacket)
{
 
       uint32 total_len = 0, topic_len = 0;
       uint32 data_len = 0;
       int32 len = 0;
       uint8 flags = 0;
      
       //pkt_id检查----------------------------------------------------------------------------
       if(pkt_id == 0)
              return 1;
      
       //$dp为系统上传数据点的指令--------------------------------------------------------------
       for(topic_len = 0; topic[topic_len] != '\0'; ++topic_len)
       {
              if((topic[topic_len] == '#') || (topic[topic_len] == '+'))
                     return 2;
       }
      
       //Publish消息---------------------------------------------------------------------------
       flags |= MQTT_PKT_PUBLISH << 4;
      
       //retain标志----------------------------------------------------------------------------
       if(retain)
              flags |= 0x01;
      
       //总长度--------------------------------------------------------------------------------
       total_len = topic_len + payload_len + 2;
      
       //qos级别--主要用于PUBLISH(发布态)消息的,保证消息传递的次数-----------------------------
       switch(qos)
       {
              case MQTT_QOS_LEVEL0:
                     flags |= MQTT_CONNECT_WILL_QOS0;   //最多一次
              break;
             
              case MQTT_QOS_LEVEL1:
                     flags |= 0x02;                                   //最少一次
                     total_len += 2;
              break;
             
              case MQTT_QOS_LEVEL2:
                     flags |= 0x04;                                   //只有一次
                     total_len += 2;
              break;
             
              default:
              return 3;
       }
      
       //分配内存------------------------------------------------------------------------------
       if(payload != NULL)
       {
              if(payload[0] == 2)
              {
                     uint32 data_len_t = 0;
                    
                     while(payload[data_len_t++] != '}');
                     data_len_t -= 3;
                     data_len = data_len_t + 7;
                     data_len_t = payload_len - data_len;
                    
                     MQTT_NewBuffer(mqttPacket, total_len + 3 - data_len_t);
                    
                     if(mqttPacket->_data == NULL)
                            return 4;
                    
                     memset(mqttPacket->_data, 0, total_len + 3 - data_len_t);
              }
              else
              {
                     MQTT_NewBuffer(mqttPacket, total_len + 5);
                    
                     if(mqttPacket->_data == NULL)
                            return 4;
                    
                     memset(mqttPacket->_data, 0, total_len + 5);
              }
       }
       else
       {
              MQTT_NewBuffer(mqttPacket, total_len + 5);
             
              if(mqttPacket->_data == NULL)
                     return 4;
             
              memset(mqttPacket->_data, 0, total_len + 5);
       }
      
/*************************************固定头部***********************************************/
      
       //固定头部----------------------头部消息-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = flags;
      
       //固定头部----------------------剩余长度值-----------------------------------------------
       len = MQTT_DumpLength(total_len, mqttPacket->_data + mqttPacket->_len);
       if(len < 0)
       {
              MQTT_DeleteBuffer(mqttPacket);
              return 5;
       }
       else
              mqttPacket->_len += len;
      
/*************************************可变头部***********************************************/
      
       //可变头部----------------------写入topic长度、topic-------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(topic_len);
       mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(topic_len);
      
       strncat((int8 *)mqttPacket->_data + mqttPacket->_len, topic, topic_len);
       mqttPacket->_len += topic_len;
       if(qos != MQTT_QOS_LEVEL0)
       {
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(pkt_id);
              mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(pkt_id);
       }
      
       //可变头部----------------------写入payload----------------------------------------------
       if(payload != NULL)
       {
              if(payload[0] == 2)
              {
                     memcpy((int8 *)mqttPacket->_data + mqttPacket->_len, payload, data_len);
                     mqttPacket->_len += data_len;
              }
              else
              {
                     memcpy((int8 *)mqttPacket->_data + mqttPacket->_len, payload, payload_len);
                     mqttPacket->_len += payload_len;
              }
       }
      
       return 0;
 
}
 
//==========================================================
//    函数名称:   MQTT_UnPacketPublish
//
//    函数功能:   Publish消息解包
//
//    入口参数:   flags:MQTT相关标志信息
//                         pkt:指向可变头部
//                         size:固定头部中的剩余长度信息
//
//    返回参数:   0-成功          其他-失败原因
//
//    说明:         
//==========================================================
uint8 MQTT_UnPacketPublish(uint8 *rev_data, int8 **topic, uint16 *topic_len, int8 **payload, uint16 *payload_len, uint8 *qos, uint16 *pkt_id)
{
      
       const int8 flags = rev_data[0] & 0x0F;
       uint8 *msgPtr;
       uint32 remain_len = 0;
 
       const int8 dup = flags & 0x08;
 
       *qos = (flags & 0x06) >> 1;
      
       msgPtr = rev_data + MQTT_ReadLength(rev_data + 1, 4, &remain_len) + 1;
      
       if(remain_len < 2 || flags & 0x01)                                                //retain
              return 255;
      
       *topic_len = (uint16)msgPtr[0] << 8 | msgPtr[1];
       if(remain_len < *topic_len + 2)
              return 255;
      
       if(strstr((int8 *)msgPtr + 2, CMD_TOPIC_PREFIX) != NULL)    //如果是命令下发
              return MQTT_PKT_CMD;
      
       switch(*qos)
       {
              case MQTT_QOS_LEVEL0:                                                          // qos0 have no packet identifier
                    
                     if(0 != dup)
                            return 255;
 
                     *topic = MQTT_MallocBuffer(*topic_len + 1);                //为topic分配内存
                     if(*topic == NULL)
                            return 255;
                    
                     memset(*topic, 0, *topic_len + 1);
                     memcpy(*topic, (int8 *)msgPtr + 2, *topic_len);             //复制数据
                    
                     *payload_len = remain_len - 2 - *topic_len;                  //为payload分配内存
                     *payload = MQTT_MallocBuffer(*payload_len + 1);
                     if(*payload == NULL)                                                    //如果失败
                     {
                            MQTT_FreeBuffer(*topic);                                      //则需要把topic的内存释放掉
                            return 255;
                     }
                    
                     memset(*payload, 0, *payload_len + 1);
                     memcpy(*payload, (int8 *)msgPtr + 2 + *topic_len, *payload_len);
                    
              break;
 
              case MQTT_QOS_LEVEL1:
              case MQTT_QOS_LEVEL2:
                    
                     if(*topic_len + 2 > remain_len)
                            return 255;
                    
                     *pkt_id = (uint16)msgPtr[*topic_len + 2] << 8 | msgPtr[*topic_len + 3];
                     if(pkt_id == 0)
                            return 255;
                    
                     *topic = MQTT_MallocBuffer(*topic_len + 1);                //为topic分配内存
                     if(*topic == NULL)
                            return 255;
                    
                     memset(*topic, 0, *topic_len + 1);
                     memcpy(*topic, (int8 *)msgPtr + 2, *topic_len);             //复制数据
                    
                     *payload_len = remain_len - 4 - *topic_len;
                     *payload = MQTT_MallocBuffer(*payload_len + 1);              //为payload分配内存
                     if(*payload == NULL)                                                    //如果失败
                     {
                            MQTT_FreeBuffer(*topic);                                      //则需要把topic的内存释放掉
                            return 255;
                     }
                    
                     memset(*payload, 0, *payload_len + 1);
                     memcpy(*payload, (int8 *)msgPtr + 4 + *topic_len, *payload_len);
                    
              break;
 
              default:
                     return 255;
       }
      
       if(strchr((int8 *)topic, '+') || strchr((int8 *)topic, '#'))
              return 255;
 
       return 0;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketPublishAck
//
//    函数功能:   Publish Ack消息组包
//
//    入口参数:   pkt_id:packet id
//                         mqttPacket:包指针
//
//    返回参数:   0-成功          1-失败原因
//
//    说明:          当收到的Publish消息的QoS等级为1时,需要Ack回复
//==========================================================
uint1 MQTT_PacketPublishAck(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket)
{
 
       MQTT_NewBuffer(mqttPacket, 4);
       if(mqttPacket->_data == NULL)
              return 1;
      
/*************************************固定头部***********************************************/
      
       //固定头部----------------------头部消息-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_PUBACK << 4;
      
       //固定头部----------------------剩余长度-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = 2;
      
/*************************************可变头部***********************************************/
      
       //可变头部----------------------pkt_id长度-----------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = pkt_id >> 8;
       mqttPacket->_data[mqttPacket->_len++] = pkt_id & 0xff;
      
       return 0;
 
}
 
//==========================================================
//    函数名称:   MQTT_UnPacketPublishAck
//
//    函数功能:   Publish Ack消息解包
//
//    入口参数:   rev_data:收到的数据
//
//    返回参数:   0-成功          1-失败原因
//
//    说明:         
//==========================================================
uint1 MQTT_UnPacketPublishAck(uint8 *rev_data)
{
 
       if(rev_data[1] != 2)
              return 1;
 
       if(rev_data[2] == MOSQ_MSB(MQTT_PUBLISH_ID) && rev_data[3] == MOSQ_LSB(MQTT_PUBLISH_ID))
              return 0;
       else
              return 1;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketPublishRec
//
//    函数功能:   Publish Rec消息组包
//
//    入口参数:   pkt_id:packet id
//                         mqttPacket:包指针
//
//    返回参数:   0-成功          1-失败原因
//
//    说明:          当收到的Publish消息的QoS等级为2时,先收到rec
//==========================================================
uint1 MQTT_PacketPublishRec(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket)
{
 
       MQTT_NewBuffer(mqttPacket, 4);
       if(mqttPacket->_data == NULL)
              return 1;
      
/*************************************固定头部***********************************************/
      
       //固定头部----------------------头部消息-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_PUBREC << 4;
      
       //固定头部----------------------剩余长度-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = 2;
      
/*************************************可变头部***********************************************/
      
       //可变头部----------------------pkt_id长度-----------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = pkt_id >> 8;
       mqttPacket->_data[mqttPacket->_len++] = pkt_id & 0xff;
      
       return 0;
 
}
 
//==========================================================
//    函数名称:   MQTT_UnPacketPublishRec
//
//    函数功能:   Publish Rec消息解包
//
//    入口参数:   rev_data:接收到的数据
//
//    返回参数:   0-成功          1-失败
//
//    说明:         
//==========================================================
uint1 MQTT_UnPacketPublishRec(uint8 *rev_data)
{
 
       if(rev_data[1] != 2)
              return 1;
 
       if(rev_data[2] == MOSQ_MSB(MQTT_PUBLISH_ID) && rev_data[3] == MOSQ_LSB(MQTT_PUBLISH_ID))
              return 0;
       else
              return 1;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketPublishRel
//
//    函数功能:   Publish Rel消息组包
//
//    入口参数:   pkt_id:packet id
//                         mqttPacket:包指针
//
//    返回参数:   0-成功          1-失败原因
//
//    说明:          当收到的Publish消息的QoS等级为2时,先收到rec,再回复rel
//==========================================================
uint1 MQTT_PacketPublishRel(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket)
{
 
       MQTT_NewBuffer(mqttPacket, 4);
       if(mqttPacket->_data == NULL)
              return 1;
      
/*************************************固定头部***********************************************/
      
       //固定头部----------------------头部消息-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_PUBREL << 4 | 0x02;
      
       //固定头部----------------------剩余长度-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = 2;
      
/*************************************可变头部***********************************************/
      
       //可变头部----------------------pkt_id长度-----------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = pkt_id >> 8;
       mqttPacket->_data[mqttPacket->_len++] = pkt_id & 0xff;
      
       return 0;
 
}
 
//==========================================================
//    函数名称:   MQTT_UnPacketPublishRel
//
//    函数功能:   Publish Rel消息解包
//
//    入口参数:   rev_data:接收到的数据
//
//    返回参数:   0-成功          1-失败
//
//    说明:         
//==========================================================
uint1 MQTT_UnPacketPublishRel(uint8 *rev_data, uint16 pkt_id)
{
 
       if(rev_data[1] != 2)
              return 1;
 
       if(rev_data[2] == MOSQ_MSB(pkt_id) && rev_data[3] == MOSQ_LSB(pkt_id))
              return 0;
       else
              return 1;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketPublishComp
//
//    函数功能:   Publish Comp消息组包
//
//    入口参数:   pkt_id:packet id
//                         mqttPacket:包指针
//
//    返回参数:   0-成功          1-失败原因
//
//    说明:          当收到的Publish消息的QoS等级为2时,先收到rec,再回复rel
//==========================================================
uint1 MQTT_PacketPublishComp(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket)
{
 
       MQTT_NewBuffer(mqttPacket, 4);
       if(mqttPacket->_data == NULL)
              return 1;
      
/*************************************固定头部***********************************************/
      
       //固定头部----------------------头部消息-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_PUBCOMP << 4;
      
       //固定头部----------------------剩余长度-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = 2;
      
/*************************************可变头部***********************************************/
      
       //可变头部----------------------pkt_id长度-----------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = pkt_id >> 8;
       mqttPacket->_data[mqttPacket->_len++] = pkt_id & 0xff;
      
       return 0;
 
}
 
//==========================================================
//    函数名称:   MQTT_UnPacketPublishComp
//
//    函数功能:   Publish Comp消息解包
//
//    入口参数:   rev_data:接收到的数据
//
//    返回参数:   0-成功          1-失败
//
//    说明:         
//==========================================================
uint1 MQTT_UnPacketPublishComp(uint8 *rev_data)
{
 
       if(rev_data[1] != 2)
              return 1;
 
       if(rev_data[2] == MOSQ_MSB(MQTT_PUBLISH_ID) && rev_data[3] == MOSQ_LSB(MQTT_PUBLISH_ID))
              return 0;
       else
              return 1;
 
}
 
//==========================================================
//    函数名称:   MQTT_PacketPing
//
//    函数功能:   心跳请求组包
//
//    入口参数:   mqttPacket:包指针
//
//    返回参数:   0-成功          1-失败
//
//    说明:         
//==========================================================
uint1 MQTT_PacketPing(MQTT_PACKET_STRUCTURE *mqttPacket)
{
 
       MQTT_NewBuffer(mqttPacket, 2);
       if(mqttPacket->_data == NULL)
              return 1;
      
/*************************************固定头部***********************************************/
      
       //固定头部----------------------头部消息-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_PINGREQ << 4;
      
       //固定头部----------------------剩余长度-------------------------------------------------
       mqttPacket->_data[mqttPacket->_len++] = 0;
      
       return 0;
 
}
//==========================================================
 
MQTT的H文件:
#ifndef _MQTTKIT_H_
#define _MQTTKIT_H_
 
 
#include "Common.h"
 
 
//=============================配置==============================
//===========可以提供RTOS的内存管理方案,也可以使用C库的=========
//RTOS
#include <stdlib.h>
 
#define MQTT_MallocBuffer      malloc
 
#define MQTT_FreeBuffer          free
//==========================================================
 
 
#define MOSQ_MSB(A)         (uint8)((A & 0xFF00) >> 8)
#define MOSQ_LSB(A)         (uint8)(A & 0x00FF)
 
 
/*--------------------------------内存分配方案标志--------------------------------*/
#define MEM_FLAG_NULL         0
#define MEM_FLAG_ALLOC              1
#define MEM_FLAG_STATIC             2
 
 
typedef struct Buffer
{
      
       uint8      *_data;          //协议数据
      
       uint32    _len;              //写入的数据长度
      
       uint32    _size;             //缓存总大小
      
       uint8      _memFlag;    //内存使用的方案:0-未分配   1-使用的动态分配             2-使用的固定内存
      
} MQTT_PACKET_STRUCTURE;
 
 
/*--------------------------------固定头部消息类型--------------------------------*/
enum MqttPacketType
{
      
    MQTT_PKT_CONNECT = 1, /**< 连接请求数据包 */
    MQTT_PKT_CONNACK,     /**< 连接确认数据包 */
    MQTT_PKT_PUBLISH,     /**< 发布数据数据包 */
    MQTT_PKT_PUBACK,      /**< 发布确认数据包 */
    MQTT_PKT_PUBREC,      /**< 发布数据已接收数据包,Qos 2时,回复MQTT_PKT_PUBLISH */
    MQTT_PKT_PUBREL,      /**< 发布数据释放数据包, Qos 2时,回复MQTT_PKT_PUBREC */
    MQTT_PKT_PUBCOMP,     /**< 发布完成数据包, Qos 2时,回复MQTT_PKT_PUBREL */
    MQTT_PKT_SUBSCRIBE,   /**< 订阅数据包 */
    MQTT_PKT_SUBACK,      /**< 订阅确认数据包 */
    MQTT_PKT_UNSUBSCRIBE, /**< 取消订阅数据包 */
    MQTT_PKT_UNSUBACK,    /**< 取消订阅确认数据包 */
    MQTT_PKT_PINGREQ,     /**< ping 数据包 */
    MQTT_PKT_PINGRESP,    /**< ping 响应数据包 */
    MQTT_PKT_DISCONNECT,  /**< 断开连接数据包 */
      
       //新增
      
       MQTT_PKT_CMD               /**< 命令下发数据包 */
      
};
 
 
/*--------------------------------MQTT QOS等级--------------------------------*/
enum MqttQosLevel
{
      
    MQTT_QOS_LEVEL0,  /**< 最多发送一次 */
    MQTT_QOS_LEVEL1,  /**< 最少发送一次  */
    MQTT_QOS_LEVEL2   /**< 只发送一次 */
      
};
 
 
/*--------------------------------MQTT 连接请求标志位,内部使用--------------------------------*/
enum MqttConnectFlag
{
      
    MQTT_CONNECT_CLEAN_SESSION  = 0x02,
    MQTT_CONNECT_WILL_FLAG      = 0x04,
    MQTT_CONNECT_WILL_QOS0      = 0x00,
    MQTT_CONNECT_WILL_QOS1      = 0x08,
    MQTT_CONNECT_WILL_QOS2      = 0x10,
    MQTT_CONNECT_WILL_RETAIN    = 0x20,
    MQTT_CONNECT_PASSORD        = 0x40,
    MQTT_CONNECT_USER_NAME      = 0x80
      
};
 
 
/*--------------------------------消息的packet ID,可自定义--------------------------------*/
#define MQTT_PUBLISH_ID              10
 
#define MQTT_SUBSCRIBE_ID           20
 
#define MQTT_UNSUBSCRIBE_ID             30
 
 
/*--------------------------------删包--------------------------------*/
void MQTT_DeleteBuffer(MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------解包--------------------------------*/
uint8 MQTT_UnPacketRecv(uint8 *dataPtr);
 
/*--------------------------------登录组包--------------------------------*/
uint8 MQTT_PacketConnect(const int8 *user, const int8 *password, const int8 *devid,
                                          uint16 cTime, uint1 clean_session, uint1 qos,
                                          const int8 *will_topic, const int8 *will_msg, int32 will_retain,
                                          MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------断开连接组包--------------------------------*/
uint1 MQTT_PacketDisConnect(MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------连接响应解包--------------------------------*/
uint8 MQTT_UnPacketConnectAck(uint8 *rev_data);
 
/*--------------------------------数据点上传组包--------------------------------*/
uint1 MQTT_PacketSaveData(const int8 *devid, int16 send_len, int8 *type_bin_head, uint8 type, MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------二进制文件上传组包--------------------------------*/
uint1 MQTT_PacketSaveBinData(const int8 *name, int16 file_len, MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------命令下发解包--------------------------------*/
uint8 MQTT_UnPacketCmd(uint8 *rev_data, int8 **cmdid, int8 **req, uint16 *req_len);
 
/*--------------------------------命令回复组包--------------------------------*/
uint1 MQTT_PacketCmdResp(const int8 *cmdid, const int8 *req, MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------订阅主题组包--------------------------------*/
uint8 MQTT_PacketSubscribe(uint16 pkt_id, enum MqttQosLevel qos, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------订阅主题回复解包--------------------------------*/
uint8 MQTT_UnPacketSubscribe(uint8 *rev_data);
 
/*--------------------------------取消订阅组包--------------------------------*/
uint8 MQTT_PacketUnSubscribe(uint16 pkt_id, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------取消订阅回复解包--------------------------------*/
uint1 MQTT_UnPacketUnSubscribe(uint8 *rev_data);
 
/*--------------------------------发布主题组包--------------------------------*/
uint8 MQTT_PacketPublish(uint16 pkt_id, const int8 *topic,
                                          const int8 *payload, uint32 payload_len,
                                          enum MqttQosLevel qos, int32 retain, int32 own,
                                          MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------发布消息回复解包--------------------------------*/
uint8 MQTT_UnPacketPublish(uint8 *rev_data, int8 **topic, uint16 *topic_len, int8 **payload, uint16 *payload_len, uint8 *qos, uint16 *pkt_id);
 
/*--------------------------------发布消息的Ack组包--------------------------------*/
uint1 MQTT_PacketPublishAck(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------发布消息的Ack解包--------------------------------*/
uint1 MQTT_UnPacketPublishAck(uint8 *rev_data);
 
/*--------------------------------发布消息的Rec组包--------------------------------*/
uint1 MQTT_PacketPublishRec(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------发布消息的Rec解包--------------------------------*/
uint1 MQTT_UnPacketPublishRec(uint8 *rev_data);
 
/*--------------------------------发布消息的Rel组包--------------------------------*/
uint1 MQTT_PacketPublishRel(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------发布消息的Rel解包--------------------------------*/
uint1 MQTT_UnPacketPublishRel(uint8 *rev_data, uint16 pkt_id);
 
/*--------------------------------发布消息的Comp组包--------------------------------*/
uint1 MQTT_PacketPublishComp(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket);
 
/*--------------------------------发布消息的Comp解包--------------------------------*/
uint1 MQTT_UnPacketPublishComp(uint8 *rev_data);
 
/*--------------------------------心跳请求组包--------------------------------*/
uint1 MQTT_PacketPing(MQTT_PACKET_STRUCTURE *mqttPacket);
 
 
#endif
 
//==========================================================
 
cJSON的C文件:
 
/*
  Copyright (c) 2009 Dave Gamble
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
 
  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.
 
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/
 
/* cJSON */
/* JSON parser in C. */
 
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
#include "cJSON.h"
 
static const char *ep;
 
const char *cJSON_GetErrorPtr(void) {return ep;}
 
static int cJSON_strcasecmp(const char *s1,const char *s2)
{
       if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
       for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)     if(*s1 == 0)    return 0;
       return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}
 
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
 
static char* cJSON_strdup(const char* str)
{
      size_t len;
      char* copy;
 
      len = strlen(str) + 1;
      if (!(copy = (char*)cJSON_malloc(len))) return 0;
      memcpy(copy,str,len);
      return copy;
}
 
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
    if (!hooks) { /* Reset hooks */
        cJSON_malloc = malloc;
        cJSON_free = free;
        return;
    }
 
       cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
       cJSON_free     = (hooks->free_fn)?hooks->free_fn:free;
}
 
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
       cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
       if (node) memset(node,0,sizeof(cJSON));
       return node;
}
 
/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{
       cJSON *next;
       while (c)
       {
              next=c->next;
              if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
              if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
              if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string);
              cJSON_free(c);
              c=next;
       }
}
 
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num)
{
       double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
 
       if (*num=='-') sign=-1,num++; /* Has sign? */
       if (*num=='0') num++;                     /* is zero */
       if (*num>='1' && *num<='9')   do   n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9');  /* Number? */
       if (*num=='.' && num[1]>='0' && num[1]<='9') {num++;         do       n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');}    /* Fractional part? */
       if (*num=='e' || *num=='E')       /* Exponent? */
       {      num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++;         /* With sign? */
              while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');     /* Number? */
       }
 
       n=sign*n*pow(10.0,(scale+subscale*signsubscale));       /* number = +/- number.fraction * 10^+/- exponent */
      
       item->valuedouble=n;
       item->valueint=(int)n;
       item->type=cJSON_Number;
       return num;
}
 
static int pow2gt (int x)       {      --x; x|=x>>1;       x|=x>>2;       x|=x>>4;       x|=x>>8;       x|=x>>16;     return x+1;    }
 
typedef struct {char *buffer; int length; int offset; } printbuffer;
 
static char* ensure(printbuffer *p,int needed)
{
       char *newbuffer;int newsize;
       if (!p || !p->buffer) return 0;
       needed+=p->offset;
       if (needed<=p->length) return p->buffer+p->offset;
 
       newsize=pow2gt(needed);
       newbuffer=(char*)cJSON_malloc(newsize);
       if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;}
       if (newbuffer) memcpy(newbuffer,p->buffer,p->length);
       cJSON_free(p->buffer);
       p->length=newsize;
       p->buffer=newbuffer;
       return newbuffer+p->offset;
}
 
static int update(printbuffer *p)
{
       char *str;
       if (!p || !p->buffer) return 0;
       str=p->buffer+p->offset;
       return p->offset+strlen(str);
}
 
/* Render the number nicely from the given item into a string. */
static char *print_number(cJSON *item,printbuffer *p)
{
       char *str=0;
       double d=item->valuedouble;
       if (d==0)
       {
              if (p) str=ensure(p,2);
              else str=(char*)cJSON_malloc(2);      /* special case for 0. */
              if (str) strcpy(str,"0");
       }
       else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
       {
              if (p) str=ensure(p,21);
              else str=(char*)cJSON_malloc(21);    /* 2^64+1 can be represented in 21 chars. */
              if (str)     sprintf(str,"%d",item->valueint);
       }
       else
       {
              if (p) str=ensure(p,64);
              else str=(char*)cJSON_malloc(64);    /* This is a nice tradeoff. */
              if (str)
              {
                     if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
                     else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9)                  sprintf(str,"%e",d);
                     else                                                                              sprintf(str,"%f",d);
              }
       }
       return str;
}
 
static unsigned parse_hex4(const char *str)
{
       unsigned h=0;
       if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
       h=h<<4;str++;
       if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
       h=h<<4;str++;
       if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
       h=h<<4;str++;
       if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
       return h;
}
 
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
{
       const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
       if (*str!='\"') {ep=str;return 0;}    /* not a string! */
      
       while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++;    /* Skip escaped quotes. */
      
       out=(char*)cJSON_malloc(len+1);     /* This is how long we need for the string, roughly. */
       if (!out) return 0;
      
       ptr=str+1;ptr2=out;
       while (*ptr!='\"' && *ptr)
       {
              if (*ptr!='\\') *ptr2++=*ptr++;
              else
              {
                     ptr++;
                     switch (*ptr)
                     {
                            case 'b': *ptr2++='\b'; break;
                            case 'f': *ptr2++='\f';   break;
                            case 'n': *ptr2++='\n'; break;
                            case 'r': *ptr2++='\r';   break;
                            case 't': *ptr2++='\t';   break;
                            case 'u':   /* transcode utf16 to utf8. */
                                   uc=parse_hex4(ptr+1);ptr+=4;  /* get the unicode char. */
 
                                   if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break;     /* check for invalid.   */
 
                                   if (uc>=0xD800 && uc<=0xDBFF)   /* UTF16 surrogate pairs.    */
                                   {
                                          if (ptr[1]!='\\' || ptr[2]!='u')  break;     /* missing second-half of surrogate.      */
                                          uc2=parse_hex4(ptr+3);ptr+=6;
                                          if (uc2<0xDC00 || uc2>0xDFFF)        break;     /* invalid second-half of surrogate.  */
                                          uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
                                   }
 
                                   len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
                                  
                                   switch (len) {
                                          case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
                                          case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
                                          case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
                                          case 1: *--ptr2 =(uc | firstByteMark[len]);
                                   }
                                   ptr2+=len;
                                   break;
                            default:  *ptr2++=*ptr; break;
                     }
                     ptr++;
              }
       }
       *ptr2=0;
       if (*ptr=='\"') ptr++;
       item->valuestring=out;
       item->type=cJSON_String;
       return ptr;
}
 
/* Render the cstring provided to an escaped version that can be printed. */
static char *print_string_ptr(const char *str,printbuffer *p)
{
       const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token;
      
       for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0;
       if (!flag)
       {
              len=ptr-str;
              if (p) out=ensure(p,len+3);
              else        out=(char*)cJSON_malloc(len+3);
              if (!out) return 0;
              ptr2=out;*ptr2++='\"';
              strcpy(ptr2,str);
              ptr2[len]='\"';
              ptr2[len+1]=0;
              return out;
       }
      
       if (!str)
       {
              if (p) out=ensure(p,3);
              else out=(char*)cJSON_malloc(3);
              if (!out) return 0;
              strcpy(out,"\"\"");
              return out;
       }
       ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
      
       if (p) out=ensure(p,len+3);
       else out=(char*)cJSON_malloc(len+3);
       if (!out) return 0;
 
       ptr2=out;ptr=str;
       *ptr2++='\"';
       while (*ptr)
       {
              if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
              else
              {
                     *ptr2++='\\';
                     switch (token=*ptr++)
                     {
                            case '\\': *ptr2++='\\'; break;
                            case '\"': *ptr2++='\"'; break;
                            case '\b': *ptr2++='b';  break;
                            case '\f':  *ptr2++='f';   break;
                            case '\n': *ptr2++='n';  break;
                            case '\r': *ptr2++='r';  break;
                            case '\t': *ptr2++='t';  break;
                            default: sprintf(ptr2,"u%04x",token);ptr2+=5;  break;     /* escape and print */
                     }
              }
       }
       *ptr2++='\"';*ptr2++=0;
       return out;
}
/* Invote print_string_ptr (which is useful) on an item. */
static char *print_string(cJSON *item,printbuffer *p)      {return print_string_ptr(item->valuestring,p);}
 
/* Predeclare these prototypes. */
static const char *parse_value(cJSON *item,const char *value);
static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p);
static const char *parse_array(cJSON *item,const char *value);
static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p);
static const char *parse_object(cJSON *item,const char *value);
static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p);
 
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
 
/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
       const char *end=0;
       cJSON *c=cJSON_New_Item();
       ep=0;
       if (!c) return 0;       /* memory fail */
 
       end=parse_value(c,skip(value));
       if (!end)  {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */
 
       /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
       if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
       if (return_parse_end) *return_parse_end=end;
       return c;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
 
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item)                            {return print_value(item,0,1,0);}
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);}
 
char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt)
{
       printbuffer p;
       p.buffer=(char*)cJSON_malloc(prebuffer);
       p.length=prebuffer;
       p.offset=0;
       return print_value(item,0,fmt,&p);
       return p.buffer;
}
 
 
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value)
{
       if (!value)                                   return 0; /* Fail on null. */
       if (!strncmp(value,"null",4))  { item->type=cJSON_NULL;  return value+4; }
       if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
       if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1;       return value+4; }
       if (*value=='\"')                          { return parse_string(item,value); }
       if (*value=='-' || (*value>='0' && *value<='9'))      { return parse_number(item,value); }
       if (*value=='[')                           { return parse_array(item,value); }
       if (*value=='{')                           { return parse_object(item,value); }
 
       ep=value;return 0;       /* failure. */
}
 
/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p)
{
       char *out=0;
       if (!item) return 0;
       if (p)
       {
              switch ((item->type)&255)
              {
                     case cJSON_NULL:       {out=ensure(p,5);  if (out) strcpy(out,"null");     break;}
                     case cJSON_False: {out=ensure(p,6);  if (out) strcpy(out,"false");    break;}
                     case cJSON_True: {out=ensure(p,5);  if (out) strcpy(out,"true");    break;}
                     case cJSON_Number:  out=print_number(item,p);break;
                     case cJSON_String:      out=print_string(item,p);break;
                     case cJSON_Array:       out=print_array(item,depth,fmt,p);break;
                     case cJSON_Object:     out=print_object(item,depth,fmt,p);break;
              }
       }
       else
       {
              switch ((item->type)&255)
              {
                     case cJSON_NULL:       out=cJSON_strdup("null");  break;
                     case cJSON_False: out=cJSON_strdup("false");break;
                     case cJSON_True: out=cJSON_strdup("true"); break;
                     case cJSON_Number:  out=print_number(item,0);break;
                     case cJSON_String:      out=print_string(item,0);break;
                     case cJSON_Array:       out=print_array(item,depth,fmt,0);break;
                     case cJSON_Object:     out=print_object(item,depth,fmt,0);break;
              }
       }
       return out;
}
 
/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value)
{
       cJSON *child;
       if (*value!='[') {ep=value;return 0;}     /* not an array! */
 
       item->type=cJSON_Array;
       value=skip(value+1);
       if (*value==']') return value+1;   /* empty array. */
 
       item->child=child=cJSON_New_Item();
       if (!item->child) return 0;            /* memory fail */
       value=skip(parse_value(child,skip(value)));       /* skip any spacing, get the value. */
       if (!value) return 0;
 
       while (*value==',')
       {
              cJSON *new_item;
              if (!(new_item=cJSON_New_Item())) return 0;        /* memory fail */
              child->next=new_item;new_item->prev=child;child=new_item;
              value=skip(parse_value(child,skip(value+1)));
              if (!value) return 0;       /* memory fail */
       }
 
       if (*value==']') return value+1;   /* end of array */
       ep=value;return 0;       /* malformed. */
}
 
/* Render an array to text */
static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p)
{
       char **entries;
       char *out=0,*ptr,*ret;int len=5;
       cJSON *child=item->child;
       int numentries=0,i=0,fail=0;
       size_t tmplen=0;
      
       /* How many entries in the array? */
       while (child) numentries++,child=child->next;
       /* Explicitly handle numentries==0 */
       if (!numentries)
       {
              if (p) out=ensure(p,3);
              else out=(char*)cJSON_malloc(3);
              if (out) strcpy(out,"[]");
              return out;
       }
 
       if (p)
       {
              /* Compose the output array. */
              i=p->offset;
              ptr=ensure(p,1);if (!ptr) return 0;       *ptr='[';  p->offset++;
              child=item->child;
              while (child && !fail)
              {
                     print_value(child,depth+1,fmt,p);
                     p->offset=update(p);
                     if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;}
                     child=child->next;
              }
              ptr=ensure(p,2);if (!ptr) return 0;       *ptr++=']';*ptr=0;
              out=(p->buffer)+i;
       }
       else
       {
              /* Allocate an array to hold the values for each */
              entries=(char**)cJSON_malloc(numentries*sizeof(char*));
              if (!entries) return 0;
              memset(entries,0,numentries*sizeof(char*));
              /* Retrieve all the results: */
              child=item->child;
              while (child && !fail)
              {
                     ret=print_value(child,depth+1,fmt,0);
                     entries[i++]=ret;
                     if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
                     child=child->next;
              }
             
              /* If we didn't fail, try to malloc the output string */
              if (!fail)    out=(char*)cJSON_malloc(len);
              /* If that fails, we fail. */
              if (!out) fail=1;
 
              /* Handle failure. */
              if (fail)
              {
                     for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
                     cJSON_free(entries);
                     return 0;
              }
             
              /* Compose the output array. */
              *out='[';
              ptr=out+1;*ptr=0;
              for (i=0;i<numentries;i++)
              {
                     tmplen=strlen(entries[i]);memcpy(ptr,entries[i],tmplen);ptr+=tmplen;
                     if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
                     cJSON_free(entries[i]);
              }
              cJSON_free(entries);
              *ptr++=']';*ptr++=0;
       }
       return out;    
}
 
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value)
{
       cJSON *child;
       if (*value!='{') {ep=value;return 0;}     /* not an object! */
      
       item->type=cJSON_Object;
       value=skip(value+1);
       if (*value=='}') return value+1;   /* empty array. */
      
       item->child=child=cJSON_New_Item();
       if (!item->child) return 0;
       value=skip(parse_string(child,skip(value)));
       if (!value) return 0;
       child->string=child->valuestring;child->valuestring=0;
       if (*value!=':') {ep=value;return 0;}     /* fail! */
       value=skip(parse_value(child,skip(value+1)));   /* skip any spacing, get the value. */
       if (!value) return 0;
      
       while (*value==',')
       {
              cJSON *new_item;
              if (!(new_item=cJSON_New_Item()))  return 0; /* memory fail */
              child->next=new_item;new_item->prev=child;child=new_item;
              value=skip(parse_string(child,skip(value+1)));
              if (!value) return 0;
              child->string=child->valuestring;child->valuestring=0;
              if (*value!=':') {ep=value;return 0;}     /* fail! */
              value=skip(parse_value(child,skip(value+1)));   /* skip any spacing, get the value. */
              if (!value) return 0;
       }
      
       if (*value=='}') return value+1;   /* end of array */
       ep=value;return 0;       /* malformed. */
}
 
/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p)
{
       char **entries=0,**names=0;
       char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
       cJSON *child=item->child;
       int numentries=0,fail=0;
       size_t tmplen=0;
       /* Count the number of entries. */
       while (child) numentries++,child=child->next;
       /* Explicitly handle empty object case */
       if (!numentries)
       {
              if (p) out=ensure(p,fmt?depth+4:3);
              else out=(char*)cJSON_malloc(fmt?depth+4:3);
              if (!out)   return 0;
              ptr=out;*ptr++='{';
              if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
              *ptr++='}';*ptr++=0;
              return out;
       }
       if (p)
       {
              /* Compose the output: */
              i=p->offset;
              len=fmt?2:1;  ptr=ensure(p,len+1);   if (!ptr) return 0;
              *ptr++='{';     if (fmt) *ptr++='\n';     *ptr=0;   p->offset+=len;
              child=item->child;depth++;
              while (child)
              {
                     if (fmt)
                     {
                            ptr=ensure(p,depth);   if (!ptr) return 0;
                            for (j=0;j<depth;j++) *ptr++='\t';
                            p->offset+=depth;
                     }
                     print_string_ptr(child->string,p);
                     p->offset=update(p);
                    
                     len=fmt?2:1;
                     ptr=ensure(p,len); if (!ptr) return 0;
                     *ptr++=':';if (fmt) *ptr++='\t';
                     p->offset+=len;
                    
                     print_value(child,depth,fmt,p);
                     p->offset=update(p);
 
                     len=(fmt?1:0)+(child->next?1:0);
                     ptr=ensure(p,len+1); if (!ptr) return 0;
                     if (child->next) *ptr++=',';
                     if (fmt) *ptr++='\n';*ptr=0;
                     p->offset+=len;
                     child=child->next;
              }
              ptr=ensure(p,fmt?(depth+1):2);  if (!ptr) return 0;
              if (fmt)    for (i=0;i<depth-1;i++) *ptr++='\t';
              *ptr++='}';*ptr=0;
              out=(p->buffer)+i;
       }
       else
       {
              /* Allocate space for the names and the objects */
              entries=(char**)cJSON_malloc(numentries*sizeof(char*));
              if (!entries) return 0;
              names=(char**)cJSON_malloc(numentries*sizeof(char*));
              if (!names) {cJSON_free(entries);return 0;}
              memset(entries,0,sizeof(char*)*numentries);
              memset(names,0,sizeof(char*)*numentries);
 
              /* Collect all the results into our arrays: */
              child=item->child;depth++;if (fmt) len+=depth;
              while (child)
              {
                     names[i]=str=print_string_ptr(child->string,0);
                     entries[i++]=ret=print_value(child,depth,fmt,0);
                     if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
                     child=child->next;
              }
             
              /* Try to allocate the output string */
              if (!fail)    out=(char*)cJSON_malloc(len);
              if (!out) fail=1;
 
              /* Handle failure */
              if (fail)
              {
                     for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
                     cJSON_free(names);cJSON_free(entries);
                     return 0;
              }
             
              /* Compose the output: */
              *out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
              for (i=0;i<numentries;i++)
              {
                     if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
                     tmplen=strlen(names[i]);memcpy(ptr,names[i],tmplen);ptr+=tmplen;
                     *ptr++=':';if (fmt) *ptr++='\t';
                     strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
                     if (i!=numentries-1) *ptr++=',';
                     if (fmt) *ptr++='\n';*ptr=0;
                     cJSON_free(names[i]);cJSON_free(entries[i]);
              }
             
              cJSON_free(names);cJSON_free(entries);
              if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
              *ptr++='}';*ptr++=0;
       }
       return out;    
}
 
/* Get Array size/item / object item. */
int    cJSON_GetArraySize(cJSON *array)                                                {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
cJSON *cJSON_GetArrayItem(cJSON *array,int item)                          {cJSON *c=array->child;  while (c && item>0) item--,c=c->next; return c;}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
 
/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
/* Utility for handling references. */
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
 
/* Add item to array/object. */
void   cJSON_AddItemToArray(cJSON *array, cJSON *item)                                          {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
void   cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item)    {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
void   cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);}
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)                                       {cJSON_AddItemToArray(array,create_reference(item));}
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item)       {cJSON_AddItemToObject(object,string,create_reference(item));}
 
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which)                  {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
       if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
void   cJSON_DeleteItemFromArray(cJSON *array,int which)                   {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
void   cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
 
/* Replace array/object items with new ones. */
void   cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem)             {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;}
       newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;}
void   cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem)          {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
       newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
       if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
void   cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
 
/* Create basic types: */
cJSON *cJSON_CreateNull(void)                            {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void)                                   {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void)                                  {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b)                                  {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
cJSON *cJSON_CreateNumber(double num)                 {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
cJSON *cJSON_CreateString(const char *string)     {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void)                                  {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void)                               {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
 
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(const int *numbers,int count)             {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(const float *numbers,int count)       {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count)       {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
 
/* Duplication */
cJSON *cJSON_Duplicate(cJSON *item,int recurse)
{
       cJSON *newitem,*cptr,*nptr=0,*newchild;
       /* Bail on bad ptr */
       if (!item) return 0;
       /* Create new item */
       newitem=cJSON_New_Item();
       if (!newitem) return 0;
       /* Copy over all vars */
newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
       if (item->valuestring)   {newitem->valuestring=cJSON_strdup(item->valuestring);  if (!newitem->valuestring)     {cJSON_Delete(newitem);return 0;}}
       if (item->string)           {newitem->string=cJSON_strdup(item->string);                  if (!newitem->string)             {cJSON_Delete(newitem);return 0;}}
       /* If non-recursive, then we're done! */
       if (!recurse) return newitem;
       /* Walk the ->next chain for the child. */
       cptr=item->child;
       while (cptr)
       {
              newchild=cJSON_Duplicate(cptr,1);         /* Duplicate (with recurse) each item in the ->next chain */
              if (!newchild) {cJSON_Delete(newitem);return 0;}
              if (nptr)   {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;}     /* If newitem->child already set, then crosswire ->prev and ->next and move on */
              else        {newitem->child=newchild;nptr=newchild;}                               /* Set newitem->child and move to it */
              cptr=cptr->next;
       }
       return newitem;
}
 
void cJSON_Minify(char *json)
{
       char *into=json;
       while (*json)
       {
              if (*json==' ') json++;
              else if (*json=='\t') json++; /* Whitespace characters. */
              else if (*json=='\r') json++;
              else if (*json=='\n') json++;
              else if (*json=='/' && json[1]=='/')  while (*json && *json!='\n') json++; /* double-slash comments, to end of line. */
              else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */
              else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */
              else *into++=*json++;              /* All other characters. */
       }
       *into=0; /* and null-terminate. */
}
//==========================================================
 
cJSON的H文件:
 
/*
  Copyright (c) 2009 Dave Gamble
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
 
  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.
 
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/
 
#ifndef cJSON__h
#define cJSON__h
 
#ifdef __cplusplus
extern "C"
{
#endif
 
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
      
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
 
/* The cJSON structure: */
typedef struct cJSON {
       struct cJSON *next,*prev;    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
       struct cJSON *child;            /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
 
       int type;                              /* The type of the item, as above. */
 
       char *valuestring;                /* The item's string, if type==cJSON_String */
       int valueint;                        /* The item's number, if type==cJSON_Number */
       double valuedouble;                  /* The item's number, if type==cJSON_Number */
 
       char *string;                        /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
 
typedef struct cJSON_Hooks {
      void *(*malloc_fn)(size_t sz);
      void (*free_fn)(void *ptr);
} cJSON_Hooks;
 
/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
 
 
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char  *cJSON_Print(cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char  *cJSON_PrintUnformatted(cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
/* Delete a cJSON entity and all subentities. */
extern void   cJSON_Delete(cJSON *c);
 
/* Returns the number of items in an array (or object). */
extern int        cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
 
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);
      
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
 
/* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
 
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void    cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
extern void    cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item);       /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void    cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
 
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void   cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void   cJSON_DeleteItemFromObject(cJSON *object,const char *string);
      
/* Update array items. */
extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem);      /* Shifts pre-existing items to the right. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
 
/* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. */
 
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
 
extern void cJSON_Minify(char *json);
 
/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name)             cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name)            cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name)           cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b)  cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n)   cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
 
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val)                   ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#define cJSON_SetNumberValue(object,val)          ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
 
#ifdef __cplusplus
}
#endif
 
#endif

这就是所有连接ONENET的主文件,我们只需要更改所要连接的WIFI名和密码、项目的产品ID、鉴权信息,设备ID即可连接上网;

6.png

产品ID在这里:

7.png

设备ID和鉴权信息在产品详情里:

8.png

至此,我们联网的配置就差不多了,然后就是发送数据和接收数据,其中发送数据的函数我们可以自己添加想要发送的数据,

9.png

接收数据的函数OneNet_RevPro中,在末尾更改要接受的数据就好:

10.png

最后来到主函数,

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_key.h"
#include "delay.h"
#include "usart.h"
#include "esp8266.h"
#include "onenet.h"
#include "AM23XX.h"
#include "timer.h"
 
u8 humidityH;        //湿度整数部分
u8 humidityL;   //湿度小数部分
u8 temperatureH;   //温度整数部分
u8 temperatureL;   //温度小数部分
DHT11_Data_TypeDef DHT11_Data;
 
u8 test1 = 1,test2 = 2,test3 = 3,test4 = 4;
extern unsigned char esp8266_buf[256];
extern unsigned short esp8266_cnt , esp8266_cntPre ;
unsigned char *dataPtr = NULL;
u16 send_tim;
int main(void)
 {   
      
 
unsigned short timeCount = 0;  //发送间隔变量
 
uart_init(115200);//串口1初始化
uart2_init(115200);//串口2初始化
        
delay_init();                //延时函数初始化      
NVIC_Configuration();         //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
 
 
LED_GPIO_Config();
DHT11_GPIO_Config();
 
ESP8266_Init();                                 //初始化ESP8266
printf("8266_INIT_END\n");//初始化结束
        
        while(OneNet_DevLink())                //接入OneNET
       delay_ms(500);
       printf("接入onenet成功");
TIM3_Int_Init(9999,7199);
        LED_G(1);
while(1)
{            
       if(++timeCount >= 500)           //间隔5s
       {
                     if( Read_DHT11(&DHT11_Data)==SUCCESS)  //获取温湿度数据(温湿度均有整数位,小数位--湿度小数位忽略)
                     {
                            printf("hum=%d.%d\n",DHT11_Data.humi_int,DHT11_Data.humi_deci);
                            humidityH=DHT11_Data.humi_int;     //湿度整数部分
                            humidityL=DHT11_Data.humi_deci;    //湿度小数部分
                            temperatureH=DHT11_Data.temp_int;   //温度整数部分
                            temperatureL=DHT11_Data.temp_deci;   //温度小数部分
                           
                            printf("hum temp=%d .%d %d .%d\r\n",humidityH,humidityL,temperatureH,temperatureL);
                     }
                     timeCount = 0;
       }
       //ESP8266的返回格式为   "+IPD,x:yyy"   x代表数据长度,yyy是数据内容
       if(send_tim > 5)
                     {
                            send_tim =0;
                            printf( "OneNet_SendData\r\n");//通过串口1发送数据(表示下一步要发送数据了)
                            OneNet_SendData();//发送数据给onenet
                            ESP8266_Clear();
                     }
 
                     dataPtr = ESP8266_GetIPD(0);//获取平台返回的数据
                     if(dataPtr != NULL)//如果返回数据不为空
                     OneNet_RevPro(dataPtr);//平台返回数据检测
                     delay_ms(10);
}
 
}
 
//==========================================================
//    函数名称:   USART2_IRQHandler
//
//    函数功能:   串口2收发中断
//
//    入口参数:   无
//
//    返回参数:   无
//
//    说明:         
//==========================================================
void USART2_IRQHandler(void)
{
 
       if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断
       {
              if(esp8266_cnt >= sizeof(esp8266_buf))  esp8266_cnt = 0; //防止串口被刷爆
              esp8266_buf[esp8266_cnt++] = USART2->DR;
      
             
              USART_ClearFlag(USART2, USART_FLAG_RXNE);
       }
      
      
 
}
 
//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
       if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
              {
                     send_tim++;
                    
                    
                     TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志
 
              }
             
}

在这里要特别强调一下,当主函数中的代码过多时,在while里面的接受电脑端下发数据的函数可能会接收不及时,或者在接收的同时单片机向平台发送数据,然后将缓存区接收的数据删除,导致我发下发数据的时候单片机没有接收到,这个时候我们可以使用定时器,定时向平台发送数据,接收的时候可以一直接收,这样子就避免了平台下发数据时单片机向平台上传数据之后把缓存区接收到的数据也给删除了。

11.png

下发数据的格式:{“led”:1},注意一定是英文的,否则通信接收也不会点亮。

12.png

55.jpg

好了,今天的分享到这里,谢谢大家。



助工
2023-03-20 18:10:26     打赏
2楼

感谢楼主的分享,很实用了。


高工
2023-03-20 18:54:42     打赏
3楼

感谢分享


工程师
2023-03-20 19:55:48     打赏
4楼

干货


工程师
2023-03-20 19:59:27     打赏
5楼

感谢分享


工程师
2023-03-20 20:02:01     打赏
6楼

学习到了


工程师
2023-03-20 20:14:12     打赏
7楼

学习一下


工程师
2023-03-20 20:20:21     打赏
8楼

谢谢分享


工程师
2023-03-20 20:28:17     打赏
9楼

谢谢分享


专家
2023-03-21 06:20:46     打赏
10楼

感谢楼主的分享


共32条 1/4 1 2 3 4 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]