STM32物联网项目---ESP8266微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制---代码篇(四)

一、简介

该篇代码实现了ESP8266上传数据到云平台的功能(可以更改命令和温度的数据),我只测试了上传数据,是没有问题的,如果自己由别的需求可以自行在云平台创建设备和更改代码:

二、工程源码

这个代码是进行验证过的,初始化过程中遇到什么问题可以留言或者私信,需要原工程也可以分享给你。

timer.c

cs 复制代码
#include "stm32f10x.h"                  // Device header
#include "timer.h"

///**
//  * 函    数:定时中断初始化
//  * 参    数:无
//  * 返 回 值:无
//  */
//  
//volatile uint32_t TimerCount = 0;
//timer_init(1000,72);

//void timer2_init(uint16_t period, uint16_t prescaler)
//{
//	/*开启时钟*/
//	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
//	
//	/*配置时钟源*/
//	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
//	
//	/*时基单元初始化*/
//	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
//	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
//	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
//	TIM_TimeBaseInitStructure.TIM_Period = period - 1;				//计数周期,即ARR的值
//	TIM_TimeBaseInitStructure.TIM_Prescaler = prescaler - 1;				//预分频器,即PSC的值
//	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
//	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	
//	
//	/*中断输出配置*/
//	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位
//																//TIM_TimeBaseInit函数末尾,手动产生了更新事件
//																//若不清除此标志位,则开启中断后,会立刻进入一次中断
//																//如果不介意此问题,则不清除此标志位也可
//	
//	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
//	
//	/*NVIC中断分组*/
//	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
//																//即抢占优先级范围:0~3,响应优先级范围:0~3
//																//此分组配置在整个工程中仅需调用一次
//																//若有多个中断,可以把此代码放在main函数内,while循环之前
//																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
//	
//	/*NVIC配置*/
//	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
//	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
//	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
//	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;	//指定NVIC线路的抢占优先级为3
//	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;			//指定NVIC线路的响应优先级为3
//	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
//	
//	/*TIM使能*/
//	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
//}

//// 定时器中断函数,可以复制到使用它的地方
//void TIM2_IRQHandler(void)
//{
//	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
//	{
//		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
//		TimerCount++;
//		
//	}
//}

//int GetTimeCount(void){
//	return TimerCount;
//}


// 定义全局毫秒计数器

volatile uint32_t uwTick = 0;

/**
  * 函    数:SysTick定时器初始化
  * 功    能:配置SysTick定时器为1ms中断一次,用于系统毫秒级计时
  * 参    数:无
  * 返 回 值:无
  */
void SysTick_Init(void) {
    // SysTick_Config()是CMSIS标准库函数,用于配置SysTick定时器
    // 参数为重载值,计算公式:SystemCoreClock(系统核心时钟频率Hz) / 1000 = 1ms的计数次数
    // 例如:若系统时钟为72MHz,则72,000,000 / 1000 = 72000,即每计数72000次为1ms
    // 函数内部会自动完成:时钟源选择、重载值设置、中断使能、计数器启动
    SysTick_Config(SystemCoreClock / 1000);  // 配置为1ms触发一次中断
}

/**
  * 函    数:SysTick定时器中断服务函数
  * 功    能:每1ms被调用一次,更新全局毫秒计数器
  * 注    意:中断服务函数应尽量简短,避免耗时操作
  */
void SysTick_Handler(void) {
    uwTick++;  								// 每进入一次中断(1ms),计数器加1
}

/**
  * 函    数:获取当前系统运行的毫秒数
  * 功    能:提供系统时间基准,用于超时判断、延时计算等场景
  * 返 回 值:当前系统运行的总毫秒数(从SysTick_Init初始化开始计时)
  */
uint32_t GetTick(void) {
    return uwTick;  						// 返回全局计数器的值,即系统已运行的毫秒数
}

timer.h

cs 复制代码
#ifndef __TIMER_H
#define __TIMER_H

#include <stdint.h>

extern volatile uint32_t uwTick;  // 声明全局变量
extern uint32_t GetTick(void);    // 声明函数

//void timer2_init(uint16_t period, uint16_t prescaler);
//void time1_pwm_init(uint16_t period, uint16_t prescaler);

void SysTick_Init(void);
void SysTick_Handler(void);
uint32_t GetTick(void);



#endif

使用上面的定时器的话,需要稍加修改延迟函数:

delay.c

cs 复制代码
#include "delay.h"
#include "timer.h"
//技术支持:

static u8  fac_us=0;							//us延时倍乘数			   
static u16 fac_ms=0;							//ms延时倍乘数,在ucos下,代表每个节拍的ms数
	
		   
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//选择外部时钟  HCLK/8
	fac_us=SystemCoreClock/8000000;				//为系统时钟的1/8
	fac_ms=(u16)fac_us*1000;					//非OS下,代表每个ms需要的systick时钟数
}								    


//延时nus
//nus为要延时的us数.		    								   
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; 					//时间加载	  		 
	SysTick->VAL=0x00;        					//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//开始倒数	  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//关闭计数器
	SysTick->VAL =0X00;      					 //清空计数器	 
}


//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff/9000000
//Cortex时钟在对72M进行8分频即9MHz下,nms<=1864 
//void delay_ms(u16 nms)
//{	 		  	  
//	u32 temp;		   
//	SysTick->LOAD=(u32)nms*fac_ms;				//时间加载(SysTick->LOAD为24bit)
//	SysTick->VAL =0x00;							//清空计数器
//	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//开始倒数  
//	do
//	{
//		temp=SysTick->CTRL;
//	}while((temp&0x01)&&!(temp&(1<<16)));		//等待时间到达   
//	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//关闭计数器
//	SysTick->VAL =0X00;       					//清空计数器	  	    
//} 


// 保留SysTick_Init(),用于更新uwTick
// 修改delay_ms为基于uwTick的实现:
void delay_ms(uint32_t ms) {
    uint32_t start = GetTick();
    while (GetTick() - start < ms);
}

delay.h

cs 复制代码
#ifndef __DELAY_H
#define __DELAY_H 			   
#include "sys.h"  
//技术支持:

void delay_init(void);
//void delay_ms(u16 nms);
void delay_ms(uint32_t ms);
void delay_us(u32 nus);
void delay_s(u16 fac_s);
#endif

usart.c

cs 复制代码
#include "sys.h"
#include "usart.h"	  
#include "mqtt.h"
//技术支持:

//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 

}; 

//FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 

 
 
#if EN_USART1_RX   //如果使能了接收
u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA=0;       //接收状态标记	  
  
void uart_init(u32 bound){
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
  
	//USART1_RX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);
   
  //USART1_TX	  GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);


  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			
	NVIC_Init(&NVIC_InitStructure);	
  

	USART_InitStructure.USART_BaudRate = bound;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	

  USART_Init(USART1, &USART_InitStructure); 									
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);								
  USART_Cmd(USART1, ENABLE);                    									

}

void USART1_IRQHandler(void)                	//串口1中断服务程序
	{
	u8 Res;

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
	{
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			{
			if(USART_RX_STA&0x4000)//接收到了0x0d
				{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //还没收到0X0D
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
					}		 
				}
			}   		 
    } 
} 
	
//发送一个字节
void Usart_SendByte(USART_TypeDef * pUSARTx,uint8_t date)
{
	 USART_SendData(pUSARTx,date);
	 while( USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)== RESET);
}

//发送字符串
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	 while( *str!='\0' )
	 {
		 Usart_SendByte( pUSARTx, *str++); 
	 }
	 while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC)== RESET);
}

//发送一个8位的数组
void Usart_SendArray(USART_TypeDef * pUSARTx,uint8_t *arr,uint16_t num)
{ 
   while(num--)
	 {
		 Usart_SendByte( pUSARTx ,*arr++);
	 }
   while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC)== RESET);
}

//发送一个16位的数据
void Usart_SendHalfWord(USART_TypeDef * pUSARTx,uint16_t date)
{
	uint16_t tmp_h;
	uint16_t tmp_l;
	tmp_h =date>>0x08;
	tmp_l =date & 0xff;
	Usart_SendByte(pUSARTx,tmp_h);
	Usart_SendByte(pUSARTx,tmp_l);
}




void uart2_init(uint32_t baud)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);	//开启USART2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA2引脚初始化为复用推挽输出
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA3引脚初始化为上拉输入
	
	/*USART初始化*/
	USART_InitTypeDef USART_InitStructure;					//定义结构体变量
	USART_InitStructure.USART_BaudRate = baud;				//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;	//模式,发送模式和接收模式均选择
	USART_InitStructure.USART_Parity = USART_Parity_No;		//奇偶校验,不需要
	USART_InitStructure.USART_StopBits = USART_StopBits_1;	//停止位,选择1位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位
	USART_Init(USART2, &USART_InitStructure);				//将结构体变量交给USART_Init,配置USART2
	
	/*中断输出配置*/
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);			//开启串口接收数据的中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//配置NVIC为分组2
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;		//选择配置NVIC的USART2线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;		//指定NVIC线路的抢占优先级为0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;		//指定NVIC线路的响应优先级为0
	NVIC_Init(&NVIC_InitStructure);							//将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*USART使能*/
	USART_Cmd(USART2, ENABLE);								//使能USART2,串口开始运行
}

/**  
  * @简要  无线模块的串口发送一个字节
  * @参数  uint8_t byte:要发送的字节
  * @注意	无
  * @返回值 无  
  */
void Usart2_SendByte(uint8_t byte)
{
	USART_SendData(USART2, byte);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形
	while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);	//等待发送完成
	/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}

/**  
  * @简要  无线模块的串口发送字符串
  * @参数  char *str: 字符数组指针
  * @注意	这里使用了#define 同名替换了wireless_send_data
  * @返回值 无  
  */

//发送字符串
//void Usart2_SendString(USART_TypeDef * pUSARTx, char *str)
//{
//	 while( *str!='\0' )
//	 {
//		 Usart_SendByte( pUSARTx, *str++); 
//	 }
//	 while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC)== RESET);
//}
void Usart2_SendString(char *str) {
    while (*str != '\0') {
        Usart2_SendByte(*str++);  // 直接使用USART2发送
    }
    while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
}

void USART2_IRQHandler(void)
{
	uint8_t data = 0;
	if(USART_GetITStatus(USART2,USART_IT_RXNE) == SET)
	{
		data = USART_ReceiveData(USART2);
		MQTT_Receive_Callback(data);
		USART_ClearITPendingBit(USART2,USART_IT_RXNE);
	}
}


#endif	

usart.h

cs 复制代码
#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "sys.h" 
//技术支持:

#define USART_REC_LEN  			200  	//定义最大接收字节数 200
#define EN_USART1_RX 			1		//使能(1)/禁止(0)串口1接收
	  	
extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART_RX_STA;         		//接收状态标记	

void uart_init(u32 bound);
void Usart_SendByte(USART_TypeDef * pUSARTx,uint8_t date);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendArray(USART_TypeDef * pUSARTx,uint8_t *arr,uint16_t num);
void Usart_SendHalfWord(USART_TypeDef * pUSARTx,uint16_t date);
void uart2_init(uint32_t baud);
void Usart2_SendByte(uint8_t byte);
//void Usart2_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart2_SendString(char *str) ;


#endif

mqtt.c

下面注释的是我目前不需要的功能,还有一些优化前的代码,我是一步一步调试的,可以更直观的看到哪里有问题,所有串口打印函数使用的较多,可以自行选择是否屏蔽;

cs 复制代码
#include "mqtt.h"
#include "string.h"
#include "usart.h"
#include <stdlib.h>  // 提供 atoi() 的声明
#include "timer.h"


/*********************************************************************************核心配置参数**********************************************************************************/
#define ONENET_MQTT_SET_ENABLE  1          			//是否使用cJson库
#define WIFI_SSID               "ESPESP"       		//WiFi用户用
#define WIFI_PASSWORD           "123456789" 		//WiFi密码
#define ONENET_MQTT_PRODUCT_ID  "7M6S32xnSS"  		//云平台 产品ID  
#define ONENET_MQTT_DEVICE_NAME "20010712"			//云平台 设备名称
#define ONENET_MQTT_TOKEN       "version=2018-10-31&res=products%2F7M6S32xnSS%2Fdevices%2F20010712&et=2066868610&method=md5&sign=LJFN5pnrXIN6xgTT85NwHg%3D%3D"  //TOKEN


/**   
  * @简要  	连接onenet以及下发数据和上报数据的AT指令集
  * @注意	这里一般无需更改,如果需要其他AT指令参数,可以在下方对应的位置更改参数 
  */
/*********************************************************************************AT指令宏**********************************************************************************/ 
#define WIRELESS_WIFI_INFO "AT+CWJAP=\"" WIFI_SSID "\",\"" WIFI_PASSWORD "\"\r\n"																				//AT指令:连接2.4GHz wifi 
#define ONENET_MQTT_SERVER_INFO "AT+MQTTCONN=0,\"mqtts.heclouds.com\",1883,1\r\n"																				//AT指令:连接onenet的mqtt服务器
#define ONENET_MQTT_USERCFG_INFO "AT+MQTTUSERCFG=0,1,\"" ONENET_MQTT_DEVICE_NAME "\",\"" ONENET_MQTT_PRODUCT_ID "\",\"" ONENET_MQTT_TOKEN "\",0,0,\"\"\r\n" 	//AT指令:配置MQTT客户端的用户参数   
#define ONENET_MQTT_PUBTOPIC "AT+MQTTPUBRAW=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/post\""								//AT指令:设备属性上报请求(发布)
#define ONENET_MQTT_PUB_SET "AT+MQTTPUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/set_reply\"" 							//AT指令:设备属性设置响应(发布)
#define ONENET_MQTT_REPLY_TOPIC "AT+MQTTSUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/post/reply\",0\r\n"					//AT指令:设备上报响应请求(订阅)


#if ONENET_MQTT_SET_ENABLE
	#include "cJson.h"		//如果不使用属性设置,可以不用cJson文件代码
	#define ONENET_MQTT_SET_TOPIC "AT+MQTTSUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/set\",0\r\n"		//AT指令:设备属性设置请求(订阅)
	#define ONENET_MQTT_REVEIVE_SET_TOPIC   "\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/set\""                  //属性设置请求接收判断
	#define ONENET_MQTT_REVEIVE_REPLY_TOPIC "\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/post/reply\""           //属性上报请求接收判断
#endif


#define ONENET_MQTT_SUB_TOPICS "AT+MQTTSUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/post/reply\",0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/set\",0\r\n"

// 缓冲区配置
#define RX_BUFFER_SIZE 512                  //数组缓存区大小
uint8_t MQTT_RxBuffer[RX_BUFFER_SIZE] = {0};//接受数组缓存区
uint16_t MQTT_RxDataCnt = 0;                //数组下标


/**   
  * @简要  	onenet产品的属性结构体
  * @注意	这里需要按照onenet产品中创建的属性值来创建并初始化OneNET_MQTT_Data结构体内容,数据类型和名称要一致 
  */
OneNET_MQTT_Data ChuangLian_Control = {"ChuangLian_Control", TYPE_BOOL, {.bool_value = W_FALSE}};
OneNET_MQTT_Data JiaShiQi_Control   = {"JiaShiQi_Control", TYPE_BOOL, {.bool_value = W_FALSE}};
OneNET_MQTT_Data DiaoDeng_Control   = {"DiaoDeng_Control", TYPE_INT, {.int_value = 0}};
OneNET_MQTT_Data sensor_status      = {"sensor_status", TYPE_INT, {.int_value = 0}};
OneNET_MQTT_Data temperature        = {"temp", TYPE_FLOAT, {.float_value = 0.0f}};
OneNET_MQTT_Data humidity           = {"humidity", TYPE_FLOAT, {.float_value = 0.0f}};
/**   
  * @简要  	onenet产品的属性值指针数组和数组大小
  * @注意	这里只适用数据上报函数,这里填写需要上报数据的属性结构体指针
  */
//OneNET_MQTT_Data *onenet_data_array[] = {
//    &ChuangLian_Control,
//    &JiaShiQi_Control,
//	&DiaoDeng_Control,
//	&sensor_status,
//    &temperature,
//    &humidity, 
//};

OneNET_MQTT_Data *onenet_data_array[] = {
    &temperature,  // 只保留平台上已定义的属性
    // &humidity,   // 如果平台定义了湿度,取消注释
    // 移除其他未定义的属性:ChuangLian_Control, JiaShiQi_Control, DiaoDeng_Control, sensor_status
};

#define DATA_ARRAY_SIZE (sizeof(onenet_data_array) / sizeof(onenet_data_array[0]))

#define DATA_ARRAY_SIZE (sizeof(onenet_data_array) / sizeof(onenet_data_array[0]))

Wireless_TypeDef MQTT_Status = {0,0};




// 数据更新函数:每次调用时,需要自增的属性自动累加
void update_sensor_data(void) {
    // 1. 整型属性自增(如传感器状态计数,每次+1)
    sensor_status.value.int_value++;  // 每次上报加1
    
    // 2. 浮点型属性自增(如温度,每次+0.5)
    temperature.value.float_value += 0.5f;  // 每次上报加0.5
    
    // 3. 其他属性可保持固定值或按实际需求更新
    humidity.value.float_value = 60.0f;  // 固定值示例
    ChuangLian_Control.value.bool_value = 0;  // 固定值示例
    JiaShiQi_Control.value.bool_value = 0;    // 固定值示例
    DiaoDeng_Control.value.int_value = 50;    // 固定值示例
}

//获取接受数据标志位
uint8_t MQTT_Get_Receive_Flag(void)
{
    uint8_t flag = MQTT_Status.receiveDataFlag;
    MQTT_Status.receiveDataFlag = 0;
    return flag;
}

//MQTT清理数组
void MQTT_Clear_Buffer(void)
{
	memset((void *)MQTT_RxBuffer,0,RX_BUFFER_SIZE);
	MQTT_RxDataCnt = 0;
}

//ONENET平台连接标志位
//1:成功 0:失败
#if ONENET_MQTT_SET_ENABLE
uint8_t MQTT_Get_Onenet_Command_flag(void) {
	return strstr((char*)MQTT_RxBuffer, ONENET_MQTT_REVEIVE_SET_TOPIC) ? 1 : 0;
}
#endif

// AT指令发送函数(优化后核心实现)
uint8_t MQTT_Send_Command(char *cmd, char *res, uint8_t MaxSendCount, uint8_t Clear_Buffer, uint16_t timeout_ms) {
    uint32_t start_time;
    bool received;
    uint8_t temp_buffer[RX_BUFFER_SIZE] = {0};
    uint16_t temp_len = 0;

    printf("\n------------------------------------------------ [CMD] ----------------------------------------------------\n");
    printf("无线模块发送指令: %s", cmd);
    printf("期望返回字符串为: %s\n", res);
    printf("延迟时间: %dms, 最大发送次数: %d\n", timeout_ms, MaxSendCount);

    while (MaxSendCount--) {
        memset(temp_buffer, 0, RX_BUFFER_SIZE);
        temp_len = 0;
        received = false;
        Usart2_SendString(cmd);
        start_time = GetTick();

        while ((GetTick() - start_time) < timeout_ms) {
            if (MQTT_Get_Receive_Flag() == 1) {
                received = true;
                if (temp_len + MQTT_RxDataCnt < RX_BUFFER_SIZE) {
                    memcpy(temp_buffer + temp_len, MQTT_RxBuffer, MQTT_RxDataCnt);
                    temp_len += MQTT_RxDataCnt;
                }

                // 打印接收数据
                printf("[RAW RECV] Hex: ");
                for (uint16_t i = 0; i < temp_len; i++) printf("%02X ", temp_buffer[i]);
                printf("[RAW RECV] ASCII: %.*s\n", temp_len, temp_buffer);

                // 检测预期响应
                if (strstr((char*)temp_buffer, res) != NULL) {
                    printf("[OK] 收到预期响应\n");
                    if (Clear_Buffer) { MQTT_Clear_Buffer(); memset(temp_buffer, 0, RX_BUFFER_SIZE); }
                    return 1;
                }
                // 处理模块忙状态
                else if (strstr((char*)temp_buffer, "busy p...") != NULL) {
                    printf("[INFO] 模块忙,继续等待响应...\n");
                    MQTT_Clear_Buffer();
                }
                // 其他不匹配数据
                else {
                    printf("[ERROR] 未匹配到预期响应!\n");
                    if (Clear_Buffer) { MQTT_Clear_Buffer(); memset(temp_buffer, 0, RX_BUFFER_SIZE); }
                    break;
                }
            }
            delay_ms(10);
        }

        // 超时检查
        if (received && strstr((char*)temp_buffer, res) != NULL) {
            printf("[OK] 超时前收到预期响应\n");
            if (Clear_Buffer) { MQTT_Clear_Buffer(); memset(temp_buffer, 0, RX_BUFFER_SIZE); }
            return 1;
        }
        if (!received) printf("[ERROR] 超时未收到响应!\n");
        else printf("[ERROR] 超时未匹配到预期响应!\n");

        if (MaxSendCount > 0) {
            printf("[RETRY] 剩余重试次数: %d\n", MaxSendCount);
            delay_ms(500);
        }
    }

    printf("[FAIL] 所有重试均失败!\n");
    return 0;
}




uint8_t WiFi_Init(void) {
    uint8_t wifi_retry = 0;
    MQTT_Status.error_code = 0;

    if (MQTT_Send_Command("AT+RST\r\n", "", 3, 1, 1000) != 1)    MQTT_Status.error_code |= (1 << 0);
    if (MQTT_Send_Command("ATE0\r\n", "OK", 3, 1, 500) != 1)     MQTT_Status.error_code |= (1 << 1);
    if (MQTT_Send_Command("AT+CWMODE=1\r\n", "OK", 3, 1, 50) != 1) MQTT_Status.error_code |= (1 << 2);
    if (MQTT_Send_Command("AT+CWDHCP=1,1\r\n", "OK", 3, 1, 50) != 1) MQTT_Status.error_code |= (1 << 3);

    // WiFi连接重试
    while (wifi_retry++ < 5) {
        if (MQTT_Send_Command(WIRELESS_WIFI_INFO, "GOT IP", 1, 1, 10000)) break;
        delay_ms(2000);
    }
    if (wifi_retry >= 5) {
        MQTT_Status.error_code |= (1 << 4);
        return 0;
    }
    return 1;
}


// MQTT配置(精简步骤)
uint8_t MQTT_Config(void) {
    MQTT_Status.error_code = 0;

    if (MQTT_Send_Command(ONENET_MQTT_USERCFG_INFO, "OK", 3, 1, 50) != 1)
        MQTT_Status.error_code |= (1 << 5);
    if (MQTT_Send_Command(ONENET_MQTT_SERVER_INFO, "+MQTTCONNECTED", 3, 1, 10000) != 1)
        MQTT_Status.error_code |= (1 << 6);
    if (MQTT_Send_Command(ONENET_MQTT_REPLY_TOPIC, "OK", 3, 1, 500) != 1)
        MQTT_Status.error_code |= (1 << 7);
	delay_ms(3000);
#if ONENET_MQTT_SET_ENABLE
    if (MQTT_Send_Command(ONENET_MQTT_SET_TOPIC, "OK", 3, 1, 10000) != 1)
        MQTT_Status.error_code |= (1 << 8);
#endif

    return (MQTT_Status.error_code == 0) ? 1 : 0;
}


/**  
  * @简要  无线模块系统事件处理函数
  * @参数  	无
  * @注意	无
  * @返回值 无  
  */
void MQTT_System_Handler(void)
{
	if(MQTT_Status.error_code & ERROR_WiFi_CONNECT) printf("ERROR_WiFi_CONNECT:	WIFI DISCONNECT\r\n");
	if(MQTT_Status.error_code & ERROR_MQTT_CONNECT) printf("ERROR_MQTT_CONNECT:	MQTT DISCONNECT\r\n");
	if(MQTT_Status.error_code & ERROR_PUBLISH)      printf("ERROR_PUBLISH\r\n");
}

/**  
  * @简要  无线模块onenet云平台数据处理
  * @参数  	无
  * @注意	这里统一修改有关onenet平台的属性值,然后统一上报数据
  * @返回值 无  
  */
void MQTT_ONENET_Data_Handler(void)
{ 
	ChuangLian_Control.value.bool_value = 0; //ChuangLian_status;       	//赋值给onenet对应的属性值,注意属性的数据类型
	JiaShiQi_Control.value.bool_value = 0;//JiaShiQi_status;       		//赋值给onenet对应的属性值,注意属性的数据类型
	DiaoDeng_Control.value.int_value = 0;//DiaoDeng_status;       		//赋值给onenet对应的属性值,注意属性的数据类型
	temperature.value.float_value = 0;//dht11_data.temp;       			//赋值给onenet对应的属性值,注意属性的数据类型
	humidity.value.float_value = 0;//dht11_data.humi;         			//赋值给onenet对应的属性值,注意属性的数据类型
}



//无线模块初始化错误处理函数
void MQTT_Error_Handler(uint16_t error_code)
{
	printf("\r\n error \r\n\r\n");
	if(error_code & (0x01 << 0))  printf("1.AT+RST\r\n\r\n");
	if(error_code & (0x01 << 1))  printf("2.ATE0\r\n\r\n");
	if(error_code & (0x01 << 2))  printf("3.AT+CWMODE=1\r\n\r\n");
	if(error_code & (0x01 << 3))  printf("4.AT+CWDHCP=1,1\r\n\r\n");
	if(error_code & (0x01 << 4))  printf("5.%s\r\n",WIRELESS_WIFI_INFO);
	if(error_code & (0x01 << 5))  printf("6.%s\r\n",ONENET_MQTT_USERCFG_INFO);
	if(error_code & (0x01 << 6))  printf("7.%s\r\n",ONENET_MQTT_SERVER_INFO);
	if(error_code & (0x01 << 7))  printf("8.%s\r\n",ONENET_MQTT_REPLY_TOPIC);
#if ONENET_MQTT_SET_ENABLE
	if(error_code & (0x01 << 8))  printf("9.%s\r\n",ONENET_MQTT_SET_TOPIC);
	
	printf("%d\r\n",error_code);
#endif
	printf("\r\n	End		\r\n");	


}


void MQTT_Publish_Data(void) {
    char globalBuffer[512] = {0};
    char atCommand[256] = {0};
    uint16_t bufferPos = 0;

    // 1. 组装JSON消息内容
    bufferPos += snprintf(globalBuffer, sizeof(globalBuffer), "{\"id\":\"123\",\"params\":{");
    for (uint8_t i = 0; i < DATA_ARRAY_SIZE; i++) {
        switch (onenet_data_array[i]->type) {
            case TYPE_BOOL:
                bufferPos += snprintf(globalBuffer + bufferPos, sizeof(globalBuffer) - bufferPos,
                    "\"%s\":{\"value\":%s}%s", onenet_data_array[i]->name,
                    onenet_data_array[i]->value.bool_value ? "true" : "false",
                    (i < DATA_ARRAY_SIZE - 1) ? "," : "");
                break;
            case TYPE_INT:
                bufferPos += snprintf(globalBuffer + bufferPos, sizeof(globalBuffer) - bufferPos,
                    "\"%s\":{\"value\":%d}%s", onenet_data_array[i]->name,
                    onenet_data_array[i]->value.int_value,
                    (i < DATA_ARRAY_SIZE - 1) ? "," : "");
                break;
            case TYPE_FLOAT:
                bufferPos += snprintf(globalBuffer + bufferPos, sizeof(globalBuffer) - bufferPos,
                    "\"%s\":{\"value\":%.1f}%s", onenet_data_array[i]->name,
                    onenet_data_array[i]->value.float_value,
                    (i < DATA_ARRAY_SIZE - 1) ? "," : "");
                break;
        }
    }

    bufferPos += snprintf(globalBuffer + bufferPos, sizeof(globalBuffer) - bufferPos, "}}");

    // 2. 发送AT+MQTTPUBRAW指令(指定主题和消息长度)
    snprintf(atCommand, sizeof(atCommand), "%s,%d,0,0\r\n", ONENET_MQTT_PUBTOPIC, bufferPos);
    if (MQTT_Send_Command(atCommand, "OK", 1, 1, 100) != 1) {
        printf("发布指令发送失败\r\n");
        return; // 若指令失败,直接返回
    }

    // 3. 直接发送JSON消息内容(关键修改:不使用MQTT_Send_Command,避免当作指令处理)
    Usart2_SendString(globalBuffer);
    printf("已发送消息内容: %s\r\n", globalBuffer);

    // 4. (可选)等待平台响应(通过接收回调处理,无需主动发送指令等待)
}






// 接收回调(精简判断)
void MQTT_Receive_Callback(uint8_t byte) {
    if (MQTT_RxDataCnt < RX_BUFFER_SIZE) MQTT_RxBuffer[MQTT_RxDataCnt++] = byte;
    else MQTT_Clear_Buffer(); // 溢出保护

    // 检测结束标志(简化判断)
    if (MQTT_RxDataCnt >= 4 &&
        MQTT_RxBuffer[MQTT_RxDataCnt-4] == '\r' &&
        MQTT_RxBuffer[MQTT_RxDataCnt-3] == '\n' &&
        MQTT_RxBuffer[MQTT_RxDataCnt-2] == '\r' &&
        MQTT_RxBuffer[MQTT_RxDataCnt-1] == '\n') {
        MQTT_Status.receiveDataFlag = 1;
    }
}

#if ONENET_MQTT_SET_ENABLE
/*
 * 函数名:MQTT_Get_Command_Id
 * 功能:从 MQTT 接收的 JSON 数据中,提取「命令ID」(用于后续命令响应匹配)
 * 参数:char *jsonData - 接收的 JSON 格式数据字符串
 * 返回值:uint32_t - 提取到的命令ID(失败返回 0)
 * 说明:依赖 cJSON 库解析 JSON 数据,是物联网平台常见的「命令标识」提取逻辑
 */
uint32_t MQTT_Get_Command_Id(char *jsonData) {

    cJSON *json_obj = cJSON_Parse(jsonData);					    // 1. 解析 JSON 字符串:将字符串形式的 JSON 转为 cJSON 结构体(便于提取字段)
																	// cJSON_Parse:cJSON 库核心函数,失败返回 NULL
    if (!json_obj)  												// 若解析失败(如 JSON 格式错误),直接返回 0
        return 0;

    cJSON *Id_Item = cJSON_GetObjectItem(json_obj, "id");    		// 2. 从 JSON 结构体中提取 "id" 字段(命令ID,平台下发命令时的唯一标识)  

    uint32_t id = Id_Item ? atoi(Id_Item->valuestring) : 0;    		// 3. 提取 ID 值:若 "id" 字段存在,将其字符串值转为整数;否则返回 0
																	// atoi:将字符串转为整数(如 "123" → 123)
    cJSON_Delete(json_obj);    										// 4. 释放 cJSON 结构体内存(必须调用,避免内存泄漏)
    
    return id;  													// 返回提取到的命令ID
}

/*
 * 函数名:MQTT_Receiver_Command_Extract
 * 功能:从 JSON 数据中提取「设备控制参数」,并更新到对应的设备控制变量
 * 参数:char *jsonData - 接收的 JSON 格式数据字符串
 * 说明:针对具体设备(窗帘、加湿器、吊灯)的控制逻辑,提取不同类型的参数(布尔值/整数)
 */
void MQTT_Receiver_Command_Extract(char *jsonData) {

    cJSON *json_obj = cJSON_Parse(jsonData);			    	// 1. 解析 JSON 字符串:失败则直接返回
    if (!json_obj) 
        return;


    cJSON *params = cJSON_GetObjectItem(json_obj, "params");    // 2. 提取 JSON 中的 "params" 字段(平台下发的控制参数都在该字段下)
    if (!params) { 												 // 若 "params" 字段不存在,释放内存后返回
        cJSON_Delete(json_obj); 
        return; 
    }

    // 3. 提取「窗帘控制参数」:匹配窗帘控制变量的名称(ChuangLian_Control.name)
    // 条件1:item 不为空(字段存在);条件2:字段类型是布尔值(cJSON_IsBool)
    // 若满足,将 JSON 布尔值(valueint:true=1,false=0)赋值给窗帘的布尔控制值
    cJSON *item = cJSON_GetObjectItem(params, ChuangLian_Control.name);
    if (item && cJSON_IsBool(item)) 
        ChuangLian_Control.value.bool_value = item->valueint;

    // 4. 提取「加湿器控制参数」:逻辑同上,处理加湿器的布尔控制值(如开/关)
    item = cJSON_GetObjectItem(params, JiaShiQi_Control.name);
    if (item && cJSON_IsBool(item)) 
        JiaShiQi_Control.value.bool_value = item->valueint;

    // 5. 提取「吊灯控制参数」:逻辑类似,但处理整数类型(如亮度值:0-100)
    // cJSON_IsNumber:判断字段是否为数字类型
    // valueint:cJSON 数字字段的整数值(若为浮点数需用 valuedouble)
    item = cJSON_GetObjectItem(params, DiaoDeng_Control.name);
    if (item && cJSON_IsNumber(item)) 
        DiaoDeng_Control.value.int_value = item->valueint;

    // 6. 释放 cJSON 内存,避免泄漏
    cJSON_Delete(json_obj);
}

/*
 * 函数名:MQTT_Receive_Command_Respond
 * 功能:向 MQTT 平台发送「命令处理响应」(告知平台命令已接收并处理)
 * 参数:
 *   uint32_t id - 命令ID(与平台下发的ID一致,用于匹配)
 *   uint16_t code - 响应码(200=成功,其他=失败,遵循平台协议)
 *   char *msg - 响应消息(如 "success" 表示成功,便于调试)
 * 说明:按 OneNET MQTT 平台要求的格式拼接响应 JSON,再调用发送函数
 */
void MQTT_Receive_Command_Respond(uint32_t id, uint16_t code, char *msg) {
    char jsonData[256] = {0};  // 响应 JSON 的缓冲区(大小 256,足够存储简短响应)

    // 1. 格式化拼接响应 JSON:按 OneNET 平台要求的格式组装
    // 格式说明:
    // ONENET_MQTT_PUB_SET:平台规定的「属性设置响应」Topic 前缀(宏定义)
    // 中间部分:{"id":"%d","code":%d,"msg":"%s"} → 响应核心内容(ID、响应码、消息)
    // 末尾 "\r\n":平台要求的消息结束符
    // 注意:内层双引号需用 \ 转义(如 \"id\" → 最终输出 "id")
    snprintf(jsonData, sizeof(jsonData), 
             "%s,\"{\\\"id\\\":\\\"%d\\\",\\\"code\\\":%d,\\\"msg\\\":\\\"%s\\\"}\",0,0\r\n",
             ONENET_MQTT_PUB_SET,  // 响应 Topic 前缀
             id,                   // 命令ID(与下发一致)
             code,                 // 响应码(200=成功)
             msg                   // 响应消息
            );

    // 2. 调用 MQTT 发送函数,将响应发送给平台
    // 参数说明:发送数据、发送标识("ok")、QoS等级(1)、是否保留(1)、超时时间(50ms)
    MQTT_Send_Command(jsonData, "ok", 1, 1, 50);
}

/*
 * 函数名:MQTT_Processing_Command
 * 功能:MQTT 命令处理主函数(协调提取 JSON、解析命令、发送响应)
 * 说明:从接收缓冲区中定位有效 JSON 数据,依次完成「提ID→解参数→发响应」流程
 */
void MQTT_Processing_Command(void) {
    char *jsonData = NULL;  // 指向有效 JSON 数据的指针

    // 1. 定位 JSON 数据的起始位置({):
    // 优先逻辑:从接收缓冲区中找「OneNET 平台属性设置 Topic」(ONENET_MQTT_REVEIVE_SET_TOPIC)
    // 找到 Topic 后,从 Topic 后面的「{」开始,才是真正的 JSON 数据
    jsonData = strstr((char*)MQTT_RxBuffer, ONENET_MQTT_REVEIVE_SET_TOPIC);
    if (jsonData)  // 若找到 Topic,移动指针到 JSON 起始符「{」
        jsonData = strchr(jsonData, '{');
    else  // 若未找到 Topic(兼容异常情况),直接从缓冲区中找第一个「{」
        jsonData = strchr((char*)MQTT_RxBuffer, '{');

    if (!jsonData)  // 若未找到「{」(无有效 JSON),直接返回
        return;

    // 2. 步骤1:从 JSON 中提取命令ID(用于响应匹配)
    uint32_t cmd_id = MQTT_Get_Command_Id(jsonData);

    // 3. 步骤2:从 JSON 中提取控制参数,更新设备控制变量
    MQTT_Receiver_Command_Extract(jsonData);

    // 4. 步骤3:向平台发送响应(告知处理成功,响应码 200,消息 "success")
    MQTT_Receive_Command_Respond(cmd_id, 200, "success");
}

void MQTT_Command_Control(void) {
    // 执行设备控制逻辑(仅保留核心打印,实际应用需替换为硬件控制)
    if (ChuangLian_Control.value.bool_value) printf("\r\n打开窗帘\r\n");
    else printf("\r\n关闭窗帘\r\n");

    if (JiaShiQi_Control.value.bool_value) printf("\r\n打开加湿器\r\n");
    else printf("\r\n关闭加湿器\r\n");

    if (DiaoDeng_Control.value.int_value >= 0 && DiaoDeng_Control.value.int_value <= 100)
        printf("\r\n吊灯亮度: %d%%\r\n", DiaoDeng_Control.value.int_value);
}
#endif

// 接收数据处理(精简状态判断)
void MQTT_Receive_data_Handler(void) {
    // WiFi状态判断
    if (strstr((char*)MQTT_RxBuffer, "WIFI DISCONNECT")) {
        printf("WiFi连接断开\r\n");
    } else if (strstr((char*)MQTT_RxBuffer, "WIFI GOT IP")) {
        printf("WiFi已获取IP\r\n");
    }

    // MQTT状态判断
    if (strstr((char*)MQTT_RxBuffer, "+MQTTDISCONNECTED:0")) {
        printf("MQTT连接断开\r\n");
    } else if (strstr((char*)MQTT_RxBuffer, "+MQTTCONNECTED:0,1")) {
        printf("MQTT已连接服务器\r\n");
    }

#if ONENET_MQTT_SET_ENABLE
    // 处理属性设置命令
    if (MQTT_Get_Onenet_Command_flag() == 1) {
        MQTT_Processing_Command();
        MQTT_Command_Control();
        MQTT_Publish_Data(); // 执行后上报状态
    }
#endif

    MQTT_Clear_Buffer(); // 处理完成后清空缓冲区
}

// MQTT初始化入口(精简流程)
void MQTT_INIT(void) {
    printf("Start MQTT service\r\n");

    // 连接WiFi
    if (!WiFi_Init()) {
        printf("WiFi初始化失败\r\n");
        return;
    }
    printf("WiFi连接成功!\r\n");

    // 初始化MQTT
    if (!MQTT_Config()) {
        printf("MQTT初始化失败\r\n");
        return;
    }
    printf("MQTT连接成功!\r\n");

    printf("ESP8266初始化完成\r\n");
}

// 删除冗余调试函数(Test_WiFi_Only、WiFi_Connection_Debug等非核心功能)

//无线模块串口发送指令
/*cmd :发送的字符串
  res :期望返回字符串结果
  MaxSendCount:最大发送次数
  Clear_BUffer:是否清除数组 1:清除
  time: 延迟时间ms*/
//uint8_t MQTT_Send_Command(char *cmd,char *res,uint8_t MaxSendCount,uint8_t Clear_Buffer,uint16_t time)
//{
//  printf("无线模块串口发送指令:%s\r\n",cmd);
//  while(MaxSendCount--){
//		Usart2_SendString(cmd);
//		delay_ms(time);
//		if(MQTT_Get_Receive_Flag() == 1){
//#if ONENET_MQTT_SET_ENABLE
//      if(MQTT_Get_Onenet_Command_flag() == 1) {MQTT_Status.error_code = 1;return 0;}
//#endif
//	  if(strstr((const char *)MQTT_RxBuffer,res) != NULL)
//      {  
//        if(Clear_Buffer == 1) MQTT_Clear_Buffer();
//        return 1;
//      }
//      printf("error recovery data:%s",MQTT_RxBuffer);
//      if(Clear_Buffer == 1) MQTT_Clear_Buffer();
//    }
//    delay_ms(500);
//  }
//  return 0;
//}

//uint8_t MQTT_Send_Command(char *cmd, char *res, uint8_t MaxSendCount, uint8_t Clear_Buffer, uint16_t timeout_ms) 
//{
//    uint32_t start_time = 0;
//    bool received = false;
//    // 新增:用于累积多段响应的缓冲区
//    uint8_t temp_buffer[RX_BUFFER_SIZE] = {0};
//    uint16_t temp_len = 0;

//    printf("\n------------------------------------------------ [CMD] ----------------------------------------------------\n");
//    printf("无线模块发送指令: %s", cmd);
//    printf("期望返回字符串为: %s\n", res);
//    printf("延迟时间: %dms, 最大发送次数: %d\n", timeout_ms, MaxSendCount);

//    while (MaxSendCount--) {
//        // 每次重试前清空累积缓冲区
//        memset(temp_buffer, 0, RX_BUFFER_SIZE);
//        temp_len = 0;
//        received = false;

//        Usart2_SendString(cmd);
//        start_time = GetTick();

//        while ((GetTick() - start_time) < timeout_ms) {
//            if (MQTT_Get_Receive_Flag() == 1) {
//                received = true;
//                // 将新接收的数据追加到累积缓冲区
//                if (temp_len + MQTT_RxDataCnt < RX_BUFFER_SIZE) {
//                    memcpy(temp_buffer + temp_len, MQTT_RxBuffer, MQTT_RxDataCnt);
//                    temp_len += MQTT_RxDataCnt;
//                }

//                // 打印累积的响应数据
//                printf("[RAW RECV] Hex: ");
//                for (uint16_t i = 0; i < temp_len; i++) {
//                    printf("%02X ", temp_buffer[i]);
//                }
//                printf("[RAW RECV] ASCII: %.*s\n", temp_len, temp_buffer);

//                // 检测累积缓冲区中是否包含预期响应
//                if (strstr((const char *)temp_buffer, res) != NULL) {
//                    printf("[OK] 收到预期响应\n");
//                    if (Clear_Buffer) {
//                        MQTT_Clear_Buffer();
//                        memset(temp_buffer, 0, RX_BUFFER_SIZE);
//                    }
//                    return 1;
//                }
//                // 检测到busy p...,继续等待后续数据
//                else if (strstr((const char *)temp_buffer, "busy p...") != NULL) {
//                    printf("[INFO] 模块忙,继续等待响应...\n");
//                    MQTT_Clear_Buffer(); // 仅清空原始缓冲区,保留累积缓冲区
//                }
//                // 其他不匹配数据,终止等待
//                else {
//                    printf("[ERROR] 未匹配到预期响应!\n");
//                    printf("Received: %.*s\n", temp_len, temp_buffer);
//                    if (Clear_Buffer) {
//                        MQTT_Clear_Buffer();
//                        memset(temp_buffer, 0, RX_BUFFER_SIZE);
//                    }
//                    break;
//                }
//            }
//            delay_ms(10);
//        }

//        // 超时后检查累积缓冲区(关键:防止最后一段数据未被检测)
//        if (received && strstr((const char *)temp_buffer, res) != NULL) {
//            printf("[OK] 超时前收到预期响应\n");
//            if (Clear_Buffer) {
//                MQTT_Clear_Buffer();
//                memset(temp_buffer, 0, RX_BUFFER_SIZE);
//            }
//            return 1;
//        }

//        // 超时未收到数据或未匹配
//        if (!received) {
//            printf("[ERROR] 超时未收到响应!\n");
//        } else {
//            printf("[ERROR] 超时未匹配到预期响应!\n");
//        }

//        if (MaxSendCount > 0) {
//            printf("[RETRY] 剩余重试次数: %d\n", MaxSendCount);
//            delay_ms(500);
//        }
//    }

//    printf("[FAIL] 所有重试均失败!\n");
//    return 0;
//}


//void MQTT_INIT(void) {
//    printf("Start MQTT service\r\n");

////    // 第一阶段:WiFi基础配置
//    if(!WiFi_Init()) {
////        MQTT_Error_Handler(MQTT_Status.error_code);
//		printf("WIFI连接失败\r\n");
//        return;
//    }else{
//		printf("WIFI连接成功!!!\r\n");
//	
//	}

////    // 第二阶段:MQTT配置
//    if(!MQTT_Config()) {
////        MQTT_Error_Handler(MQTT_Status.error_code);
//		printf("MQTT连接失败\r\n");
//        return;
//    }else{
//		printf("MQTT连接成功!!!\r\n");
//	}

//    printf("ESP8266初始化成功\r\n");
//}

////uint8_t WiFi_Init(void) {
////    // 实现WiFi连接的AT指令序列
////    // 返回1成功,0失败
////	uint8_t wifi_retry = 0;
//////	uart_init(115200);    
////	if(MQTT_Send_Command("AT+RST\r\n","",3,1,1000) != 1) 						MQTT_Status.error_code |= (1 << 0);     //复位模块  (长延迟)
////	
////	if(MQTT_Send_Command("ATE0\r\n","OK",3,1,500) != 1)  						MQTT_Status.error_code |= (1 << 1);     //关闭回显  (长延迟)

////	if(MQTT_Send_Command("AT+CWMODE=1\r\n","OK",3,1,50) != 1)  					MQTT_Status.error_code |= (1 << 2); 	//设置模式一(配置他作为WiFi客户端)

////	if(MQTT_Send_Command("AT+CWDHCP=1,1\r\n", "OK", 3, 1, 50) != 1)				MQTT_Status.error_code |= (1 << 3);		//开启DHCP,在连接到Wi-Fi网络时能够自动获得IP地址
////                                                                                                            
////	while(wifi_retry++ < 5) {  // 增加重试次数
////		if(MQTT_Send_Command(WIRELESS_WIFI_INFO, "GOT IP", 1, 1, 10000)) {  // 延长超时到10秒
////			break;
////		}
////		delay_ms(2000);  // 等待2秒后重试
////	}
////	if(wifi_retry >= 5) {
////		MQTT_Status.error_code |= (1 << 4) | ERROR_WiFi_CONNECT;
////		return 0;  // 直接退出初始化
////	}
////	return 1;
////}

////uint8_t MQTT_Config(void) {
////    // 实现MQTT配置的AT指令序列
////    // 返回1成功,0失败
////	if(MQTT_Send_Command(ONENET_MQTT_USERCFG_INFO, "OK", 3, 1, 50) != 1)		MQTT_Status.error_code |= (1 << 5);			//用于配置MQTT客户端的用户参数
////                                                                                                         
//////	if(MQTT_Send_Command(ONENET_MQTT_SERVER_INFO, "OK", 3, 1, 500) != 1) 		MQTT_Status.error_code |= (1 << 6) | ERROR_MQTT_CONNECT;			//连接onenet的MQTT服务器 
////	// 修改服务器连接检查
////	if(MQTT_Send_Command(ONENET_MQTT_SERVER_INFO, "+MQTTCONNECTED", 3, 1, 10000) != 1) {
////		MQTT_Status.error_code |= (1 << 6) | ERROR_MQTT_CONNECT;
////		printf("MQTT服务器连接失败\r\n");
////		return 0;
////	}else{
////		MQTT_Status.error_code = 0;
////	}
////                                                                                                        
////	if(MQTT_Send_Command(ONENET_MQTT_REPLY_TOPIC, "OK", 3, 1, 500) != 1){
//// 		MQTT_Status.error_code |= (1 << 7);	        //订阅设备属性上报响应主题	
////		printf("订阅1失败,接收数据:%s\n", MQTT_RxBuffer);
////		delay_ms(500);
////	}
////#if ONENET_MQTT_SET_ENABLE	
////                                                                                                       
////	if(MQTT_Send_Command(ONENET_MQTT_SET_TOPIC, "OK", 3, 1, 5000) != 1){ 
////		MQTT_Status.error_code |= (1 << 8);		    //订阅设备属性设置请求
////		printf("订阅2失败,接收数据:%s\n", MQTT_RxBuffer);
////	}
////#endif	

////		
////	if(MQTT_Status.error_code == 0) 	//如果所有AT指令都没有错误
////	{
////		printf("\r\n	!!!!!	MQTT SEND SUCCEED	!!!!!	\r\n\r\n");
////		return 1;
////	}
////	else 		//如果有错误
////	{
////		printf("\r\n	*****	MQTT SEND FAILL	******	\r\n\r\n");
////		MQTT_Error_Handler(MQTT_Status.error_code);  //错误处理函数
////		return 0;
////	}	
////}



///**   
//  * @简要  数据上报至onenet云平台
//  * @参数  	无
//  * @注意	根据OneNET_MQTT_Data创建的属性结构体,并且使用指针数组封装,然后统一处理后上报到onenet云平台
//			这里会发送两个AT指令数据,第一个是mqtt长指令上报AT指令,发送后进入长数据模式
//			第二个是发送json格式的数据上报至onenet云平台
//			需要注意第二个AT指令组合后的长度是否超出数组缓存大小
//  * @示例:	第一个AT指令:AT+MQTTPUBRAW=0,"$sys/HbJo2787Fn/device-001/thing/property/post",182,0,0
//			第二个AT指令:{"id":"123","params":{"ChuangLian_Control":{"value":false},"JiaShiQi_Control":{"value":false},"sensor_status":{"value":0},"temperature":{"value":13.5},"humidity":{"value":39.0}}}
//  * @返回值 无  
//  */
//void MQTT_Publish_Data(void) 
//{
//	#define BUFFER_SIZE 512
//	#define AT_COMMAND_SIZE 256
//  uint16_t bufferPos = 0;
//	uint16_t atCommandPos = 0;
//  char globalBuffer[BUFFER_SIZE];
//  char atCommand[AT_COMMAND_SIZE];
//	static uint8_t error_send_count = 0; 
//	
//    bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos, "{\"id\":\"123\",\"params\":{"); // 手动拼接JSON数据
//    // 遍历指针数组,根据类型拼接数据
//    for (unsigned char i = 0; i < DATA_ARRAY_SIZE; ++i) {
//        switch (onenet_data_array[i]->type) {
//            case TYPE_BOOL:
//                bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos,
//                                      "\"%s\":{\"value\":%s}%s",
//                                      onenet_data_array[i]->name, onenet_data_array[i]->value.bool_value ? "true" : "false",
//                                      (i < DATA_ARRAY_SIZE - 1) ? "," : "");
//                break;
//            case TYPE_INT:
//                bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos,
//                                      "\"%s\":{\"value\":%d}%s",
//                                      onenet_data_array[i]->name, onenet_data_array[i]->value.int_value,
//                                      (i < DATA_ARRAY_SIZE - 1) ? "," : "");
//                break;
//            case TYPE_FLOAT:
//                bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos,
//                                      "\"%s\":{\"value\":%.1f}%s",
//                                      onenet_data_array[i]->name, onenet_data_array[i]->value.float_value,
//                                      (i < DATA_ARRAY_SIZE - 1) ? "," : "");
//                break;
//            case TYPE_STRING:
//                bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos,
//                                      "\"%s\":{\"value\":\"%s\"}%s",
//                                      onenet_data_array[i]->name, onenet_data_array[i]->value.string_value,
//                                      (i < DATA_ARRAY_SIZE - 1) ? "," : "");
//                break;
//        }
//    }

//  // 拼接JSON结尾
//  bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos, "}}\r\n\r\n");

//  // 如果上报数据的字符数组大于缓存,打印报错
//  if (bufferPos >= BUFFER_SIZE) {
//    // 处理错误,例如通过日志记录
//    printf("\r\nERROR:publish_data buffer overflow\r\n");
//    return;
//  }

//  // 拼接 AT 命令
//  atCommandPos += snprintf(atCommand + atCommandPos, AT_COMMAND_SIZE - atCommandPos,
//                            "%s,%d,0,0\r\n", ONENET_MQTT_PUBTOPIC, (int)bufferPos);

//  // 检查 AT 命令是否溢出
//  if (atCommandPos >= AT_COMMAND_SIZE) {
//    printf("\r\nERROR:atCommand buffer overflow\r\n");
//    return;
//  }
//	MQTT_Send_Command(atCommand, "OK", 1, 1, 25);    // 发送 AT 命令进入上报长数据模式
//    // 发送数据
//  if (MQTT_Send_Command(globalBuffer, "{\"id\":\"123\",\"code\":200,\"msg\":\"success\"}", 1, 1, 300) == 0) error_send_count++;
//  else error_send_count = 0, MQTT_Status.error_code &= ~ERROR_PUBLISH;
//   
//  if (error_send_count >= 5) MQTT_Status.error_code |= ERROR_PUBLISH; // 如果连续失败次数达到 5 次,设置错误代码
//}



////无线模块接受函数
//void MQTT_Receive_Callback(uint8_t byte) {
//    MQTT_RxBuffer[MQTT_RxDataCnt++] = byte;
//    // 确保缓冲区不溢出
//    if (MQTT_RxDataCnt >= RX_BUFFER_SIZE) {
//        MQTT_Clear_Buffer();
//    }
//    // 严格判断两个连续的\r\n作为结束标志
//    if (MQTT_RxDataCnt >= 4) {
//        if (MQTT_RxBuffer[MQTT_RxDataCnt-4] == '\r' && 
//            MQTT_RxBuffer[MQTT_RxDataCnt-3] == '\n' && 
//            MQTT_RxBuffer[MQTT_RxDataCnt-2] == '\r' && 
//            MQTT_RxBuffer[MQTT_RxDataCnt-1] == '\n') {
//            MQTT_Status.receiveDataFlag = 1;
//        }
//    }
//}




////判断WiFi连接情况
//uint8_t MQTT_Wifi_Connect_Status(void)
//{
//  if(strstr((char *)MQTT_RxBuffer,"WIFI DISCONNECT") != NULL) return 0;
//  if(strstr((char *)MQTT_RxBuffer,"WIFI GOT IP") != NULL) return 1;
//  return 0xFF;
//}

///**   
//  * @简要  判断MQTT服务器连接情况
//  * @参数  	无
//  * @注意	连接或者断开mqtt服务器时,无线模块的串口会收到下面数据
//  * @数据示例  	1.mqtt恢复连接:+MQTTCONNECTED:0,1,"mqtts.heclouds.com","1883","",1
//				2.mqtt断开连接:+MQTTDISCONNECTED:0
//  * @返回值 mqtt服务器连接情况 
//  */
//uint8_t MQTT_Connect_Status(void)
//{
//	if(strstr((char *)MQTT_RxBuffer, "+MQTTDISCONNECTED:0") != NULL)	return 0;
//	if(strstr((char *)MQTT_RxBuffer, "+MQTTCONNECTED:0,1,\"mqtts.heclouds.com\",\"1883\",\"\",1") != NULL)	return 1;
//	return 0xFF;	
//}

///**   
//  * @简要  属性设置接收信息-提取id号
//  * @参数  	char *jsonData:json数据
//  * @注意	传入json格式的数据,需要将原始接收的数据分割'{'前面的内容
//  * @参数示例:{"id":"4","version":"1.0","params":{"ChuangLian_Control":true}}		
//  * @返回值 当前属性设置信息的id号  
//  */
//uint32_t MQTT_Get_Command_Id(char *jsonData)
//{
//  uint32_t Cmd_Id = 0;		//定义一个id整形变量

//  cJSON *json_obj = cJSON_Parse(jsonData);  			// 判断JSON字符串是否合法
//  if (json_obj == NULL) 			
//	{
//    const char *error_ptr = cJSON_GetErrorPtr();	
//    if (error_ptr != NULL) 
//		{
//      printf("Error before: %s\n", error_ptr);
//    }
//        return 0xFFFFFFFF;
//  }
//  cJSON *Id_Item = cJSON_GetObjectItem(json_obj, "id");  // 提取id字段
//  if (cJSON_IsString(Id_Item) && (Id_Item->valuestring != NULL)) 
//	{
//		Cmd_Id = atoi(Id_Item->valuestring);		//将id字符串转为整形
//  }  
//  cJSON_Delete(json_obj);  // 清理JSON对象

//  return Cmd_Id;

//}

///**   
//  * @简要  根据属性设置信息-提取命令内容
//  * @参数  	char *jsonData:json数据
//  * @注意	需要根据对应的数据类型执行对应的解析函数,cJSON_IsBool(cmd_item)或者cJSON_IsNumbmer(cmd_item)
//  * @示例  {"id":"4","version":"1.0","params":{"ChuangLian_Control":true}}
//  * @返回值 无  
//  */
//void MQTT_Receiver_Command_Extract(char *jsonData)
//{
//  if(jsonData == NULL) printf("jsonData 为空\r\n");
//	// 检测字符串是否符合json格式
//	cJSON *json_obj = cJSON_Parse(jsonData);
//  if (json_obj == NULL) {
//    const char *error_ptr = cJSON_GetErrorPtr();
//    if (error_ptr != NULL) {
//        printf("Error before: %s\r\n", error_ptr);
//    }
//    return ;
//  }
//	
//	// 提取params字段
//  cJSON *params_item = cJSON_GetObjectItem(json_obj, "params");
//  if (params_item == NULL) {
//    printf("Params not found\r\n");
//    cJSON_Delete(json_obj);
//    return;
//  }
//	
//	cJSON *Cmd_Item = NULL;
//	
//	Cmd_Item  = cJSON_GetObjectItem(params_item, ChuangLian_Control.name);
//	if (Cmd_Item != NULL && cJSON_IsBool(Cmd_Item)) 
//	{
//		ChuangLian_Control.value.bool_value = (uint8_t)Cmd_Item->valueint;
//		printf("ChuangLian_Control.value:%d\r\n",ChuangLian_Control.value.bool_value);
//	}

//	Cmd_Item  = cJSON_GetObjectItem(params_item, JiaShiQi_Control.name);
//	if (Cmd_Item != NULL && cJSON_IsBool(Cmd_Item)) 
//	{
//		JiaShiQi_Control.value.bool_value = (uint8_t)Cmd_Item->valueint;
//		printf("JiaShiQi_Control.value:%u\r\n",JiaShiQi_Control.value.bool_value);
//	}

//	Cmd_Item  = cJSON_GetObjectItem(params_item, DiaoDeng_Control.name);
//	if (Cmd_Item != NULL && cJSON_IsNumber(Cmd_Item)) 
//	{
//		DiaoDeng_Control.value.int_value = (uint8_t)Cmd_Item->valueint;
//		printf("DiaoDeng_Control.value:%u\r\n",DiaoDeng_Control.value.int_value);
//	}
//	
//	cJSON_Delete(json_obj);   //释放内存
//}

///**   
//  * @简要  接收到属性设置数据后回应
//  * @参数  	uint32_t respond_id:回应的id
//			uint16_t code:回应的代码,默认:200
//			char *msg:回应的字符,默认:success 
//  */
//void MQTT_Receive_Command_Respond(uint32_t respond_id, uint16_t code, char *msg)
//{
//	char jsonData[256] = {0};
//	snprintf(jsonData, sizeof(jsonData),  
//					 "%s,\"{\\\"id\\\":\\\"%d\\\"\\,\\\"code\\\":%d\\,\\\"msg\\\":\\\"%s\\\"}\",0,0\r\n",  
//					 ONENET_MQTT_PUB_SET, respond_id, code, msg);
//  MQTT_Send_Command(jsonData,"ok",1,1,50);
//}

///**   
//  * @简要  无线模块处理接收到的属性设置数据
//  * @参数  	无
//  * @注意	将数据转为json格式,然后提取id号和指令,最后响应属性设置
//			主要用于APP或者微信小程序中调用API执行HTTP操作时,需要返回执行状态的场景
//  * @示例:	原始数据: +MQTTSUBRECV:0,"$sys/HbJo2787Fn/device-001/thing/property/set",63,{"id":"4","version":"1.0","params":{"ChuangLian_Control":true}}
//			转为json格式:{"id":"4","version":"1.0","params":{"ChuangLian_Control":true}}
//			提取id号转为整形:4
//			提取参数params: "ChuangLian_Control":true
//  * @返回值 无  
//  */
//void MQTT_Processing_Command(void)
//{
//	char *jsonData = NULL;
//	uint32_t cmd_id;
//	jsonData = strstr((const char*)MQTT_RxBuffer, ONENET_MQTT_REVEIVE_SET_TOPIC);   //先截取出属性设置部分的字符串
//	if(jsonData == NULL) strchr((const char*)MQTT_RxBuffer, '{');	                  //提取json数据
//	else jsonData = strchr((const char*)jsonData, '{');	                           //提取json数据
//	printf("jsonData:\r\n%s\r\n",jsonData);			                                    //显示接收到的json数据
//	cmd_id = MQTT_Get_Command_Id(jsonData);					                                //获得命令的id号
//	MQTT_Receiver_Command_Extract(jsonData);				                          //提取指令		
//	MQTT_Receive_Command_Respond(cmd_id, 200, "success");	                    //回应onenet	
//}

///**  
//  * @简要  无线模块命令控制
//  * @参数  	无
//  * @注意	这里会在接收到属性设置信息后执行函数,根据提取后的指令,执行不同的操作,最后需要修改对应的数据值
//  * @返回值 无  
//  */
//void MQTT_Command_Control(void)
//{
//	if(ChuangLian_Control.value.bool_value == W_TRUE) 			printf("\r\n打开窗帘\r\n");//ChuangLian_Control_ON();
//	else if(ChuangLian_Control.value.bool_value == W_FALSE) 	printf("\r\n关闭窗帘\r\n");//ChuangLian_Control_OFF();
//	
//	if(JiaShiQi_Control.value.bool_value == W_TRUE) 			printf("\r\n打开加湿器\r\n");//JiaShiQi_Control_ON();
//	else if(JiaShiQi_Control.value.bool_value == W_FALSE) 		printf("\r\n关闭加湿器\r\n");//JiaShiQi_Control_OFF();
//	
//	if(DiaoDeng_Control.value.int_value >= 0 && DiaoDeng_Control.value.int_value <= 100) 	printf("\r\n打开吊灯\r\n");//DiaoDeng_Control_PWM(DiaoDeng_Control.value.int_value);
//}



////无线模块接受处理函数
//void MQTT_Receive_data_Handler(void)
//{
//  if(MQTT_Wifi_Connect_Status() == 0) MQTT_Status.error_code |= ERROR_WiFi_CONNECT,printf("WiFi连接错误\r\n");
//  else if(MQTT_Wifi_Connect_Status() == 1) MQTT_Status.error_code &= ~ERROR_WiFi_CONNECT,printf("WIFI连接成功\r\n");     
//	
//	if(MQTT_Connect_Status() == 0) MQTT_Status.error_code |= ERROR_MQTT_CONNECT,printf("MQTT连接失败\r\n");    
//	else if(MQTT_Connect_Status() == 1) MQTT_Status.error_code &= ~ERROR_MQTT_CONNECT,printf("+MQTTCONNECTED:0,1,\"mqtts.heclouds.com\",\"1883\",\"\",1\r\n");  
//#if ONENET_MQTT_SET_ENABLE
//	if(MQTT_Get_Onenet_Command_flag() == 1)	//判断是命令数据
//	{
//		MQTT_Processing_Command();			      //处理数据
//		MQTT_Command_Control();				        //执行命令
//		MQTT_Clear_Buffer();					//清除无线模块接收数据缓存	
//		MQTT_Publish_Data();                  //执行完成后发送数据
//	}
//#endif
//	MQTT_Clear_Buffer();		      //清除无线模块接收数据缓存	



//}

////// 先单独测试WiFi连接
//void Test_WiFi_Only() {
//    MQTT_Send_Command("AT+RST\r\n","",3,1,500);
//    MQTT_Send_Command("ATE0\r\n","OK",3,1,50);
//    MQTT_Send_Command("AT+CWMODE=1\r\n","OK",3,1,50);
//    MQTT_Send_Command(WIRELESS_WIFI_INFO, "GOT IP", 5, 1, 5000);
//}


//void WiFi_Connection_Debug(void) {
//    printf("\n===== WiFi调试模式 =====\n");
//    
//    // 1. 测试AT基础指令
//    MQTT_Send_Command("AT\r\n", "OK", 3, 1, 1000);
//    
//    // 2. 检查WiFi模式
//    MQTT_Send_Command("AT+CWMODE?\r\n", "+CWMODE:1", 3, 1, 500);
//    
//    // 3. 扫描周围WiFi(验证模块射频功能)
//    MQTT_Send_Command("AT+CWLAP\r\n", "ESP", 5, 1, 5000);  // 扫描是否能看到目标AP
//    
//    // 4. 单独连接测试(延长超时到10秒)
//    char cmd[128];
//    snprintf(cmd, sizeof(cmd), "AT+CWJAP=\"%s\",\"%s\"\r\n", WIFI_SSID, WIFI_PASSWORD);
//    MQTT_Send_Command(cmd, "GOT IP", 5, 1, 10000);
//    
//    // 5. 获取连接状态
//    MQTT_Send_Command("AT+CWJAP?\r\n", "+CWJAP", 3, 1, 1000);
//    
//    printf("===== 调试结束 =====\n\n");
//}

mqtt.h

cs 复制代码
#ifndef __driver_wireless__
#define __driver_wireless__

#include <stdint.h>

#define W_ENABLE 	1
#define W_DISABLE 	0

#define W_FALSE 	0
#define W_TRUE 		1

#define W_OK		1    

#define ERROR_WiFi_CONNECT	(0x01 << 15)
#define ERROR_MQTT_CONNECT	(0x01 << 14)
#define ERROR_PUBLISH		(0x01 << 13)

// 定义类型标识常量
#define TYPE_BOOL		(0x01 << 0)
#define TYPE_INT 		(0x01 << 1)
#define TYPE_FLOAT 		(0x01 << 2)
#define TYPE_STRING 	(0x01 << 3)

typedef struct {
    uint8_t receiveDataFlag;		//接收数据标志位
    uint16_t error_code;			//无线模块初始化错误代码
}Wireless_TypeDef;

// 定义联合体,用于存储不同类型的数据
typedef union {
	uint8_t	bool_value;      //布尔型
    int16_t int_value;       // 整型
    float float_value;       // 浮点型
    const char *string_value; // 字符串
} Type_Value;

typedef struct 
{
	char *name;
	uint8_t type;
	Type_Value value;
}OneNET_MQTT_Data;

// 错误码定义(示例)
typedef enum {
    ERROR_NONE = 0,               // 无错误
    ERROR_TIMEOUT = 1,            // 超时错误
    ERROR_ONENET_CTRL_MSG = 2,    // 收到OneNET控制消息
    ERROR_INVALID_RESPONSE = 3,   // 响应格式无效
    // 更多错误类型...
} MQTT_ErrorCode;

extern OneNET_MQTT_Data *onenet_data_array[];
extern Wireless_TypeDef MQTT_Status;

extern uint8_t MQTT_RxBuffer[];

void update_sensor_data(void);
uint8_t MQTT_Get_Receive_Flag(void);
uint8_t MQTT_Get_Onenet_Command_flag(void);
void MQTT_Clear_Buffer(void);
void MQTT_System_Handler(void);
void MQTT_ONENET_Data_Handler(void);
void MQTT_Error_Handler(uint16_t error_code);
uint8_t MQTT_Send_Command(char *cmd,char *res,uint8_t MaxSendCount,uint8_t Clear_Buffer,uint16_t time);






void MQTT_INIT(void);
uint8_t WiFi_Init(void);
uint8_t MQTT_Config(void);



void MQTT_Publish_Data(void);
void MQTT_Receive_Callback(uint8_t byte);
uint8_t MQTT_Wifi_Connect_Status(void);
uint8_t MQTT_Connect_Status(void);
uint32_t MQTT_Get_Command_Id(char *jsonData);
void MQTT_Receiver_Command_Extract(char *jsonData);
void MQTT_Receive_Command_Respond(uint32_t respond_id, uint16_t code, char *msg);
void MQTT_Processing_Command(void);
void MQTT_Command_Control(void);
void MQTT_Receive_data_Handler(void);

void Test_WiFi_Only(void);
void WiFi_Connection_Debug(void);

#endif

cJSON.c

cs 复制代码
/*
  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors

  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. */

/* disable warnings about old C89 functions in MSVC */
#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
#define _CRT_SECURE_NO_DEPRECATE
#endif

#ifdef __GNUC__
#pragma GCC visibility push(default)
#endif
#if defined(_MSC_VER)
#pragma warning (push)
/* disable warning about single line comments in system headers */
#pragma warning (disable : 4001)
#endif

#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#include <float.h>

#ifdef ENABLE_LOCALES
#include <locale.h>
#endif

#if defined(_MSC_VER)
#pragma warning (pop)
#endif
#ifdef __GNUC__
#pragma GCC visibility pop
#endif

#include "cJSON.h"

/* define our own boolean type */
#ifdef true
#undef true
#endif
#define true ((cJSON_bool)1)

#ifdef false
#undef false
#endif
#define false ((cJSON_bool)0)

/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
#ifndef isinf
#define isinf(d) (isnan((d - d)) && !isnan(d))
#endif
#ifndef isnan
#define isnan(d) (d != d)
#endif

#ifndef NAN
#ifdef _WIN32
#define NAN sqrt(-1.0)
#else
#define NAN 0.0/0.0
#endif
#endif

typedef struct {
    const unsigned char *json;
    size_t position;
} error;
static error global_error = { NULL, 0 };

CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
{
    return (const char*) (global_error.json + global_error.position);
}

CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
{
    if (!cJSON_IsString(item))
    {
        return NULL;
    }

    return item->valuestring;
}

CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
{
    if (!cJSON_IsNumber(item))
    {
        return (double) NAN;
    }

    return item->valuedouble;
}

/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18)
    #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
#endif

CJSON_PUBLIC(const char*) cJSON_Version(void)
{
    static char version[15];
    sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);

    return version;
}

/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
{
    if ((string1 == NULL) || (string2 == NULL))
    {
        return 1;
    }

    if (string1 == string2)
    {
        return 0;
    }

    for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
    {
        if (*string1 == '\0')
        {
            return 0;
        }
    }

    return tolower(*string1) - tolower(*string2);
}

typedef struct internal_hooks
{
    void *(CJSON_CDECL *allocate)(size_t size);
    void (CJSON_CDECL *deallocate)(void *pointer);
    void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
} internal_hooks;

#if defined(_MSC_VER)
/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
static void * CJSON_CDECL internal_malloc(size_t size)
{
    return malloc(size);
}
static void CJSON_CDECL internal_free(void *pointer)
{
    free(pointer);
}
static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
{
    return realloc(pointer, size);
}
#else
#define internal_malloc malloc
#define internal_free free
#define internal_realloc realloc
#endif

/* strlen of character literals resolved at compile time */
#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))

static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };

static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
{
    size_t length = 0;
    unsigned char *copy = NULL;

    if (string == NULL)
    {
        return NULL;
    }

    length = strlen((const char*)string) + sizeof("");
    copy = (unsigned char*)hooks->allocate(length);
    if (copy == NULL)
    {
        return NULL;
    }
    memcpy(copy, string, length);

    return copy;
}

CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
{
    if (hooks == NULL)
    {
        /* Reset hooks */
        global_hooks.allocate = malloc;
        global_hooks.deallocate = free;
        global_hooks.reallocate = realloc;
        return;
    }

    global_hooks.allocate = malloc;
    if (hooks->malloc_fn != NULL)
    {
        global_hooks.allocate = hooks->malloc_fn;
    }

    global_hooks.deallocate = free;
    if (hooks->free_fn != NULL)
    {
        global_hooks.deallocate = hooks->free_fn;
    }

    /* use realloc only if both free and malloc are used */
    global_hooks.reallocate = NULL;
    if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
    {
        global_hooks.reallocate = realloc;
    }
}

/* Internal constructor. */
static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
{
    cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
    if (node)
    {
        memset(node, '\0', sizeof(cJSON));
    }

    return node;
}

/* Delete a cJSON structure. */
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
{
    cJSON *next = NULL;
    while (item != NULL)
    {
        next = item->next;
        if (!(item->type & cJSON_IsReference) && (item->child != NULL))
        {
            cJSON_Delete(item->child);
        }
        if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
        {
            global_hooks.deallocate(item->valuestring);
            item->valuestring = NULL;
        }
        if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
        {
            global_hooks.deallocate(item->string);
            item->string = NULL;
        }
        global_hooks.deallocate(item);
        item = next;
    }
}

/* get the decimal point character of the current locale */
static unsigned char get_decimal_point(void)
{
#ifdef ENABLE_LOCALES
    struct lconv *lconv = localeconv();
    return (unsigned char) lconv->decimal_point[0];
#else
    return '.';
#endif
}

typedef struct
{
    const unsigned char *content;
    size_t length;
    size_t offset;
    size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
    internal_hooks hooks;
} parse_buffer;

/* check if the given size is left to read in a given parse buffer (starting with 1) */
#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
/* check if the buffer can be accessed at the given index (starting with 0) */
#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
/* get a pointer to the buffer at the position */
#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)

/* Parse the input text to generate a number, and populate the result into item. */
static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
{
    double number = 0;
    unsigned char *after_end = NULL;
    unsigned char number_c_string[64];
    unsigned char decimal_point = get_decimal_point();
    size_t i = 0;

    if ((input_buffer == NULL) || (input_buffer->content == NULL))
    {
        return false;
    }

    /* copy the number into a temporary buffer and replace '.' with the decimal point
     * of the current locale (for strtod)
     * This also takes care of '\0' not necessarily being available for marking the end of the input */
    for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
    {
        switch (buffer_at_offset(input_buffer)[i])
        {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '+':
            case '-':
            case 'e':
            case 'E':
                number_c_string[i] = buffer_at_offset(input_buffer)[i];
                break;

            case '.':
                number_c_string[i] = decimal_point;
                break;

            default:
                goto loop_end;
        }
    }
loop_end:
    number_c_string[i] = '\0';

    number = strtod((const char*)number_c_string, (char**)&after_end);
    if (number_c_string == after_end)
    {
        return false; /* parse_error */
    }

    item->valuedouble = number;

    /* use saturation in case of overflow */
    if (number >= INT_MAX)
    {
        item->valueint = INT_MAX;
    }
    else if (number <= (double)INT_MIN)
    {
        item->valueint = INT_MIN;
    }
    else
    {
        item->valueint = (int)number;
    }

    item->type = cJSON_Number;

    input_buffer->offset += (size_t)(after_end - number_c_string);
    return true;
}

/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
{
    if (number >= INT_MAX)
    {
        object->valueint = INT_MAX;
    }
    else if (number <= (double)INT_MIN)
    {
        object->valueint = INT_MIN;
    }
    else
    {
        object->valueint = (int)number;
    }

    return object->valuedouble = number;
}

/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
{
    char *copy = NULL;
    /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
    if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference))
    {
        return NULL;
    }
    /* return NULL if the object is corrupted or valuestring is NULL */
    if (object->valuestring == NULL || valuestring == NULL)
    {
        return NULL;
    }
    if (strlen(valuestring) <= strlen(object->valuestring))
    {
        strcpy(object->valuestring, valuestring);
        return object->valuestring;
    }
    copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
    if (copy == NULL)
    {
        return NULL;
    }
    if (object->valuestring != NULL)
    {
        cJSON_free(object->valuestring);
    }
    object->valuestring = copy;

    return copy;
}

typedef struct
{
    unsigned char *buffer;
    size_t length;
    size_t offset;
    size_t depth; /* current nesting depth (for formatted printing) */
    cJSON_bool noalloc;
    cJSON_bool format; /* is this print a formatted print */
    internal_hooks hooks;
} printbuffer;

/* realloc printbuffer if necessary to have at least "needed" bytes more */
static unsigned char* ensure(printbuffer * const p, size_t needed)
{
    unsigned char *newbuffer = NULL;
    size_t newsize = 0;

    if ((p == NULL) || (p->buffer == NULL))
    {
        return NULL;
    }

    if ((p->length > 0) && (p->offset >= p->length))
    {
        /* make sure that offset is valid */
        return NULL;
    }

    if (needed > INT_MAX)
    {
        /* sizes bigger than INT_MAX are currently not supported */
        return NULL;
    }

    needed += p->offset + 1;
    if (needed <= p->length)
    {
        return p->buffer + p->offset;
    }

    if (p->noalloc) {
        return NULL;
    }

    /* calculate new buffer size */
    if (needed > (INT_MAX / 2))
    {
        /* overflow of int, use INT_MAX if possible */
        if (needed <= INT_MAX)
        {
            newsize = INT_MAX;
        }
        else
        {
            return NULL;
        }
    }
    else
    {
        newsize = needed * 2;
    }

    if (p->hooks.reallocate != NULL)
    {
        /* reallocate with realloc if available */
        newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
        if (newbuffer == NULL)
        {
            p->hooks.deallocate(p->buffer);
            p->length = 0;
            p->buffer = NULL;

            return NULL;
        }
    }
    else
    {
        /* otherwise reallocate manually */
        newbuffer = (unsigned char*)p->hooks.allocate(newsize);
        if (!newbuffer)
        {
            p->hooks.deallocate(p->buffer);
            p->length = 0;
            p->buffer = NULL;

            return NULL;
        }

        memcpy(newbuffer, p->buffer, p->offset + 1);
        p->hooks.deallocate(p->buffer);
    }
    p->length = newsize;
    p->buffer = newbuffer;

    return newbuffer + p->offset;
}

/* calculate the new length of the string in a printbuffer and update the offset */
static void update_offset(printbuffer * const buffer)
{
    const unsigned char *buffer_pointer = NULL;
    if ((buffer == NULL) || (buffer->buffer == NULL))
    {
        return;
    }
    buffer_pointer = buffer->buffer + buffer->offset;

    buffer->offset += strlen((const char*)buffer_pointer);
}

/* securely comparison of floating-point variables */
static cJSON_bool compare_double(double a, double b)
{
    double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
    return (fabs(a - b) <= maxVal * DBL_EPSILON);
}

/* Render the number nicely from the given item into a string. */
static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
{
    unsigned char *output_pointer = NULL;
    double d = item->valuedouble;
    int length = 0;
    size_t i = 0;
    unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
    unsigned char decimal_point = get_decimal_point();
    double test = 0.0;

    if (output_buffer == NULL)
    {
        return false;
    }

    /* This checks for NaN and Infinity */
    if (isnan(d) || isinf(d))
    {
        length = sprintf((char*)number_buffer, "null");
    }
    else if(d == (double)item->valueint)
    {
        length = sprintf((char*)number_buffer, "%d", item->valueint);
    }
    else
    {
        /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
        length = sprintf((char*)number_buffer, "%1.15g", d);

        /* Check whether the original double can be recovered */
        if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
        {
            /* If not, print with 17 decimal places of precision */
            length = sprintf((char*)number_buffer, "%1.17g", d);
        }
    }

    /* sprintf failed or buffer overrun occurred */
    if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
    {
        return false;
    }

    /* reserve appropriate space in the output */
    output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
    if (output_pointer == NULL)
    {
        return false;
    }

    /* copy the printed number to the output and replace locale
     * dependent decimal point with '.' */
    for (i = 0; i < ((size_t)length); i++)
    {
        if (number_buffer[i] == decimal_point)
        {
            output_pointer[i] = '.';
            continue;
        }

        output_pointer[i] = number_buffer[i];
    }
    output_pointer[i] = '\0';

    output_buffer->offset += (size_t)length;

    return true;
}

/* parse 4 digit hexadecimal number */
static unsigned parse_hex4(const unsigned char * const input)
{
    unsigned int h = 0;
    size_t i = 0;

    for (i = 0; i < 4; i++)
    {
        /* parse digit */
        if ((input[i] >= '0') && (input[i] <= '9'))
        {
            h += (unsigned int) input[i] - '0';
        }
        else if ((input[i] >= 'A') && (input[i] <= 'F'))
        {
            h += (unsigned int) 10 + input[i] - 'A';
        }
        else if ((input[i] >= 'a') && (input[i] <= 'f'))
        {
            h += (unsigned int) 10 + input[i] - 'a';
        }
        else /* invalid */
        {
            return 0;
        }

        if (i < 3)
        {
            /* shift left to make place for the next nibble */
            h = h << 4;
        }
    }

    return h;
}

/* converts a UTF-16 literal to UTF-8
 * A literal can be one or two sequences of the form \uXXXX */
static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
{
    long unsigned int codepoint = 0;
    unsigned int first_code = 0;
    const unsigned char *first_sequence = input_pointer;
    unsigned char utf8_length = 0;
    unsigned char utf8_position = 0;
    unsigned char sequence_length = 0;
    unsigned char first_byte_mark = 0;

    if ((input_end - first_sequence) < 6)
    {
        /* input ends unexpectedly */
        goto fail;
    }

    /* get the first utf16 sequence */
    first_code = parse_hex4(first_sequence + 2);

    /* check that the code is valid */
    if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
    {
        goto fail;
    }

    /* UTF16 surrogate pair */
    if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
    {
        const unsigned char *second_sequence = first_sequence + 6;
        unsigned int second_code = 0;
        sequence_length = 12; /* \uXXXX\uXXXX */

        if ((input_end - second_sequence) < 6)
        {
            /* input ends unexpectedly */
            goto fail;
        }

        if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
        {
            /* missing second half of the surrogate pair */
            goto fail;
        }

        /* get the second utf16 sequence */
        second_code = parse_hex4(second_sequence + 2);
        /* check that the code is valid */
        if ((second_code < 0xDC00) || (second_code > 0xDFFF))
        {
            /* invalid second half of the surrogate pair */
            goto fail;
        }


        /* calculate the unicode codepoint from the surrogate pair */
        codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
    }
    else
    {
        sequence_length = 6; /* \uXXXX */
        codepoint = first_code;
    }

    /* encode as UTF-8
     * takes at maximum 4 bytes to encode:
     * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
    if (codepoint < 0x80)
    {
        /* normal ascii, encoding 0xxxxxxx */
        utf8_length = 1;
    }
    else if (codepoint < 0x800)
    {
        /* two bytes, encoding 110xxxxx 10xxxxxx */
        utf8_length = 2;
        first_byte_mark = 0xC0; /* 11000000 */
    }
    else if (codepoint < 0x10000)
    {
        /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
        utf8_length = 3;
        first_byte_mark = 0xE0; /* 11100000 */
    }
    else if (codepoint <= 0x10FFFF)
    {
        /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
        utf8_length = 4;
        first_byte_mark = 0xF0; /* 11110000 */
    }
    else
    {
        /* invalid unicode codepoint */
        goto fail;
    }

    /* encode as utf8 */
    for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
    {
        /* 10xxxxxx */
        (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
        codepoint >>= 6;
    }
    /* encode first byte */
    if (utf8_length > 1)
    {
        (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
    }
    else
    {
        (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
    }

    *output_pointer += utf8_length;

    return sequence_length;

fail:
    return 0;
}

/* Parse the input text into an unescaped cinput, and populate item. */
static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
{
    const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
    const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
    unsigned char *output_pointer = NULL;
    unsigned char *output = NULL;

    /* not a string */
    if (buffer_at_offset(input_buffer)[0] != '\"')
    {
        goto fail;
    }

    {
        /* calculate approximate size of the output (overestimate) */
        size_t allocation_length = 0;
        size_t skipped_bytes = 0;
        while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
        {
            /* is escape sequence */
            if (input_end[0] == '\\')
            {
                if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
                {
                    /* prevent buffer overflow when last input character is a backslash */
                    goto fail;
                }
                skipped_bytes++;
                input_end++;
            }
            input_end++;
        }
        if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
        {
            goto fail; /* string ended unexpectedly */
        }

        /* This is at most how much we need for the output */
        allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
        output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
        if (output == NULL)
        {
            goto fail; /* allocation failure */
        }
    }

    output_pointer = output;
    /* loop through the string literal */
    while (input_pointer < input_end)
    {
        if (*input_pointer != '\\')
        {
            *output_pointer++ = *input_pointer++;
        }
        /* escape sequence */
        else
        {
            unsigned char sequence_length = 2;
            if ((input_end - input_pointer) < 1)
            {
                goto fail;
            }

            switch (input_pointer[1])
            {
                case 'b':
                    *output_pointer++ = '\b';
                    break;
                case 'f':
                    *output_pointer++ = '\f';
                    break;
                case 'n':
                    *output_pointer++ = '\n';
                    break;
                case 'r':
                    *output_pointer++ = '\r';
                    break;
                case 't':
                    *output_pointer++ = '\t';
                    break;
                case '\"':
                case '\\':
                case '/':
                    *output_pointer++ = input_pointer[1];
                    break;

                /* UTF-16 literal */
                case 'u':
                    sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
                    if (sequence_length == 0)
                    {
                        /* failed to convert UTF16-literal to UTF-8 */
                        goto fail;
                    }
                    break;

                default:
                    goto fail;
            }
            input_pointer += sequence_length;
        }
    }

    /* zero terminate the output */
    *output_pointer = '\0';

    item->type = cJSON_String;
    item->valuestring = (char*)output;

    input_buffer->offset = (size_t) (input_end - input_buffer->content);
    input_buffer->offset++;

    return true;

fail:
    if (output != NULL)
    {
        input_buffer->hooks.deallocate(output);
        output = NULL;
    }

    if (input_pointer != NULL)
    {
        input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
    }

    return false;
}

/* Render the cstring provided to an escaped version that can be printed. */
static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
{
    const unsigned char *input_pointer = NULL;
    unsigned char *output = NULL;
    unsigned char *output_pointer = NULL;
    size_t output_length = 0;
    /* numbers of additional characters needed for escaping */
    size_t escape_characters = 0;

    if (output_buffer == NULL)
    {
        return false;
    }

    /* empty string */
    if (input == NULL)
    {
        output = ensure(output_buffer, sizeof("\"\""));
        if (output == NULL)
        {
            return false;
        }
        strcpy((char*)output, "\"\"");

        return true;
    }

    /* set "flag" to 1 if something needs to be escaped */
    for (input_pointer = input; *input_pointer; input_pointer++)
    {
        switch (*input_pointer)
        {
            case '\"':
            case '\\':
            case '\b':
            case '\f':
            case '\n':
            case '\r':
            case '\t':
                /* one character escape sequence */
                escape_characters++;
                break;
            default:
                if (*input_pointer < 32)
                {
                    /* UTF-16 escape sequence uXXXX */
                    escape_characters += 5;
                }
                break;
        }
    }
    output_length = (size_t)(input_pointer - input) + escape_characters;

    output = ensure(output_buffer, output_length + sizeof("\"\""));
    if (output == NULL)
    {
        return false;
    }

    /* no characters have to be escaped */
    if (escape_characters == 0)
    {
        output[0] = '\"';
        memcpy(output + 1, input, output_length);
        output[output_length + 1] = '\"';
        output[output_length + 2] = '\0';

        return true;
    }

    output[0] = '\"';
    output_pointer = output + 1;
    /* copy the string */
    for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
    {
        if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
        {
            /* normal character, copy */
            *output_pointer = *input_pointer;
        }
        else
        {
            /* character needs to be escaped */
            *output_pointer++ = '\\';
            switch (*input_pointer)
            {
                case '\\':
                    *output_pointer = '\\';
                    break;
                case '\"':
                    *output_pointer = '\"';
                    break;
                case '\b':
                    *output_pointer = 'b';
                    break;
                case '\f':
                    *output_pointer = 'f';
                    break;
                case '\n':
                    *output_pointer = 'n';
                    break;
                case '\r':
                    *output_pointer = 'r';
                    break;
                case '\t':
                    *output_pointer = 't';
                    break;
                default:
                    /* escape and print as unicode codepoint */
                    sprintf((char*)output_pointer, "u%04x", *input_pointer);
                    output_pointer += 4;
                    break;
            }
        }
    }
    output[output_length + 1] = '\"';
    output[output_length + 2] = '\0';

    return true;
}

/* Invoke print_string_ptr (which is useful) on an item. */
static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
{
    return print_string_ptr((unsigned char*)item->valuestring, p);
}

/* Predeclare these prototypes. */
static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);

/* Utility to jump whitespace and cr/lf */
static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
{
    if ((buffer == NULL) || (buffer->content == NULL))
    {
        return NULL;
    }

    if (cannot_access_at_index(buffer, 0))
    {
        return buffer;
    }

    while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
    {
       buffer->offset++;
    }

    if (buffer->offset == buffer->length)
    {
        buffer->offset--;
    }

    return buffer;
}

/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
{
    if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
    {
        return NULL;
    }

    if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
    {
        buffer->offset += 3;
    }

    return buffer;
}

CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
{
    size_t buffer_length;

    if (NULL == value)
    {
        return NULL;
    }

    /* Adding null character size due to require_null_terminated. */
    buffer_length = strlen(value) + sizeof("");

    return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
}

/* Parse an object - create a new root, and populate. */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
{
    parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
    cJSON *item = NULL;

    /* reset error position */
    global_error.json = NULL;
    global_error.position = 0;

    if (value == NULL || 0 == buffer_length)
    {
        goto fail;
    }

    buffer.content = (const unsigned char*)value;
    buffer.length = buffer_length;
    buffer.offset = 0;
    buffer.hooks = global_hooks;

    item = cJSON_New_Item(&global_hooks);
    if (item == NULL) /* memory fail */
    {
        goto fail;
    }

    if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
    {
        /* parse failure. ep is set. */
        goto fail;
    }

    /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
    if (require_null_terminated)
    {
        buffer_skip_whitespace(&buffer);
        if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
        {
            goto fail;
        }
    }
    if (return_parse_end)
    {
        *return_parse_end = (const char*)buffer_at_offset(&buffer);
    }

    return item;

fail:
    if (item != NULL)
    {
        cJSON_Delete(item);
    }

    if (value != NULL)
    {
        error local_error;
        local_error.json = (const unsigned char*)value;
        local_error.position = 0;

        if (buffer.offset < buffer.length)
        {
            local_error.position = buffer.offset;
        }
        else if (buffer.length > 0)
        {
            local_error.position = buffer.length - 1;
        }

        if (return_parse_end != NULL)
        {
            *return_parse_end = (const char*)local_error.json + local_error.position;
        }

        global_error = local_error;
    }

    return NULL;
}

/* Default options for cJSON_Parse */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
{
    return cJSON_ParseWithOpts(value, 0, 0);
}

CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
{
    return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
}

#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))

static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
{
    static const size_t default_buffer_size = 256;
    printbuffer buffer[1];
    unsigned char *printed = NULL;

    memset(buffer, 0, sizeof(buffer));

    /* create buffer */
    buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
    buffer->length = default_buffer_size;
    buffer->format = format;
    buffer->hooks = *hooks;
    if (buffer->buffer == NULL)
    {
        goto fail;
    }

    /* print the value */
    if (!print_value(item, buffer))
    {
        goto fail;
    }
    update_offset(buffer);

    /* check if reallocate is available */
    if (hooks->reallocate != NULL)
    {
        printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
        if (printed == NULL) {
            goto fail;
        }
        buffer->buffer = NULL;
    }
    else /* otherwise copy the JSON over to a new buffer */
    {
        printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
        if (printed == NULL)
        {
            goto fail;
        }
        memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
        printed[buffer->offset] = '\0'; /* just to be sure */

        /* free the buffer */
        hooks->deallocate(buffer->buffer);
        buffer->buffer = NULL;
    }

    return printed;

fail:
    if (buffer->buffer != NULL)
    {
        hooks->deallocate(buffer->buffer);
        buffer->buffer = NULL;
    }

    if (printed != NULL)
    {
        hooks->deallocate(printed);
        printed = NULL;
    }

    return NULL;
}

/* Render a cJSON item/entity/structure to text. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
{
    return (char*)print(item, true, &global_hooks);
}

CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
{
    return (char*)print(item, false, &global_hooks);
}

CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
{
    printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };

    if (prebuffer < 0)
    {
        return NULL;
    }

    p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
    if (!p.buffer)
    {
        return NULL;
    }

    p.length = (size_t)prebuffer;
    p.offset = 0;
    p.noalloc = false;
    p.format = fmt;
    p.hooks = global_hooks;

    if (!print_value(item, &p))
    {
        global_hooks.deallocate(p.buffer);
        p.buffer = NULL;
        return NULL;
    }

    return (char*)p.buffer;
}

CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
{
    printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };

    if ((length < 0) || (buffer == NULL))
    {
        return false;
    }

    p.buffer = (unsigned char*)buffer;
    p.length = (size_t)length;
    p.offset = 0;
    p.noalloc = true;
    p.format = format;
    p.hooks = global_hooks;

    return print_value(item, &p);
}

/* Parser core - when encountering text, process appropriately. */
static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
{
    if ((input_buffer == NULL) || (input_buffer->content == NULL))
    {
        return false; /* no input */
    }

    /* parse the different types of values */
    /* null */
    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
    {
        item->type = cJSON_NULL;
        input_buffer->offset += 4;
        return true;
    }
    /* false */
    if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
    {
        item->type = cJSON_False;
        input_buffer->offset += 5;
        return true;
    }
    /* true */
    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
    {
        item->type = cJSON_True;
        item->valueint = 1;
        input_buffer->offset += 4;
        return true;
    }
    /* string */
    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
    {
        return parse_string(item, input_buffer);
    }
    /* number */
    if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
    {
        return parse_number(item, input_buffer);
    }
    /* array */
    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
    {
        return parse_array(item, input_buffer);
    }
    /* object */
    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
    {
        return parse_object(item, input_buffer);
    }

    return false;
}

/* Render a value to text. */
static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
{
    unsigned char *output = NULL;

    if ((item == NULL) || (output_buffer == NULL))
    {
        return false;
    }

    switch ((item->type) & 0xFF)
    {
        case cJSON_NULL:
            output = ensure(output_buffer, 5);
            if (output == NULL)
            {
                return false;
            }
            strcpy((char*)output, "null");
            return true;

        case cJSON_False:
            output = ensure(output_buffer, 6);
            if (output == NULL)
            {
                return false;
            }
            strcpy((char*)output, "false");
            return true;

        case cJSON_True:
            output = ensure(output_buffer, 5);
            if (output == NULL)
            {
                return false;
            }
            strcpy((char*)output, "true");
            return true;

        case cJSON_Number:
            return print_number(item, output_buffer);

        case cJSON_Raw:
        {
            size_t raw_length = 0;
            if (item->valuestring == NULL)
            {
                return false;
            }

            raw_length = strlen(item->valuestring) + sizeof("");
            output = ensure(output_buffer, raw_length);
            if (output == NULL)
            {
                return false;
            }
            memcpy(output, item->valuestring, raw_length);
            return true;
        }

        case cJSON_String:
            return print_string(item, output_buffer);

        case cJSON_Array:
            return print_array(item, output_buffer);

        case cJSON_Object:
            return print_object(item, output_buffer);

        default:
            return false;
    }
}

/* Build an array from input text. */
static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
{
    cJSON *head = NULL; /* head of the linked list */
    cJSON *current_item = NULL;

    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
    {
        return false; /* to deeply nested */
    }
    input_buffer->depth++;

    if (buffer_at_offset(input_buffer)[0] != '[')
    {
        /* not an array */
        goto fail;
    }

    input_buffer->offset++;
    buffer_skip_whitespace(input_buffer);
    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
    {
        /* empty array */
        goto success;
    }

    /* check if we skipped to the end of the buffer */
    if (cannot_access_at_index(input_buffer, 0))
    {
        input_buffer->offset--;
        goto fail;
    }

    /* step back to character in front of the first element */
    input_buffer->offset--;
    /* loop through the comma separated array elements */
    do
    {
        /* allocate next item */
        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
        if (new_item == NULL)
        {
            goto fail; /* allocation failure */
        }

        /* attach next item to list */
        if (head == NULL)
        {
            /* start the linked list */
            current_item = head = new_item;
        }
        else
        {
            /* add to the end and advance */
            current_item->next = new_item;
            new_item->prev = current_item;
            current_item = new_item;
        }

        /* parse next value */
        input_buffer->offset++;
        buffer_skip_whitespace(input_buffer);
        if (!parse_value(current_item, input_buffer))
        {
            goto fail; /* failed to parse value */
        }
        buffer_skip_whitespace(input_buffer);
    }
    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));

    if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
    {
        goto fail; /* expected end of array */
    }

success:
    input_buffer->depth--;

    if (head != NULL) {
        head->prev = current_item;
    }

    item->type = cJSON_Array;
    item->child = head;

    input_buffer->offset++;

    return true;

fail:
    if (head != NULL)
    {
        cJSON_Delete(head);
    }

    return false;
}

/* Render an array to text */
static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
{
    unsigned char *output_pointer = NULL;
    size_t length = 0;
    cJSON *current_element = item->child;

    if (output_buffer == NULL)
    {
        return false;
    }

    /* Compose the output array. */
    /* opening square bracket */
    output_pointer = ensure(output_buffer, 1);
    if (output_pointer == NULL)
    {
        return false;
    }

    *output_pointer = '[';
    output_buffer->offset++;
    output_buffer->depth++;

    while (current_element != NULL)
    {
        if (!print_value(current_element, output_buffer))
        {
            return false;
        }
        update_offset(output_buffer);
        if (current_element->next)
        {
            length = (size_t) (output_buffer->format ? 2 : 1);
            output_pointer = ensure(output_buffer, length + 1);
            if (output_pointer == NULL)
            {
                return false;
            }
            *output_pointer++ = ',';
            if(output_buffer->format)
            {
                *output_pointer++ = ' ';
            }
            *output_pointer = '\0';
            output_buffer->offset += length;
        }
        current_element = current_element->next;
    }

    output_pointer = ensure(output_buffer, 2);
    if (output_pointer == NULL)
    {
        return false;
    }
    *output_pointer++ = ']';
    *output_pointer = '\0';
    output_buffer->depth--;

    return true;
}

/* Build an object from the text. */
static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
{
    cJSON *head = NULL; /* linked list head */
    cJSON *current_item = NULL;

    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
    {
        return false; /* to deeply nested */
    }
    input_buffer->depth++;

    if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
    {
        goto fail; /* not an object */
    }

    input_buffer->offset++;
    buffer_skip_whitespace(input_buffer);
    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
    {
        goto success; /* empty object */
    }

    /* check if we skipped to the end of the buffer */
    if (cannot_access_at_index(input_buffer, 0))
    {
        input_buffer->offset--;
        goto fail;
    }

    /* step back to character in front of the first element */
    input_buffer->offset--;
    /* loop through the comma separated array elements */
    do
    {
        /* allocate next item */
        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
        if (new_item == NULL)
        {
            goto fail; /* allocation failure */
        }

        /* attach next item to list */
        if (head == NULL)
        {
            /* start the linked list */
            current_item = head = new_item;
        }
        else
        {
            /* add to the end and advance */
            current_item->next = new_item;
            new_item->prev = current_item;
            current_item = new_item;
        }

        if (cannot_access_at_index(input_buffer, 1))
        {
            goto fail; /* nothing comes after the comma */
        }

        /* parse the name of the child */
        input_buffer->offset++;
        buffer_skip_whitespace(input_buffer);
        if (!parse_string(current_item, input_buffer))
        {
            goto fail; /* failed to parse name */
        }
        buffer_skip_whitespace(input_buffer);

        /* swap valuestring and string, because we parsed the name */
        current_item->string = current_item->valuestring;
        current_item->valuestring = NULL;

        if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
        {
            goto fail; /* invalid object */
        }

        /* parse the value */
        input_buffer->offset++;
        buffer_skip_whitespace(input_buffer);
        if (!parse_value(current_item, input_buffer))
        {
            goto fail; /* failed to parse value */
        }
        buffer_skip_whitespace(input_buffer);
    }
    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));

    if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
    {
        goto fail; /* expected end of object */
    }

success:
    input_buffer->depth--;

    if (head != NULL) {
        head->prev = current_item;
    }

    item->type = cJSON_Object;
    item->child = head;

    input_buffer->offset++;
    return true;

fail:
    if (head != NULL)
    {
        cJSON_Delete(head);
    }

    return false;
}

/* Render an object to text. */
static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
{
    unsigned char *output_pointer = NULL;
    size_t length = 0;
    cJSON *current_item = item->child;

    if (output_buffer == NULL)
    {
        return false;
    }

    /* Compose the output: */
    length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
    output_pointer = ensure(output_buffer, length + 1);
    if (output_pointer == NULL)
    {
        return false;
    }

    *output_pointer++ = '{';
    output_buffer->depth++;
    if (output_buffer->format)
    {
        *output_pointer++ = '\n';
    }
    output_buffer->offset += length;

    while (current_item)
    {
        if (output_buffer->format)
        {
            size_t i;
            output_pointer = ensure(output_buffer, output_buffer->depth);
            if (output_pointer == NULL)
            {
                return false;
            }
            for (i = 0; i < output_buffer->depth; i++)
            {
                *output_pointer++ = '\t';
            }
            output_buffer->offset += output_buffer->depth;
        }

        /* print key */
        if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
        {
            return false;
        }
        update_offset(output_buffer);

        length = (size_t) (output_buffer->format ? 2 : 1);
        output_pointer = ensure(output_buffer, length);
        if (output_pointer == NULL)
        {
            return false;
        }
        *output_pointer++ = ':';
        if (output_buffer->format)
        {
            *output_pointer++ = '\t';
        }
        output_buffer->offset += length;

        /* print value */
        if (!print_value(current_item, output_buffer))
        {
            return false;
        }
        update_offset(output_buffer);

        /* print comma if not last */
        length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
        output_pointer = ensure(output_buffer, length + 1);
        if (output_pointer == NULL)
        {
            return false;
        }
        if (current_item->next)
        {
            *output_pointer++ = ',';
        }

        if (output_buffer->format)
        {
            *output_pointer++ = '\n';
        }
        *output_pointer = '\0';
        output_buffer->offset += length;

        current_item = current_item->next;
    }

    output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
    if (output_pointer == NULL)
    {
        return false;
    }
    if (output_buffer->format)
    {
        size_t i;
        for (i = 0; i < (output_buffer->depth - 1); i++)
        {
            *output_pointer++ = '\t';
        }
    }
    *output_pointer++ = '}';
    *output_pointer = '\0';
    output_buffer->depth--;

    return true;
}

/* Get Array size/item / object item. */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
{
    cJSON *child = NULL;
    size_t size = 0;

    if (array == NULL)
    {
        return 0;
    }

    child = array->child;

    while(child != NULL)
    {
        size++;
        child = child->next;
    }

    /* FIXME: Can overflow here. Cannot be fixed without breaking the API */

    return (int)size;
}

static cJSON* get_array_item(const cJSON *array, size_t index)
{
    cJSON *current_child = NULL;

    if (array == NULL)
    {
        return NULL;
    }

    current_child = array->child;
    while ((current_child != NULL) && (index > 0))
    {
        index--;
        current_child = current_child->next;
    }

    return current_child;
}

CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
{
    if (index < 0)
    {
        return NULL;
    }

    return get_array_item(array, (size_t)index);
}

static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
{
    cJSON *current_element = NULL;

    if ((object == NULL) || (name == NULL))
    {
        return NULL;
    }

    current_element = object->child;
    if (case_sensitive)
    {
        while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
        {
            current_element = current_element->next;
        }
    }
    else
    {
        while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
        {
            current_element = current_element->next;
        }
    }

    if ((current_element == NULL) || (current_element->string == NULL)) {
        return NULL;
    }

    return current_element;
}

CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
{
    return get_object_item(object, string, false);
}

CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
{
    return get_object_item(object, string, true);
}

CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
{
    return cJSON_GetObjectItem(object, string) ? 1 : 0;
}

/* 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(const cJSON *item, const internal_hooks * const hooks)
{
    cJSON *reference = NULL;
    if (item == NULL)
    {
        return NULL;
    }

    reference = cJSON_New_Item(hooks);
    if (reference == NULL)
    {
        return NULL;
    }

    memcpy(reference, item, sizeof(cJSON));
    reference->string = NULL;
    reference->type |= cJSON_IsReference;
    reference->next = reference->prev = NULL;
    return reference;
}

static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
{
    cJSON *child = NULL;

    if ((item == NULL) || (array == NULL) || (array == item))
    {
        return false;
    }

    child = array->child;
    /*
     * To find the last item in array quickly, we use prev in array
     */
    if (child == NULL)
    {
        /* list is empty, start new one */
        array->child = item;
        item->prev = item;
        item->next = NULL;
    }
    else
    {
        /* append to the end */
        if (child->prev)
        {
            suffix_object(child->prev, item);
            array->child->prev = item;
        }
    }

    return true;
}

/* Add item to array/object. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
{
    return add_item_to_array(array, item);
}

#if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
    #pragma GCC diagnostic push
#endif
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wcast-qual"
#endif
/* helper function to cast away const */
static void* cast_away_const(const void* string)
{
    return (void*)string;
}
#if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
    #pragma GCC diagnostic pop
#endif


static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
{
    char *new_key = NULL;
    int new_type = cJSON_Invalid;

    if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
    {
        return false;
    }

    if (constant_key)
    {
        new_key = (char*)cast_away_const(string);
        new_type = item->type | cJSON_StringIsConst;
    }
    else
    {
        new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
        if (new_key == NULL)
        {
            return false;
        }

        new_type = item->type & ~cJSON_StringIsConst;
    }

    if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
    {
        hooks->deallocate(item->string);
    }

    item->string = new_key;
    item->type = new_type;

    return add_item_to_array(object, item);
}

CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
{
    return add_item_to_object(object, string, item, &global_hooks, false);
}

/* Add an item to an object with constant string as key */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
{
    return add_item_to_object(object, string, item, &global_hooks, true);
}

CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
{
    if (array == NULL)
    {
        return false;
    }

    return add_item_to_array(array, create_reference(item, &global_hooks));
}

CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
{
    if ((object == NULL) || (string == NULL))
    {
        return false;
    }

    return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
}

CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
{
    cJSON *null = cJSON_CreateNull();
    if (add_item_to_object(object, name, null, &global_hooks, false))
    {
        return null;
    }

    cJSON_Delete(null);
    return NULL;
}

CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
{
    cJSON *true_item = cJSON_CreateTrue();
    if (add_item_to_object(object, name, true_item, &global_hooks, false))
    {
        return true_item;
    }

    cJSON_Delete(true_item);
    return NULL;
}

CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
{
    cJSON *false_item = cJSON_CreateFalse();
    if (add_item_to_object(object, name, false_item, &global_hooks, false))
    {
        return false_item;
    }

    cJSON_Delete(false_item);
    return NULL;
}

CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
{
    cJSON *bool_item = cJSON_CreateBool(boolean);
    if (add_item_to_object(object, name, bool_item, &global_hooks, false))
    {
        return bool_item;
    }

    cJSON_Delete(bool_item);
    return NULL;
}

CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
{
    cJSON *number_item = cJSON_CreateNumber(number);
    if (add_item_to_object(object, name, number_item, &global_hooks, false))
    {
        return number_item;
    }

    cJSON_Delete(number_item);
    return NULL;
}

CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
{
    cJSON *string_item = cJSON_CreateString(string);
    if (add_item_to_object(object, name, string_item, &global_hooks, false))
    {
        return string_item;
    }

    cJSON_Delete(string_item);
    return NULL;
}

CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
{
    cJSON *raw_item = cJSON_CreateRaw(raw);
    if (add_item_to_object(object, name, raw_item, &global_hooks, false))
    {
        return raw_item;
    }

    cJSON_Delete(raw_item);
    return NULL;
}

CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
{
    cJSON *object_item = cJSON_CreateObject();
    if (add_item_to_object(object, name, object_item, &global_hooks, false))
    {
        return object_item;
    }

    cJSON_Delete(object_item);
    return NULL;
}

CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
{
    cJSON *array = cJSON_CreateArray();
    if (add_item_to_object(object, name, array, &global_hooks, false))
    {
        return array;
    }

    cJSON_Delete(array);
    return NULL;
}

CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
{
    if ((parent == NULL) || (item == NULL))
    {
        return NULL;
    }

    if (item != parent->child)
    {
        /* not the first element */
        item->prev->next = item->next;
    }
    if (item->next != NULL)
    {
        /* not the last element */
        item->next->prev = item->prev;
    }

    if (item == parent->child)
    {
        /* first element */
        parent->child = item->next;
    }
    else if (item->next == NULL)
    {
        /* last element */
        parent->child->prev = item->prev;
    }

    /* make sure the detached item doesn't point anywhere anymore */
    item->prev = NULL;
    item->next = NULL;

    return item;
}

CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
{
    if (which < 0)
    {
        return NULL;
    }

    return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
}

CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
{
    cJSON_Delete(cJSON_DetachItemFromArray(array, which));
}

CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
{
    cJSON *to_detach = cJSON_GetObjectItem(object, string);

    return cJSON_DetachItemViaPointer(object, to_detach);
}

CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
{
    cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);

    return cJSON_DetachItemViaPointer(object, to_detach);
}

CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
{
    cJSON_Delete(cJSON_DetachItemFromObject(object, string));
}

CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
{
    cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
}

/* Replace array/object items with new ones. */
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
{
    cJSON *after_inserted = NULL;

    if (which < 0 || newitem == NULL)
    {
        return false;
    }

    after_inserted = get_array_item(array, (size_t)which);
    if (after_inserted == NULL)
    {
        return add_item_to_array(array, newitem);
    }

    if (after_inserted != array->child && after_inserted->prev == NULL) {
        /* return false if after_inserted is a corrupted array item */
        return false;
    }

    newitem->next = after_inserted;
    newitem->prev = after_inserted->prev;
    after_inserted->prev = newitem;
    if (after_inserted == array->child)
    {
        array->child = newitem;
    }
    else
    {
        newitem->prev->next = newitem;
    }
    return true;
}

CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
{
    if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL))
    {
        return false;
    }

    if (replacement == item)
    {
        return true;
    }

    replacement->next = item->next;
    replacement->prev = item->prev;

    if (replacement->next != NULL)
    {
        replacement->next->prev = replacement;
    }
    if (parent->child == item)
    {
        if (parent->child->prev == parent->child)
        {
            replacement->prev = replacement;
        }
        parent->child = replacement;
    }
    else
    {   /*
         * To find the last item in array quickly, we use prev in array.
         * We can't modify the last item's next pointer where this item was the parent's child
         */
        if (replacement->prev != NULL)
        {
            replacement->prev->next = replacement;
        }
        if (replacement->next == NULL)
        {
            parent->child->prev = replacement;
        }
    }

    item->next = NULL;
    item->prev = NULL;
    cJSON_Delete(item);

    return true;
}

CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
{
    if (which < 0)
    {
        return false;
    }

    return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
}

static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
{
    if ((replacement == NULL) || (string == NULL))
    {
        return false;
    }

    /* replace the name in the replacement */
    if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
    {
        cJSON_free(replacement->string);
    }
    replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
    if (replacement->string == NULL)
    {
        return false;
    }

    replacement->type &= ~cJSON_StringIsConst;

    return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
}

CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
{
    return replace_item_in_object(object, string, newitem, false);
}

CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
{
    return replace_item_in_object(object, string, newitem, true);
}

/* Create basic types: */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if(item)
    {
        item->type = cJSON_NULL;
    }

    return item;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if(item)
    {
        item->type = cJSON_True;
    }

    return item;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if(item)
    {
        item->type = cJSON_False;
    }

    return item;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if(item)
    {
        item->type = boolean ? cJSON_True : cJSON_False;
    }

    return item;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if(item)
    {
        item->type = cJSON_Number;
        item->valuedouble = num;

        /* use saturation in case of overflow */
        if (num >= INT_MAX)
        {
            item->valueint = INT_MAX;
        }
        else if (num <= (double)INT_MIN)
        {
            item->valueint = INT_MIN;
        }
        else
        {
            item->valueint = (int)num;
        }
    }

    return item;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if(item)
    {
        item->type = cJSON_String;
        item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
        if(!item->valuestring)
        {
            cJSON_Delete(item);
            return NULL;
        }
    }

    return item;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if (item != NULL)
    {
        item->type = cJSON_String | cJSON_IsReference;
        item->valuestring = (char*)cast_away_const(string);
    }

    return item;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if (item != NULL) {
        item->type = cJSON_Object | cJSON_IsReference;
        item->child = (cJSON*)cast_away_const(child);
    }

    return item;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
    cJSON *item = cJSON_New_Item(&global_hooks);
    if (item != NULL) {
        item->type = cJSON_Array | cJSON_IsReference;
        item->child = (cJSON*)cast_away_const(child);
    }

    return item;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if(item)
    {
        item->type = cJSON_Raw;
        item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
        if(!item->valuestring)
        {
            cJSON_Delete(item);
            return NULL;
        }
    }

    return item;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if(item)
    {
        item->type=cJSON_Array;
    }

    return item;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
{
    cJSON *item = cJSON_New_Item(&global_hooks);
    if (item)
    {
        item->type = cJSON_Object;
    }

    return item;
}

/* Create Arrays: */
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
{
    size_t i = 0;
    cJSON *n = NULL;
    cJSON *p = NULL;
    cJSON *a = NULL;

    if ((count < 0) || (numbers == NULL))
    {
        return NULL;
    }

    a = cJSON_CreateArray();

    for(i = 0; a && (i < (size_t)count); i++)
    {
        n = cJSON_CreateNumber(numbers[i]);
        if (!n)
        {
            cJSON_Delete(a);
            return NULL;
        }
        if(!i)
        {
            a->child = n;
        }
        else
        {
            suffix_object(p, n);
        }
        p = n;
    }

    if (a && a->child) {
        a->child->prev = n;
    }

    return a;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
{
    size_t i = 0;
    cJSON *n = NULL;
    cJSON *p = NULL;
    cJSON *a = NULL;

    if ((count < 0) || (numbers == NULL))
    {
        return NULL;
    }

    a = cJSON_CreateArray();

    for(i = 0; a && (i < (size_t)count); i++)
    {
        n = cJSON_CreateNumber((double)numbers[i]);
        if(!n)
        {
            cJSON_Delete(a);
            return NULL;
        }
        if(!i)
        {
            a->child = n;
        }
        else
        {
            suffix_object(p, n);
        }
        p = n;
    }

    if (a && a->child) {
        a->child->prev = n;
    }

    return a;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
{
    size_t i = 0;
    cJSON *n = NULL;
    cJSON *p = NULL;
    cJSON *a = NULL;

    if ((count < 0) || (numbers == NULL))
    {
        return NULL;
    }

    a = cJSON_CreateArray();

    for(i = 0; a && (i < (size_t)count); i++)
    {
        n = cJSON_CreateNumber(numbers[i]);
        if(!n)
        {
            cJSON_Delete(a);
            return NULL;
        }
        if(!i)
        {
            a->child = n;
        }
        else
        {
            suffix_object(p, n);
        }
        p = n;
    }

    if (a && a->child) {
        a->child->prev = n;
    }

    return a;
}

CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
{
    size_t i = 0;
    cJSON *n = NULL;
    cJSON *p = NULL;
    cJSON *a = NULL;

    if ((count < 0) || (strings == NULL))
    {
        return NULL;
    }

    a = cJSON_CreateArray();

    for (i = 0; a && (i < (size_t)count); i++)
    {
        n = cJSON_CreateString(strings[i]);
        if(!n)
        {
            cJSON_Delete(a);
            return NULL;
        }
        if(!i)
        {
            a->child = n;
        }
        else
        {
            suffix_object(p,n);
        }
        p = n;
    }

    if (a && a->child) {
        a->child->prev = n;
    }

    return a;
}

/* Duplication */
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
{
    cJSON *newitem = NULL;
    cJSON *child = NULL;
    cJSON *next = NULL;
    cJSON *newchild = NULL;

    /* Bail on bad ptr */
    if (!item)
    {
        goto fail;
    }
    /* Create new item */
    newitem = cJSON_New_Item(&global_hooks);
    if (!newitem)
    {
        goto fail;
    }
    /* Copy over all vars */
    newitem->type = item->type & (~cJSON_IsReference);
    newitem->valueint = item->valueint;
    newitem->valuedouble = item->valuedouble;
    if (item->valuestring)
    {
        newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
        if (!newitem->valuestring)
        {
            goto fail;
        }
    }
    if (item->string)
    {
        newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
        if (!newitem->string)
        {
            goto fail;
        }
    }
    /* If non-recursive, then we're done! */
    if (!recurse)
    {
        return newitem;
    }
    /* Walk the ->next chain for the child. */
    child = item->child;
    while (child != NULL)
    {
        newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
        if (!newchild)
        {
            goto fail;
        }
        if (next != NULL)
        {
            /* If newitem->child already set, then crosswire ->prev and ->next and move on */
            next->next = newchild;
            newchild->prev = next;
            next = newchild;
        }
        else
        {
            /* Set newitem->child and move to it */
            newitem->child = newchild;
            next = newchild;
        }
        child = child->next;
    }
    if (newitem && newitem->child)
    {
        newitem->child->prev = newchild;
    }

    return newitem;

fail:
    if (newitem != NULL)
    {
        cJSON_Delete(newitem);
    }

    return NULL;
}

static void skip_oneline_comment(char **input)
{
    *input += static_strlen("//");

    for (; (*input)[0] != '\0'; ++(*input))
    {
        if ((*input)[0] == '\n') {
            *input += static_strlen("\n");
            return;
        }
    }
}

static void skip_multiline_comment(char **input)
{
    *input += static_strlen("/*");

    for (; (*input)[0] != '\0'; ++(*input))
    {
        if (((*input)[0] == '*') && ((*input)[1] == '/'))
        {
            *input += static_strlen("*/");
            return;
        }
    }
}

static void minify_string(char **input, char **output) {
    (*output)[0] = (*input)[0];
    *input += static_strlen("\"");
    *output += static_strlen("\"");


    for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
        (*output)[0] = (*input)[0];

        if ((*input)[0] == '\"') {
            (*output)[0] = '\"';
            *input += static_strlen("\"");
            *output += static_strlen("\"");
            return;
        } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
            (*output)[1] = (*input)[1];
            *input += static_strlen("\"");
            *output += static_strlen("\"");
        }
    }
}

CJSON_PUBLIC(void) cJSON_Minify(char *json)
{
    char *into = json;

    if (json == NULL)
    {
        return;
    }

    while (json[0] != '\0')
    {
        switch (json[0])
        {
            case ' ':
            case '\t':
            case '\r':
            case '\n':
                json++;
                break;

            case '/':
                if (json[1] == '/')
                {
                    skip_oneline_comment(&json);
                }
                else if (json[1] == '*')
                {
                    skip_multiline_comment(&json);
                } else {
                    json++;
                }
                break;

            case '\"':
                minify_string(&json, (char**)&into);
                break;

            default:
                into[0] = json[0];
                json++;
                into++;
        }
    }

    /* and null-terminate. */
    *into = '\0';
}

CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
{
    if (item == NULL)
    {
        return false;
    }

    return (item->type & 0xFF) == cJSON_Invalid;
}

CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
{
    if (item == NULL)
    {
        return false;
    }

    return (item->type & 0xFF) == cJSON_False;
}

CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
{
    if (item == NULL)
    {
        return false;
    }

    return (item->type & 0xff) == cJSON_True;
}


CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
{
    if (item == NULL)
    {
        return false;
    }

    return (item->type & (cJSON_True | cJSON_False)) != 0;
}
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
{
    if (item == NULL)
    {
        return false;
    }

    return (item->type & 0xFF) == cJSON_NULL;
}

CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
{
    if (item == NULL)
    {
        return false;
    }

    return (item->type & 0xFF) == cJSON_Number;
}

CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
{
    if (item == NULL)
    {
        return false;
    }

    return (item->type & 0xFF) == cJSON_String;
}

CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
{
    if (item == NULL)
    {
        return false;
    }

    return (item->type & 0xFF) == cJSON_Array;
}

CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
{
    if (item == NULL)
    {
        return false;
    }

    return (item->type & 0xFF) == cJSON_Object;
}

CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
{
    if (item == NULL)
    {
        return false;
    }

    return (item->type & 0xFF) == cJSON_Raw;
}

CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
{
    if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
    {
        return false;
    }

    /* check if type is valid */
    switch (a->type & 0xFF)
    {
        case cJSON_False:
        case cJSON_True:
        case cJSON_NULL:
        case cJSON_Number:
        case cJSON_String:
        case cJSON_Raw:
        case cJSON_Array:
        case cJSON_Object:
            break;

        default:
            return false;
    }

    /* identical objects are equal */
    if (a == b)
    {
        return true;
    }

    switch (a->type & 0xFF)
    {
        /* in these cases and equal type is enough */
        case cJSON_False:
        case cJSON_True:
        case cJSON_NULL:
            return true;

        case cJSON_Number:
            if (compare_double(a->valuedouble, b->valuedouble))
            {
                return true;
            }
            return false;

        case cJSON_String:
        case cJSON_Raw:
            if ((a->valuestring == NULL) || (b->valuestring == NULL))
            {
                return false;
            }
            if (strcmp(a->valuestring, b->valuestring) == 0)
            {
                return true;
            }

            return false;

        case cJSON_Array:
        {
            cJSON *a_element = a->child;
            cJSON *b_element = b->child;

            for (; (a_element != NULL) && (b_element != NULL);)
            {
                if (!cJSON_Compare(a_element, b_element, case_sensitive))
                {
                    return false;
                }

                a_element = a_element->next;
                b_element = b_element->next;
            }

            /* one of the arrays is longer than the other */
            if (a_element != b_element) {
                return false;
            }

            return true;
        }

        case cJSON_Object:
        {
            cJSON *a_element = NULL;
            cJSON *b_element = NULL;
            cJSON_ArrayForEach(a_element, a)
            {
                /* TODO This has O(n^2) runtime, which is horrible! */
                b_element = get_object_item(b, a_element->string, case_sensitive);
                if (b_element == NULL)
                {
                    return false;
                }

                if (!cJSON_Compare(a_element, b_element, case_sensitive))
                {
                    return false;
                }
            }

            /* doing this twice, once on a and b to prevent true comparison if a subset of b
             * TODO: Do this the proper way, this is just a fix for now */
            cJSON_ArrayForEach(b_element, b)
            {
                a_element = get_object_item(a, b_element->string, case_sensitive);
                if (a_element == NULL)
                {
                    return false;
                }

                if (!cJSON_Compare(b_element, a_element, case_sensitive))
                {
                    return false;
                }
            }

            return true;
        }

        default:
            return false;
    }
}

CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
{
    return global_hooks.allocate(size);
}

CJSON_PUBLIC(void) cJSON_free(void *object)
{
    global_hooks.deallocate(object);
    object = NULL;
}

cJSON.h

cs 复制代码
/*
  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors

  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

#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__
#endif

#ifdef __WINDOWS__

/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention.  For windows you have 3 define options:

CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol

For *nix builds that support visibility attribute, you can define similar behavior by

setting default visibility to hidden by adding
-fvisibility=hidden (for gcc)
or
-xldscope=hidden (for sun cc)
to CFLAGS

then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does

*/

#define CJSON_CDECL __cdecl
#define CJSON_STDCALL __stdcall

/* export symbols by default, this is necessary for copy pasting the C and header file */
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS
#endif

#if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type)   type CJSON_STDCALL
#elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type)   __declspec(dllexport) type CJSON_STDCALL
#elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type)   __declspec(dllimport) type CJSON_STDCALL
#endif
#else /* !__WINDOWS__ */
#define CJSON_CDECL
#define CJSON_STDCALL

#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type)   __attribute__((visibility("default"))) type
#else
#define CJSON_PUBLIC(type) type
#endif
#endif

/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 18

#include <stddef.h>

/* cJSON Types: */
#define cJSON_Invalid (0)
#define cJSON_False  (1 << 0)
#define cJSON_True   (1 << 1)
#define cJSON_NULL   (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array  (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw    (1 << 7) /* raw json */

#define cJSON_IsReference 256
#define cJSON_StringIsConst 512

/* The cJSON structure: */
typedef struct cJSON
{
    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
    struct cJSON *next;
    struct cJSON *prev;
    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
    struct cJSON *child;

    /* The type of the item, as above. */
    int type;

    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
    char *valuestring;
    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
    int valueint;
    /* The item's number, if type==cJSON_Number */
    double valuedouble;

    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
    char *string;
} cJSON;

typedef struct cJSON_Hooks
{
      /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
      void *(CJSON_CDECL *malloc_fn)(size_t sz);
      void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;

typedef int cJSON_bool;

/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
 * This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000
#endif

/* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void);

/* Supply malloc, realloc and free functions to cJSON */
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);

/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);

/* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const 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 */
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
/* Delete a cJSON entity and all subentities. */
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);

/* Returns the number of items in an array (or object). */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
/* Get item "string" from object. Case insensitive. */
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const 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. */
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);

/* Check item type and return its value */
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);

/* These functions check the type of an item */
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);

/* These calls create a cJSON item of the appropriate type. */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
/* raw json */
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);

/* Create a string where valuestring references a string so
 * it will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
/* Create an object/array that only references it's elements so
 * they will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);

/* These utilities create an Array of count items.
 * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);

/* Append item to the specified array/object. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(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.
 * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
 * writing to `item->string` */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
/* 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. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);

/* Remove/Detach items from Arrays/Objects. */
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);

/* Update array items. */
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);

/* Duplicate a cJSON item */
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool 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. */
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
 * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);

/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
 * The input pointer json cannot point to a read-only address area, such as a string constant, 
 * but should point to a readable and writable address area. */
CJSON_PUBLIC(void) cJSON_Minify(char *json);

/* Helper functions for creating and adding items to an object at the same time.
 * They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);

/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
/* helper for the cJSON_SetNumberValue macro */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);

/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
#define cJSON_SetBoolValue(object, boolValue) ( \
    (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
    (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
    cJSON_Invalid\
)

/* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)

/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object);

#ifdef __cplusplus
}
#endif

#endif

main.c

cs 复制代码
#include "stm32f10x.h"
#include "math.h"
#include <stdio.h>
#include <string.h>
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "timer.h"
#include "jansson.h"
#include "mqtt.h"



uint8_t arr[10] = {0};


#define WIRELESS_SEND_TIME 	3000	//上报数据时间间隔(单位:毫秒)
#define SENSOR_READ_TIME	3000    //获取传感器数据时间间隔(单位:毫秒)
#define KEY_SCAN_TIME		20    	//按键扫描(单位:毫秒)

uint8_t MQTT_Send_Flag = 0;     //上报数据标志位
uint8_t sensor_read_flag = 0;		//读取传感器数据标志位

static uint32_t last_pub_time = 0;
static uint32_t last_check_time = 0;

 const char *json_str = "{\"name\":\"9527\",\"age\":24,\"city\":\"Su Zhou\"}";
 
 // 定义学生信息结构体
 typedef struct {
     char name[10];   // 姓名,最大9字符+1结束符
     uint8_t age;     // 年龄
     char city[10];   // 城市,最大9字符+1结束符
 } Student_TypeDef;

// /**
//  * @brief 解析JSON字符串并填充到结构体
//  * @param jsonString 输入的JSON字符串
//  * @param student 输出参数,用于存储解析结果
//  * @return 0成功,1失败
//  */
// uint8_t myJsonTest(const char *jsonString, Student_TypeDef *student) {
//     // 声明JSON解析相关变量
//     json_t *root;          // JSON根对象
//     json_error_t error;    // 错误信息容器
//     json_t *name_value;    // name字段值对象
//     json_t *age_value;     // age字段值对象
//     json_t *city_value;    // city字段值对象

//     /* 第一步:解析JSON字符串 */
//     root = json_loads(jsonString, 0, &error);
//     if (!root) {
//         // 解析失败时打印错误信息
//         fprintf(stderr, "[ERROR] JSON解析失败 (行%d,列%d): %s\r\n", 
//                 error.line, error.column, error.text);
//         return 1;
//     }

//     /* 第二步:验证JSON类型 */
//     if (!json_is_object(root)) {
//         fprintf(stderr, "[ERROR] JSON格式错误:顶层不是对象\r\n");
//         json_decref(root);  // 释放JSON对象内存
//         return 1;
//     }

//     /* 第三步:提取name字段 */
//     name_value = json_object_get(root, "name");
//     if (json_is_string(name_value)) {
//         // 安全拷贝字符串(防止缓冲区溢出)
//         strncpy(student->name, json_string_value(name_value), sizeof(student->name) - 1);
//         student->name[sizeof(student->name) - 1] = '\0';  // 确保字符串终止
//     } else {
//         printf("[WARNING] name字段缺失或类型错误\r\n");
//         memset(student->name, 0, sizeof(student->name));  // 清空name字段
//     }

//     /* 第四步:提取age字段 */
//     age_value = json_object_get(root, "age");
//     if (json_is_integer(age_value)) {
//         int age = json_integer_value(age_value);
//         // 检查年龄范围是否合理
//         if (age >= 0 && age <= UINT8_MAX) {
//             student->age = (uint8_t)age;
//         } else {
//             printf("[WARNING] 年龄值%d超出范围\r\n", age);
//             student->age = 0;  // 设为默认值
//         }
//     } else {
//         printf("[WARNING] age字段缺失或类型错误\r\n");
//         student->age = 0;  // 设为默认值
//     }

//     /* 第五步:提取city字段 */
//     city_value = json_object_get(root, "city");
//     if (json_is_string(city_value)) {
//         // 安全拷贝字符串
//         strncpy(student->city, json_string_value(city_value), sizeof(student->city) - 1);
//         student->city[sizeof(student->city) - 1] = '\0';
//     } else {
//         printf("[WARNING] city字段缺失或类型错误\r\n");
//         memset(student->city, 0, sizeof(student->city));
//     }

//     /* 第六步:释放资源 */
//     json_decref(root);  // 减少引用计数(当计数为0时自动释放内存)

//     return 0;  // 解析成功
// }



int main(void)
{
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	delay_init();
	SysTick_Init();
	uart_init(115200);
	uart2_init(9600);

//	timer2_init(100, 720);          //定时器二定时中断初始化,用来定时执行任务
	
	MQTT_INIT();
	
	

//	
	while (1)
	{
		
//		 //  处理接收数据(来自无线模块的MQTT消息)
//        if (MQTT_Get_Receive_Flag() == 1) {
//            MQTT_Receive_data_Handler();  // 解析属性设置、状态反馈等消息
//        }

//        //  定时上报数据(每5秒一次)

//        if (GetTick() - last_pub_time >= 5000) {
//            last_pub_time = GetTick();
//            MQTT_ONENET_Data_Handler();  // 更新传感器数据到属性结构体
//            MQTT_Publish_Data();         // 发送数据到OneNET平台
//            printf("数据已上报,等待下一次上报...\r\n");
//        }

        //  异常监测与重连

        if (GetTick() - last_check_time >= 30000) {  // 每30秒检查一次连接状态
            last_check_time = GetTick();
            
            // 检查WiFi连接状态
            if (strstr((char*)MQTT_RxBuffer, "WIFI DISCONNECT") != NULL) {
                printf("WiFi连接断开,尝试重连...\r\n");
                if (WiFi_Init()) {  // 重新初始化WiFi
                    printf("WiFi重连成功,正在恢复MQTT连接...\r\n");
                    MQTT_Config();   // 重连MQTT
                }
            }

            // 检查MQTT连接状态
            if (strstr((char*)MQTT_RxBuffer, "+MQTTDISCONNECTED:0") != NULL) {
                printf("MQTT连接断开,尝试重连...\r\n");
                MQTT_Config();  // 重新配置MQTT连接
            }
        }

        //  错误信息打印
        MQTT_System_Handler();
		
		
	
////			
//		Student_TypeDef student = {{0}, 0, {0}};
//		printf("\r\n%s\r\n",json_str);
//		myJsonTest(json_str, &student);
//		printf("name:%s\r\n",student.name);
//		printf("age:%d\r\n",student.age);
//		printf("city:%s\r\n",student.city);
//		delay_ms(5000);  

	}
}	




/**  
  * @简要  定时器2中断服务函数
  * @参数  	1ms
  * @注意	定时设置标志位 ,
  * @返回值 无  
  */
void TIM2_IRQHandler(void)
{
	static uint16_t  MQTT_Send_Time = 0;
	static uint16_t sensor_read_timer = 0;
	static uint16_t key_scan_timer = 0;
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		if(MQTT_Send_Time++ == WIRELESS_SEND_TIME) {MQTT_Send_Time = 0;MQTT_Send_Flag = 1;}
		if(sensor_read_timer++ == SENSOR_READ_TIME) {sensor_read_timer = 0;sensor_read_flag = 1;}
		if(key_scan_timer++ == KEY_SCAN_TIME){key_scan_timer = 0; /*key->callback();*/}
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
相关推荐
TDengine (老段)3 小时前
TDengine 选择函数 Max() 用户手册
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
玉~你还好吗3 小时前
【嵌入式电机控制#进阶10】参数辨识(一):电阻辨识
单片机·嵌入式硬件
猫头虎3 小时前
2025最新超详细FreeRTOS入门教程:第八章 FreeRTOS任务通知
stm32·单片机·嵌入式硬件·物联网·硬件架构·51单片机·硬件工程
学生董格3 小时前
[嵌入式embed]Keil5烧录后STM32不自动运行,复位才能运行
stm32·单片机·嵌入式硬件
中科岩创5 小时前
某排水涵洞结构安全自动化监测
人工智能·物联网·自动化
蜀黍@猿6 小时前
【GD32】存储器架构介绍
单片机·mcu
伴杯猫15 小时前
【ESP32-IDF】基础外设开发2:系统中断矩阵
c语言·单片机·嵌入式硬件·mcu·物联网·github
茯苓gao16 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
点灯小铭17 小时前
基于STM32单片机的智能粮仓温湿度检测蓝牙手机APP设计
stm32·单片机·智能手机·毕业设计·课程设计