OneNET EDP协议讲解与应用

前言 

        EDP协议是OneNET自主定制的协议,应用在即需要设备上传数据点到平台,又需要下发命令到设备的协议,具体关于OneNET的EDP协议的解释,请看OneNET EDP协议概述,对于协议的具体讲解,请看设备终端接入协议-EDP,SDK请看EDP-SDK,工具请看EDP调试工具,工具使用可参考博客OneNET云平台-EDP协议数据传输

        本文简单讲解使用EDP协议的关键,对于EDP使用大概步骤为:连接平台-->连接设备-->发送心跳保持设备在线-->上报数据流到设备-->平台下发命令到设备-->设备处理命令并执行对应操作。本文用socket的方式连接、上发、接收平台来讲解EDP的应用,贴出关键代码,代码除socket部分,连接设备、上报数据、发送心跳以及命令下发处理有些是参考OneNET工程师——张继瑞的代码修改的,代码可能存在bug,请读者自行辨别,仅作为参考。本文不讲解数据转发,对EPD协议的数据转发有兴趣的读者请自行研究。

        如果你是首次使用OneNET,不知道如何创建产品和设备等,可参考我的另外一篇博客OneNET MQTT的简单使用,前面有讲解,也可以直接到OneNET平台开发文档查看资料,都非常详细。

 一、socket函数

1、创建socket

/**************************************************************
函数名称 : socket_create
函数功能 : socket创建
输入参数 : 无
返回值  	 : socket_id
备注		 : 无
**************************************************************/
int socket_create(void)
{
	int socket_id = -1;
    unsigned char domain = AF_INET;
    unsigned char type = SOCK_STREAM;
    unsigned char protocol = IPPROTO_IP;
    struct timeval send_timeout = {0};
	
	socket_id = socket(domain, type, protocol);

	if(socket_id < 0)
	{
		SOC_COMMON_LOG("socket create failed!!!, socket_id:%d", socket_id);
		return socket_id;
	}
	else
	{
		SOC_COMMON_LOG("socket create success!!!, socket_id:%d", socket_id);
		send_timeout.tv_sec = 120;
		send_timeout.tv_usec = 0;
		lwip_setsockopt(socket_id, SOL_SOCKET, SO_SNDTIMEO, &send_timeout, sizeof(send_timeout));
		return socket_id;
	}
}

2、socket 连接远程服务器

/**************************************************************
函数名称 : socket_connect_service
函数功能 : socket 连接远程服务器
输入参数 : socket_id:创建socket时返回的id
		   remote_addr:远程服务器地址
		   remote_port:端口
返回值  	 : CONNECT_OK:连接成功,CONNECT_ERROR:连接失败
备注		 : 无
**************************************************************/
socket_connect_t socket_connect_service(int socket_id, char *remote_addr, unsigned int remote_port)
{
	socket_connect_t connect_result = CONNECT_ERROR;
	
	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = lwip_htons(remote_port);
	addr.sin_addr.s_addr = inet_addr(remote_addr);
	
	if(0 == connect(socket_id, (struct sockaddr *)&addr, sizeof(addr)))
	{	
		SOC_COMMON_LOG("socket connect success!!!");
		connect_result = CONNECT_OK;
	}
	else
	{
		SOC_COMMON_LOG("socket connect failed!!!");
		connect_result = CONNECT_ERROR;
	}
	return connect_result;
}

3、socket发送数据

/**************************************************************
函数名称 : socket_send
函数功能 : socket 发送数据
输入参数 : socket_id:创建socket时返回的id
		   data_buf:数据包
		   data_len:数据包大小
返回值  	 : 发送成功返回数据包大小
备注		 : 无
**************************************************************/
int socket_send(int socket_id, char *data_buf, int data_len)
{
	int send_rec  = 0;

	send_rec = send(socket_id, data_buf, data_len, 0);

	return send_rec;
}

4、socket接收数据

/**************************************************************
函数名称 : socket_receive
函数功能 : socket 接收服务器下发的数据
输入参数 : socket_id:创建socket时返回的id
	       data_buf:存储接收到的数据区
	       data_len:最大接收大小
返回值  	 : 成功时返回大于0
备注		 : 无
**************************************************************/
int socket_receive(int socket_id, char *data_buf, int data_len)
{
	int recv_result = 0;

	recv_result = recv(socket_id, data_buf, data_len, MSG_DONTWAIT);

	return recv_result;
}

5、关闭socket连接

/**************************************************************
函数名称 : socket_close
函数功能 : 关闭socket
输入参数 : socket_id:创建socket时返回的id
返回值  	 : CLOSE_OK:关闭成功,CLOSE_ERROR:关闭失败
备注		 : 无
**************************************************************/
socket_close_t socket_close(int socket_id)
{
	socket_close_t close_result = CLOSE_ERROR;

	if(0 == close(socket_id))
	{
		SOC_COMMON_LOG("socket close success!!!");
		close_result = CLOSE_OK;
	}
	else
	{
		SOC_COMMON_LOG("socket close failed!!!");
		close_result = CLOSE_ERROR;
	}
	return close_result;
}

 

typedef enum
{
	CONNECT_OK = 0,
	CONNECT_ERROR = 1
}socket_connect_t;

typedef enum
{	
	CLOSE_OK = 0,
	CLOSE_ERROR = 1
}socket_close_t;

 

二、EDP协议讲解及应用

1、连接请求 

(1)连接OneNET平台,EDP协议对应的服务器地址为:"183.230.40.39",端口为876。

char *g_edp_ip_addr = "183.230.40.39";
int g_edp_ip_port = 876;

使用socket连接:

/* socket id */
int g_onenet_socket_id = -1;

/**************************************************************
函数名称 : onenet_edp_service_connect
函数功能 : edp 服务器连接
输入参数 : ip_addr:ip地址,ip_port:端口
返回值  	 : 0:连接成功,1:连接失败
备注		 : 无
**************************************************************/
unsigned char onenet_edp_service_connect(char *ip_addr, unsigned int ip_port)
{
	g_onenet_socket_id = socket_create();
	
	if(g_onenet_socket_id < 0)
	{
		ONENET_EDP_LOG("oconnect_onenet_service failed, g_onenet_socket_id < 0");
		return 1;
	}

	if(CONNECT_ERROR == socket_connect_service(g_onenet_socket_id, ip_addr, ip_port))
	{
		ONENET_EDP_LOG("connect_onenet_service failed, connect error");
		return 1;
	}
	else
	{
		ONENET_EDP_LOG("connect_onenet_service success");
		return 0;
	}
	
}

socket连接服务器: 

onenet_edp_service_connect(g_edp_ip_addr, g_edp_ip_port);

(2)连接设备,连接设备的方式有两种:

代码:

char *g_edp_device_id = "504890772";	/* 设备id */
char *g_edp_api_key = "cWPICK6PDU6cOHP=T0SqMcXWRc4=";/*api key*/
char *g_device_auth_info = "edp20181122";	/* 设备鉴权信息 */
char *g_edp_prd_id = "190254";			/* 产品id */

/**************************************************************
函数名称 : onenet_edp_device_link
函数功能 : edp 设备连接
输入参数 : id:设备/产品id,auth_key:apikey
返回值  	 : 0;成功,1:失败
备注		 : 无
**************************************************************/
unsigned char onenet_edp_device_link(const char* id, const char* auth_key)
{
	
	EDP_PACKET_STRUCTURE edpPacket = {NULL, 0, 0, 0};				//协议包

	unsigned char time_out = 200;
	
	ONENET_EDP_LOG("id: %s, api_key: %s\r\n", id, auth_key);

#if 1
	if(EDP_PacketConnect1(id, auth_key, 256, &edpPacket) == 0)	//根据devid 和 apikey封装协议包
#else
	if(EDP_PacketConnect2(id, auth_key, 256, &edpPacket) == 0)	//根据产品id 和 鉴权信息封装协议包
#endif
	{
		socket_send(g_onenet_socket_id, edpPacket._data, edpPacket._len);//上传平台
		vTaskDelay(1);
		EDP_DeleteBuffer(&edpPacket);								//删包
		return 0;
	}
	else
	{
		ONENET_EDP_LOGW("EDP_PacketConnect failed, onenet_edp_device_link failed\r\n");
		return 1;
	}
	
}

连接设备:

onenet_edp_device_link(g_edp_device_id, g_edp_api_key);

 连接设备成功之后,可以在页面看到设备在线状态:

2、心跳保持

        EDP连接默认超时时间为4分钟。设备登录后,在超时期内无数据传输时,需要定期向平台发送PING_REQ消息以保持连接,在这我使用FreeRTOS的软件定时每隔3min向平台发送一次心跳。

 发送心跳代码:

/* 任务句柄 */
TimerHandle_t g_onenet_edp_send_heart_sw_timer_handle = NULL;


/**************************************************************
函数名称 : onenet_edp_send_heart
函数功能 : 发送心跳
输入参数 : 无
返回值  	 : 无
备注	: EDP连接默认超时时间为4分钟。设备登录后,
		   在超时期内无数据传输时,
		   需要定期向平台发送PING_REQ消息以保持连接
**************************************************************/
void onenet_edp_send_heart(void)
{
	EDP_PACKET_STRUCTURE edpPacket = {NULL, 0, 0, 0};	//协议包
	EDP_PacketPing(&edpPacket);
	socket_send(g_onenet_socket_id, edpPacket._data, edpPacket._len);//向平台上传心跳请求
	ONENET_EDP_LOG("onenet_edp_send_heart");
	EDP_DeleteBuffer(&edpPacket);	//删包
}

/**************************************************************
函数名称 : onenet_edp_send_heart_sw_timer_callback
函数功能 : 用FreeRTOS软件定时器定时发送心跳
输入参数 : xTimer:软件定时器任务句柄
返回值  	: 无
备注	:无
**************************************************************/
void onenet_edp_send_heart_sw_timer_callback(TimerHandle_t xTimer)
{
	onenet_edp_send_heart();//发送心跳
}

/**************************************************************
函数名称 : onenet_edp_send_heart_sw_timer_task_init
函数功能 : 创建发送心跳软件定时器任务
输入参数 : 无
返回值  	: 无
备注    : 无
**************************************************************/
void onenet_edp_send_heart_sw_timer_task_init(void)
{
	g_onenet_edp_send_heart_sw_timer_handle = xTimerCreate("onenet_edp_send_heart_sw_timer_task",
                                     1800 * COAP_MAX_TRANSMIT_WAIT / portTICK_PERIOD_MS, 		/* 180s(3 min)定时,软件定时器误差大 */
                                      pdTRUE,
                                      NULL,
                                      onenet_edp_send_heart_sw_timer_callback);
}

在连接上设备之后,就开启软件定时器:

if(0 == onenet_edp_device_link(g_edp_device_id, g_edp_api_key))
{
	xTimerStart(g_onenet_edp_send_heart_sw_timer_handle, 0);/* 连接设备成功,开始发心跳 */
}

3、数据上发到平台

 在这里,作为测试,我使用FreeRTOS的软件定时器,定时向平台上传数据流。

上传数据流代码:

/---------------------------------------------------------------------------------------/
typedef enum
{
	TYPE_BOOL = 0,
	TYPE_CHAR,
	TYPE_UCHAR,
	TYPE_SHORT,
	TYPE_USHORT,
	TYPE_INT,
	TYPE_UINT,
	TYPE_LONG,
	TYPE_ULONG,
	TYPE_FLOAT,
	TYPE_DOUBLE,
	TYPE_GPS,
	TYPE_STRING,
} DATA_TYPE;

typedef struct
{
	char *name;
	void *dataPoint;
	DATA_TYPE dataType;
	bool flag;

} DATA_STREAM;

typedef enum
{
	FORMAT_TYPE1 = 1,
	FORMAT_TYPE2,
	FORMAT_TYPE3,
	FORMAT_TYPE4,
	FORMAT_TYPE5

} FORMAT_TYPE;

/---------------------------------------------------------------------------------------/

/* 任务句柄 */
TimerHandle_t g_onenet_edp_send_data_sw_timer_handle = NULL;

//数据流
float g_temperature = 23.5;
DATA_STREAM data_stream[] = {
								{"temperature", &g_temperature, TYPE_FLOAT, 1},
								
							};
unsigned char data_stream_cnt = sizeof(data_stream) / sizeof(data_stream[0]);/* 数据流个数 */

/**************************************************************
函数名称 : oennet_edp_send_data
函数功能 : 上传数据到平台设备
输入参数 : type:发送数据的格式
		   devid:设备ID
		   apikey:设备apikey
		   streamArray:数据流
		   streamArrayNum:数据流个数
返回值  	 : 无
备注		 : 无
**************************************************************/
void oennet_edp_send_data(FORMAT_TYPE type, char *devid, char *apikey, DATA_STREAM *streamArray, unsigned short streamArrayCnt)
{
	EDP_PACKET_STRUCTURE edpPacket = {NULL, 0, 0, 0};											//协议包
	short body_len = 0;

	ONENET_EDP_LOG("oennet_edp_send_data, type:%d\r\n", type);
	
	if(type != kTypeBin)																		//二进制文件吧全部工作做好,不需要执行这些
	{
		body_len = DSTREAM_GetDataStream_Body_Measure(type, streamArray, streamArrayCnt, 0);	//获取当前需要发送的数据流的总长度
		
		if(body_len > 0)
		{
			if(EDP_PacketSaveData(devid, body_len, NULL, (SaveDataType)type, &edpPacket) == 0)	//封包
			{
				body_len = DSTREAM_GetDataStream_Body(type, streamArray, streamArrayCnt, edpPacket._data, edpPacket._size, edpPacket._len);
				
				if(body_len > 0)
				{
					edpPacket._len += body_len;
					
					ONENET_EDP_LOG("Send %d Bytes\r\n", edpPacket._len);
					socket_send(g_onenet_socket_id, edpPacket._data, edpPacket._len);
				}
				else
				{
					ONENET_EDP_LOGW("DSTREAM_GetDataStream_Body Failed\r\n");
				}
				EDP_DeleteBuffer(&edpPacket);	//删包
			}
			else
			{
				ONENET_EDP_LOG("EDP_NewBuffer Failed\r\n");
			}
		}	
	}
	
}

/**************************************************************
函数名称 : onenet_edp_send_data_sw_timer_callback
函数功能 : 用FreeRTOS软件定时器定时上发数据的回掉函数
输入参数 : xTimer:软件定时器任务句柄
返回值  	 : 无
备注		 :
**************************************************************/
void onenet_edp_send_data_sw_timer_callback(TimerHandle_t xTimer)
{
	oennet_edp_send_data(FORMAT_TYPE3, g_edp_device_id, g_edp_api_key, data_stream, data_stream_cnt);//上传数据到平台
}

/**************************************************************
函数名称 : onenet_edp_send_data_sw_timer_task_init
函数功能 : 创建软件定时器任务
输入参数 : 无
返回值  	 : 无
备注		 : 无
**************************************************************/
void onenet_edp_send_data_sw_timer_task_init(void)
{
	g_onenet_edp_send_data_sw_timer_handle = xTimerCreate("onenet_edp_send_data_sw_timer_task",
                                      200 * COAP_MAX_TRANSMIT_WAIT / portTICK_PERIOD_MS, 		/* 20s定时,软件定时器误差大 */
                                      pdTRUE,
                                      NULL,
                                      onenet_edp_send_data_sw_timer_callback);
}

打开上传数据流软件定时器开始定时上传数据流:

xTimerStart(g_onenet_edp_send_data_sw_timer_handle, 0);

如果不想上传数据流,则关闭软件定时器:

xTimerStop(g_onenet_edp_send_data_sw_timer_handle, 0);

上传成功后,可在设备查看到对应的数据:

 

4、接收平台下发命令

        对应EPD协议,下发命令应该最多的便是在开关值了,对于开关命令,为了区分不同开关,OneNET使用命令构体+命令值的方式来区分,格式为:命令结构+冒号+命令值。

        下面我用一个开关来控制LED为例讲解,当点击ON按钮时,下发命令LED:1,当点击OFF按钮时,下发命令LED:0。在接收到开关值之后,将开关值上传到平台。

(1)创建应用

在应用在我们创建一个开关按钮,并与设备和数据流关联

在EDP命令内容,根据命令格式命令结构+冒号+命令值,填写为LED:{V},其中{V}为开关值,LED为命令结构体。

命令下发及其处理代码:

/* led状态 */
int g_led_status = 0;
DATA_STREAM led_status_data_stream[] = {
											{"LED_STATUS", &g_led_status, TYPE_INT, 1},
									   };
unsigned char led_status_data_stream_cnt = sizeof(led_status_data_stream) / sizeof(led_status_data_stream[0]);


/**************************************************************
函数名称 : onenet_edp_recv_cmd_pro
函数功能 : edp命令下发处理
输入参数 : cmd:命令值
返回值  	 : 无
备注		 :对于开关命令,为了区分,onenet采用命令结构体+命令值
		  的方式,格式为:命令结构+冒号+命令值,如:LED:1
**************************************************************/
void onenet_edp_recv_cmd_pro(unsigned char *cmd)
{
	
	EDP_PACKET_STRUCTURE edpPacket = {NULL, 0, 0, 0};//协议包
	signed char *cmdid_devid = NULL;
	unsigned short cmdid_len = 0;
	signed char *req = NULL;
	unsigned int req_len = 0;
	
	switch(EDP_UnPacketRecv(cmd))
	{
		case CONNRESP:
		{
			switch(EDP_UnPacketConnectRsp(cmd))
			{
				case 0:ONENET_EDP_LOG("Tips:	连接成功\r\n");break;
				case 1:ONENET_EDP_LOG("WARN:	连接失败:协议错误\r\n");break;
				case 2:ONENET_EDP_LOG("WARN:	连接失败:设备ID鉴权失败\r\n");break;
				case 3:ONENET_EDP_LOG("WARN:	连接失败:服务器失败\r\n");break;
				case 4:ONENET_EDP_LOG("WARN:	连接失败:用户ID鉴权失败\r\n");break;
				case 5:ONENET_EDP_LOG("WARN:	连接失败:未授权\r\n");break;
				case 6:ONENET_EDP_LOG("WARN:	连接失败:授权码无效\r\n");break;
				case 7:ONENET_EDP_LOG("WARN:	连接失败:激活码未分配\r\n");break;
				case 8:ONENET_EDP_LOG("WARN:	连接失败:该设备已被激活\r\n");break;
				case 9:ONENET_EDP_LOG("WARN:	连接失败:重复发送连接请求包\r\n");break;
				default:ONENET_EDP_LOG("ERR:	连接失败:未知错误\r\n");break;
			}
			break;
		}
		case DISCONNECT:
		{
			ONENET_EDP_LOG("WARN:	连接断开,准备重连。错误码:%d\r\n", cmd[2]);
			break;
		}
		case PINGRESP:
		{
			ONENET_EDP_LOG("Tips:	HeartBeat OK\r\n");
			break;
		}
		case PUSHDATA:	//解pushdata包
		{
			if(EDP_UnPacketPushData(cmd, &cmdid_devid, &req, &req_len) == 0)
			{
				ONENET_EDP_LOG("src_devid: %s, req: %s, req_len: %d\r\n", cmdid_devid, req, req_len);
				
				//执行命令回调------------------------------------------------------------
				
				edp_free_buffer(cmdid_devid);	//释放内存
				edp_free_buffer(req);
			}
			break;
		}
		case CMDREQ:	//解命令包
		{
			if(EDP_UnPacketCmd(cmd, &cmdid_devid, &cmdid_len, &req, &req_len) == 0)
			{
				//命令回复组包------------------------------------------------------------
				EDP_PacketCmdResp(cmdid_devid, cmdid_len, req, req_len, &edpPacket);
				ONENET_EDP_LOG("cmdid: %s, req: %s, req_len: %d\r\n", cmdid_devid, req, req_len);
				
				//执行命令回调------------------------------------------------------------

				if(strcmp("LED:1", req) == 0)
				{
					g_led_status = 1;
				}
				else if(strcmp("LED:0", req) == 0)
				{
					g_led_status = 0;
				}
				//上传LED状态数据到平台
				oennet_edp_send_data(FORMAT_TYPE3, g_edp_device_id, g_edp_api_key, led_status_data_stream, led_status_data_stream_cnt);
				
				edp_free_buffer(cmdid_devid);		//释放内存
				edp_free_buffer(req);
				
				//回复命令---------------------------------------------------------------
				socket_send(g_onenet_socket_id, edpPacket._data, edpPacket._len);//上传平台
				EDP_DeleteBuffer(&edpPacket);								//删包
			}
			break;
		}	
		case SAVEACK:
		{
			if(cmd[3] == MSG_ID_HIGH && cmd[4] == MSG_ID_LOW)
			{
				
				ONENET_EDP_LOG("Tips:	Send %s\r\n", cmd[5] ? "Err" : "Ok");
			}
			else
			{
				ONENET_EDP_LOG("Tips:	Message ID Err\r\n");
			}
			break;
		}
		default:break;
	}

}

/* 最大接收onenet下发命令大小 */
#define ONENET_EDP_RECV_MAX_SIZE	256

/**************************************************************
函数名称 : onenet_edp_send_data_sw_timer_task_init
函数功能 : 接收onenet下发命令任务函数
输入参数 : pvParameter:任务入口参数
返回值  	 : 无
备注		 : 无
**************************************************************/
void onenet_edp_receive_cmd_task(void *pvParameter)
{
	unsigned char data_ptr[ONENET_EDP_RECV_MAX_SIZE];
		
	memset(data_ptr, 0, sizeof(data_ptr));
	
	while(1)
	{
		if(socket_receive(g_onenet_socket_id, data_ptr, ONENET_EDP_RECV_MAX_SIZE) > 0)	//使用MSG_DONTWAIT会比较稳定
		{
			onenet_edp_recv_cmd_pro(data_ptr);	//集中处理
		}	
		vTaskDelay(1);	//挂起任务10ms
	}

}

/**************************************************************
函数名称 : onenet_edp_receive_cmd_task_init
函数功能 : 创建接收onenet下发命令任务
输入参数 : 无
返回值  	 : 无
备注		 : 无
**************************************************************/
void onenet_edp_receive_cmd_task_init(void)
{
	if(g_onenet_edp_recv_cmd_task_handle == NULL) {
            xTaskCreate(onenet_edp_receive_cmd_task,
                            "onenet_edp_receive_cmd_task",
                            1024 * 4 / sizeof(portSTACK_TYPE),
                            (void*)1,
                            TASK_PRIORITY_NORMAL,
                            &g_onenet_edp_recv_cmd_task_handle);
            ONENET_EDP_LOG("onenet_edp_receive_cmd_task");
        }

}

当在应用点击ON/OFF按钮时,命令开始下发,通过串口打印抓取到的下发命令信息如下:

cmdid: 53fb996e-1d76-54e3-afe5-ae77079692a9, req: LED:1, req_len: 5
cmdid: 07ba5b64-a672-58ec-9bd7-9d326c38936c, req: LED:0, req_len: 5

得到命令,执行相应的操作,将开关值上传到平台:

if(strcmp("LED:1", req) == 0)
{
    g_led_status = 1;
}
else if(strcmp("LED:0", req) == 0)
{
    g_led_status = 0;
}
//上传LED状态数据到平台
oennet_edp_send_data(FORMAT_TYPE3, g_edp_device_id, g_edp_api_key, led_status_data_stream, led_status_data_stream_cnt);

上传成功,可在设备查看到对应的数据流:

 

三、贴出EDP协议代码

以下代码作者是OneNET工程师——张继瑞,本人只是根据自己的使用的平台修改了内存申请和释放函数。

1、edpkit.c

#include "FreeRTOS.h"
#include "string.h"
#include "edpkit.h"

#if 0
void* edp_alloc_buffer(int buffer_size)
{
    return pvPortCalloc(1, buffer_size);
}

void edp_free_buffer(void* buffer)
{
    if (buffer) {
        vPortFree(buffer);
        buffer = NULL;
    }
}
#endif
//==========================================================
//	函数名称:	EDP_NewBuffer
//
//	函数功能:	申请内存
//
//	入口参数:	edpPacket:包结构体
//				size:大小
//
//	返回参数:	无
//
//	说明:		1.可使用动态分配来分配内存
//				2.可使用局部或全局数组来指定内存
//==========================================================
void EDP_NewBuffer(EDP_PACKET_STRUCTURE *edpPacket, uint32_t size)
{
	
	uint32_t i = 0;

	if(edpPacket->_data == NULL)
	{
		edpPacket->_memFlag = MEM_FLAG_ALLOC;
		
		edpPacket->_data = (char *)edp_alloc_buffer(size);
		if(edpPacket->_data != NULL)
		{
			edpPacket->_len = 0;
			
			edpPacket->_size = size;

			for(; i < edpPacket->_size; i++)
				edpPacket->_data[i] = 0;
		}
	}
	else
	{
		edpPacket->_memFlag = MEM_FLAG_STATIC;
		
		for(; i < edpPacket->_size; i++)
			edpPacket->_data[i] = 0;
		
		edpPacket->_len = 0;
		
		if(edpPacket->_size < size)
			edpPacket->_data = NULL;
	}

}

//==========================================================
//	函数名称:	EDP_DeleteBuffer
//
//	函数功能:	释放数据内存
//
//	入口参数:	edpPacket:包结构体
//
//	返回参数:	无
//
//	说明:		当使用的局部或全局数组时不释放内存
//==========================================================
void EDP_DeleteBuffer(EDP_PACKET_STRUCTURE *edpPacket)
{

	if(edpPacket->_memFlag == MEM_FLAG_ALLOC)
		edp_free_buffer(edpPacket->_data);
	
	edpPacket->_data = NULL;
	edpPacket->_len = 0;
	edpPacket->_size = 0;
	edpPacket->_memFlag = MEM_FLAG_NULL;

}

//==========================================================
//	函数名称:	EDP_UnPacketRecv
//
//	函数功能:	EDP数据接收类型判断
//
//	入口参数:	dataPtr:接收的数据指针
//
//	返回参数:	0-成功		其他-失败原因
//
//	说明:		
//==========================================================
uint8_t EDP_UnPacketRecv(uint8_t *dataPtr)

{
	
	return dataPtr[0];

}

//==========================================================
//	函数名称:	EDP_PacketConnect1
//
//	函数功能:	登录方式1组包
//
//	入口参数:	devid:设备ID
//				apikey:APIKEY
//				cTime:连接保持时间
//				edpPacket:包指针
//
//	返回参数:	0-成功		1-失败
//
//	说明:		
//==========================================================
bool EDP_PacketConnect1(const int8_t *devid, const int8_t *apikey, uint16_t cTime, EDP_PACKET_STRUCTURE *edpPacket)
{
	
	uint8_t devid_len = strlen(devid);
	uint8_t apikey_len = strlen(apikey);

	//分配内存---------------------------------------------------------------------
	EDP_NewBuffer(edpPacket, 56);
	if(edpPacket->_data == NULL)
		return 1;
	
	//Byte0:连接类型--------------------------------------------------------------
	edpPacket->_data[0] = CONNREQ;
	edpPacket->_len++;
	
	//Byte1:剩余消息长度----------------------------------------------------------
	edpPacket->_data[1] = 13 + devid_len + apikey_len;
	edpPacket->_len++;
	
	//Byte2~3:协议名长度----------------------------------------------------------
	edpPacket->_data[2] = 0;
	edpPacket->_data[3] = 3;
	edpPacket->_len += 2;
	
	//Byte4~6:协议名--------------------------------------------------------------
	strncat((char *)edpPacket->_data + 4, "EDP", 3);
	edpPacket->_len += 3;
	
	//Byte7:协议版本--------------------------------------------------------------
	edpPacket->_data[7] = 1;
	edpPacket->_len++;
	
	//Byte8:连接标志--------------------------------------------------------------
	edpPacket->_data[8] = 0x40;
	edpPacket->_len++;
	
	//Byte9~10:连接保持时间-------------------------------------------------------
	edpPacket->_data[9] = MOSQ_MSB(cTime);
	edpPacket->_data[10] = MOSQ_LSB(cTime);
	edpPacket->_len += 2;
	
	//Byte11~12:DEVID长度---------------------------------------------------------
	edpPacket->_data[11] = MOSQ_MSB(devid_len);
	edpPacket->_data[12] = MOSQ_LSB(devid_len);
	edpPacket->_len += 2;
	
	//Byte13~13+devid_len:DEVID---------------------------------------------------
	strncat((char *)edpPacket->_data + 13, devid, devid_len);
	edpPacket->_len += devid_len;
	
	//Byte13+devid_len~13+devid_len+2:APIKEY长度----------------------------------
	edpPacket->_data[13 + devid_len] = MOSQ_MSB(apikey_len);
	edpPacket->_data[14 + devid_len] = MOSQ_LSB(apikey_len);
	edpPacket->_len += 2;
	
	//Byte15+devid_len~15+devid_len+apikey_len:APIKEY-----------------------------
	strncat((char *)edpPacket->_data + 15 + devid_len, apikey, apikey_len);
	edpPacket->_len += apikey_len;
	
	return 0;

}

//==========================================================
//	函数名称:	EDP_PacketConnect2
//
//	函数功能:	登录方式2组包
//
//	入口参数:	devid:设备ID
//				auth_key:鉴权信息
//				cTime:连接保持时间
//				edpPacket:包指针
//
//	返回参数:	0-成功		1-失败
//
//	说明:		
//==========================================================
bool EDP_PacketConnect2(const int8_t *proid, const int8_t *auth_key, uint16_t cTime, EDP_PACKET_STRUCTURE *edpPacket)
{
	
	uint8_t proid_len = strlen(proid);
	uint8_t authkey_len = strlen(auth_key);

	//分配内存---------------------------------------------------------------------
	EDP_NewBuffer(edpPacket, 56);
	if(edpPacket->_data == NULL)
		return 1;
	
	//Byte0:连接类型--------------------------------------------------------------
	edpPacket->_data[0] = CONNREQ;
	edpPacket->_len++;
	
	//Byte1:剩余消息长度----------------------------------------------------------
	edpPacket->_data[1] = 15 + proid_len + authkey_len;
	edpPacket->_len++;
	
	//Byte2~3:协议名长度----------------------------------------------------------
	edpPacket->_data[2] = 0;
	edpPacket->_data[3] = 3;
	edpPacket->_len += 2;
	
	//Byte4~6:协议名--------------------------------------------------------------
	strncat((char *)edpPacket->_data + 4, "EDP", 3);
	edpPacket->_len += 3;
	
	//Byte7:协议版本--------------------------------------------------------------
	edpPacket->_data[7] = 1;
	edpPacket->_len++;
	
	//Byte8:连接标志--------------------------------------------------------------
	edpPacket->_data[8] = 0xC0;
	edpPacket->_len++;
	
	//Byte9~10:连接保持时间-------------------------------------------------------
	edpPacket->_data[9] = MOSQ_MSB(cTime);
	edpPacket->_data[10] = MOSQ_LSB(cTime);
	edpPacket->_len += 2;
	
	//Byte11~12:DEVID长度---------------------------------------------------------
	edpPacket->_data[11] = 0;
	edpPacket->_data[12] = 0;
	edpPacket->_len += 2;
	
	//Byte13~14:PROID长度---------------------------------------------------------
	edpPacket->_data[13] = MOSQ_MSB(proid_len);
	edpPacket->_data[14] = MOSQ_LSB(proid_len);
	edpPacket->_len += 2;
	
	//Byte15~15+proid_len:RPOID---------------------------------------------------
	strncat((char *)edpPacket->_data + 15, proid, proid_len);
	edpPacket->_len += proid_len;
	
	//Byte15+devid_len~15+proid_len+1:APIKEY长度----------------------------------
	edpPacket->_data[15 + proid_len] = MOSQ_MSB(authkey_len);
	edpPacket->_data[16 + proid_len] = MOSQ_LSB(authkey_len);
	edpPacket->_len += 2;
	
	//Byte17+proid_len~17+proid_len+apikey_len:APIKEY-----------------------------
	strncat((char *)edpPacket->_data + 17 + proid_len, auth_key, authkey_len);
	edpPacket->_len += authkey_len;
	
	return 0;

}

//==========================================================
//	函数名称:	EDP_UnPacketConnectRsp
//
//	函数功能:	连接回复解包
//
//	入口参数:	rev_data:接收到的数据
//
//	返回参数:	登录结果
//
//	说明:		
//==========================================================
uint8_t EDP_UnPacketConnectRsp(uint8_t *rev_data)
{

	//0		连接成功
	//1		验证失败:协议错误
	//2		验证失败:设备ID鉴权失败
	//3		验证失败:服务器失败
	//4		验证失败:用户ID鉴权失败
	//5		验证失败:未授权
	//6		验证失败:授权码无效
	//7		验证失败:激活码未分配
	//8		验证失败:该设备已被激活
	//9		验证失败:重复发送连接请求包
	return rev_data[3];

}

int32_t WriteRemainlen(uint8_t *buf, uint32_t len_val, uint16_t write_pos)

{
	
	int32_t remaining_count = 0;
	uint8_t byte = 0;

	do
	{
		byte = len_val % 128;
		len_val = len_val >> 7;
		/* If there are more digits to encode, set the top bit of this digit */
		if (len_val > 0)
		{
			byte = byte | 0x80;
		}
		buf[write_pos++] = byte;
		remaining_count++;
	} while(len_val > 0 && remaining_count < 5);

	return --write_pos;
}

int32_t ReadRemainlen(int8_t *buf, uint32_t *len_val, uint16_t read_pos)
{
	
    uint32_t multiplier = 1;
    uint32_t len_len = 0;
    uint8_t onebyte = 0;
	
    *len_val = 0;
	
    do
	{
        onebyte = buf[read_pos++];

        *len_val += (onebyte & 0x7f) * multiplier;
        multiplier <<= 7;

        len_len++;
        if (len_len > 4)
		{
            return -1;/*len of len more than 4;*/
        }
    } while((onebyte & 0x80) != 0);
	
    return read_pos;
}

//==========================================================
//	函数名称:	EDP_PacketSaveJson
//
//	函数功能:	封装协议头
//
//	入口参数:	devid:设备ID(可为空)
//				send_buf:json缓存buf
//				send_len:json总长
//				type_bin_head:bin文件的消息头
//				type:类型
//				edpPacket:包指针
//
//	返回参数:	0-成功		1-失败
//
//	说明:		当不为Type2的时候,type_bin_head可为NULL
//==========================================================
uint8_t EDP_PacketSaveData(const int8_t *devid, int16_t send_len, int8_t *type_bin_head, SaveDataType type, EDP_PACKET_STRUCTURE *edpPacket)

{

	int16_t remain_len = 0;
	uint8_t devid_len = 0;

	if(devid != NULL)
		devid_len = strlen(devid);
	
	if(type == 2 && type_bin_head == NULL)
		return 1;

	if(type == 2)
		EDP_NewBuffer(edpPacket, strlen(type_bin_head));
	else
		EDP_NewBuffer(edpPacket, send_len + 24);
	if(edpPacket->_data == NULL)
		return 2;

	//Byte0:消息类型--------------------------------------------------------------
	edpPacket->_data[edpPacket->_len++] = SAVEDATA;

	if(devid)
	{
		if(type == 2)
			remain_len = 12 + strlen(type_bin_head) + send_len;
		else
			remain_len = 8 + send_len + devid_len;
		
		//剩余消息长度-------------------------------------------------------------
		edpPacket->_len += WriteRemainlen(edpPacket->_data, remain_len, edpPacket->_len);
		
		//标志--bit7:1-有devid,0-无devid		bit6:1-有消息编号,0-无消息编号----
		edpPacket->_data[edpPacket->_len++] = 0xC0;
		
		//DEVID长度---------------------------------------------------------------
		edpPacket->_data[edpPacket->_len++] = 0;
		edpPacket->_data[edpPacket->_len++] = devid_len;
		
		//DEVID------------------------------------------------------------------
		strncat((char *)edpPacket->_data + edpPacket->_len, devid, devid_len);
		edpPacket->_len += devid_len;
		
		//消息编号----------------------------------------------------------------
		edpPacket->_data[edpPacket->_len++] = MSG_ID_HIGH;
		edpPacket->_data[edpPacket->_len++] = MSG_ID_LOW;
	}
	else
	{
		if(type == 2)
			remain_len = 10 + strlen(type_bin_head) + send_len;
		else
			remain_len = 6 + send_len;
		
		//剩余消息长度------------------------------------------------------------
		edpPacket->_len += WriteRemainlen(edpPacket->_data, remain_len, edpPacket->_len);
		
		//标志--bit7:1-有devid,0-无devid		bit6:1-有消息编号,0-无消息编号----
		edpPacket->_data[edpPacket->_len++] = 0x40;
		
		//消息编号----------------------------------------------------------------
		edpPacket->_data[edpPacket->_len++] = MSG_ID_HIGH;
		edpPacket->_data[edpPacket->_len++] = MSG_ID_LOW;
	}
	
	edpPacket->_data[edpPacket->_len++] = type;
	
	if(type == 2)
	{
		unsigned char type_bin_head_len = strlen(type_bin_head);
		unsigned char i = 0;
		
		//消息头长度---------------------------------------------------------------
		edpPacket->_data[edpPacket->_len++] = MOSQ_MSB(type_bin_head_len);
		edpPacket->_data[edpPacket->_len++] = MOSQ_LSB(type_bin_head_len);
		
		//消息头-------------------------------------------------------------------
		for(; i < type_bin_head_len; i++)
			edpPacket->_data[edpPacket->_len++] = type_bin_head[i];
		
		//图片长度-----------------------------------------------------------------
		edpPacket->_data[edpPacket->_len++] = (unsigned char)(send_len >> 24);
		edpPacket->_data[edpPacket->_len++] = (unsigned char)(send_len >> 16);
		edpPacket->_data[edpPacket->_len++] = (unsigned char)(send_len >> 8);
		edpPacket->_data[edpPacket->_len++] = (unsigned char)send_len;
	}
	else
	{
		//json长度-----------------------------------------------------------------
		edpPacket->_data[edpPacket->_len++] = MOSQ_MSB(send_len);
		edpPacket->_data[edpPacket->_len++] = MOSQ_LSB(send_len);
	}
	
	return 0;

}

//==========================================================
//	函数名称:	EDP_PacketPushData
//
//	函数功能:	PushData功能组包
//
//	入口参数:	devid:设备ID
//				msg:推送数据
//				msg_len:推送的数据长度
//				edpPacket:包指针
//
//	返回参数:	0-成功		1-失败
//
//	说明:		
//==========================================================
uint8_t EDP_PacketPushData(const int8_t *devid, const int8_t *msg, uint32_t msg_len, EDP_PACKET_STRUCTURE *edpPacket)
{
	
	uint32_t remain_len = 2 + strlen(devid) + msg_len;
	uint8_t devid_len = strlen(devid);
	uint16_t i = 0;
	uint16_t size = 5 + strlen(devid) + msg_len;
	
	if(devid == NULL || msg == NULL || msg_len == 0)
		return 1;

	EDP_NewBuffer(edpPacket, size);
	if(edpPacket->_data == NULL)
		return 2;
	
	//Byte0:pushdata类型-----------------------------------------------------------
	edpPacket->_data[edpPacket->_len++] = PUSHDATA;
	
	//剩余长度----------------------------------------------------------------------
	edpPacket->_len += WriteRemainlen(edpPacket->_data, remain_len, edpPacket->_len);
	
	//DEVID长度---------------------------------------------------------------------
	edpPacket->_data[edpPacket->_len++] = MOSQ_MSB(devid_len);
	edpPacket->_data[edpPacket->_len++] = MOSQ_LSB(devid_len);
	
	//写入DEVID---------------------------------------------------------------------
	for(; i < devid_len; i++)
		edpPacket->_data[edpPacket->_len++] = devid[i];
	
	//写入数据----------------------------------------------------------------------
	for(i = 0; i < msg_len; i++)
		edpPacket->_data[edpPacket->_len++] = msg[i];
	
	return 0;

}

//==========================================================
//	函数名称:	EDP_UnPacketPushData
//
//	函数功能:	PushData功能解包
//
//	入口参数:	rev_data:收到的数据
//				src_devid:源devid缓存
//				req:命令缓存
//				req_len:命令长度
//
//	返回参数:	0-成功		1-失败
//
//	说明:		
//==========================================================
uint8_t EDP_UnPacketPushData(uint8_t *rev_data, int8_t **src_devid, int8_t **req, uint32_t *req_len)
{

	int32_t read_pos = 0;
	uint32_t remain_len = 0;
	uint16_t devid_len = 0;
	
	//Byte0:PushData消息------------------------------------------------------------
	if(rev_data[read_pos++] != PUSHDATA)
		return 1;

	//读取剩余长度--------------------------------------------------------------------
	read_pos = ReadRemainlen((char *)rev_data, &remain_len, read_pos);
	if(read_pos == -1)
		return 2;
	
	//读取源devid长度-----------------------------------------------------------------
	devid_len = (uint16_t)rev_data[read_pos] << 8 | rev_data[read_pos + 1];
	read_pos += 2;

	//分配内存------------------------------------------------------------------------
	*src_devid = (char *)edp_alloc_buffer(devid_len + 1);
	if(*src_devid == NULL)
		return 3;

	//读取源devid---------------------------------------------------------------------
	memset(*src_devid, 0, devid_len + 1);
	strncpy(*src_devid, (const char *)rev_data + read_pos, devid_len);
	read_pos += devid_len;

	remain_len -= 2 + devid_len;

	//分配内存------------------------------------------------------------------------
	*req = (char *)edp_alloc_buffer(remain_len + 1);
	if(*req == NULL)
	{
		edp_free_buffer(*src_devid);
		return 4;
	}

	//读取命令------------------------------------------------------------------------
	memset(*req, 0, remain_len + 1);
	strncpy(*req, (const char *)rev_data + read_pos, remain_len);
	read_pos += remain_len;

	*req_len = remain_len;
	
	return 0;

}

//==========================================================
//	函数名称:	EDP_UnPacketCmd
//
//	函数功能:	下发命令解包
//
//	入口参数:	rev_data:收到的数据
//				cmdid:cmdid
//				cmdid_len:cmdid长度
//				req:命令
//				req_len:命令长度
//
//	返回参数:	0-成功		1-失败
//
//	说明:		
//==========================================================
uint8_t EDP_UnPacketCmd(uint8_t *rev_data, int8_t **cmdid, uint16_t *cmdid_len, int8_t **req, uint32_t *req_len)
{

	uint32_t remain_len = 0;
	int32_t read_pos = 0;
	
	//Byte0:PushData消息------------------------------------------------------------
	if(rev_data[read_pos++] != CMDREQ)
		return 1;
	
	//读取剩余长度--------------------------------------------------------------------
	read_pos = ReadRemainlen((char *)rev_data, &remain_len, read_pos);
	if(read_pos == -1)
		return 2;
	
	//读取cmdid长度-------------------------------------------------------------------
	*cmdid_len = (uint16_t)rev_data[read_pos] << 8 | rev_data[read_pos + 1];
	read_pos += 2;
	
	//分配内存------------------------------------------------------------------------
	*cmdid = (char *)edp_alloc_buffer(*cmdid_len + 1);
	if(*cmdid == NULL)
		return 3;
	
	//读取cmdid-----------------------------------------------------------------------
	memset(*cmdid, 0, *cmdid_len + 1);
	strncpy(*cmdid, (const char *)rev_data + read_pos, *cmdid_len);
	read_pos += *cmdid_len;
	
	//读取req长度---------------------------------------------------------------------
	*req_len = (uint32_t)rev_data[read_pos] << 24 | (uint32_t)rev_data[read_pos + 1] << 16
					 | (uint32_t)rev_data[read_pos + 2] << 8 | (uint32_t)rev_data[read_pos + 3];
	read_pos += 4;
	
	//分配内存------------------------------------------------------------------------
	*req = (char *)edp_alloc_buffer(*req_len + 1);
	if(*req == NULL)
	{
		edp_free_buffer(*cmdid);
		return 4;
	}
	
	//读取req-------------------------------------------------------------------------
	memset(*req, 0, *req_len + 1);
	strncpy(*req, (const char *)rev_data + read_pos, *req_len);
	read_pos += *req_len;
	
	return 0;

}

//==========================================================
//	函数名称:	EDP_PacketCmdResp
//
//	函数功能:	命令回复组包
//
//	入口参数:	cmdid:命令的cmdid(随命令下发)
//				cmdid_len:cmdid长度
//				req:命令
//				req_len:命令长度
//				edpPacket:包指针
//
//	返回参数:	0-成功		1-失败
//
//	说明:		
//==========================================================
bool EDP_PacketCmdResp(const int8_t *cmdid, uint16_t cmdid_len, const int8_t *resp, uint32_t resp_len, EDP_PACKET_STRUCTURE *edpPacket)
{
	
	uint32_t remain_len = cmdid_len + resp_len + (resp_len ? 6 : 2);
	
	EDP_NewBuffer(edpPacket, remain_len + 5);
	if(edpPacket->_data == NULL)
		return 1;
	
	//Byte0:CMDRESP消息------------------------------------------------------------
	edpPacket->_data[edpPacket->_len++] = CMDRESP;
	
	//写入剩余长度------------------------------------------------------------------
	edpPacket->_len += WriteRemainlen(edpPacket->_data, remain_len, edpPacket->_len);
	
	//写入cmdid长度------------------------------------------------------------------
	edpPacket->_data[edpPacket->_len++] = cmdid_len >> 8;
	edpPacket->_data[edpPacket->_len++] = cmdid_len & 0x00FF;
	
	//写入cmdid----------------------------------------------------------------------
	strncpy((char *)edpPacket->_data + edpPacket->_len, cmdid, cmdid_len);
	edpPacket->_len += cmdid_len;
	
	if(resp_len)
	{
		//写入req长度-----------------------------------------------------------------
		edpPacket->_data[edpPacket->_len++] = (unsigned char)(resp_len >> 24);
		edpPacket->_data[edpPacket->_len++] = (unsigned char)(resp_len >> 16);
		edpPacket->_data[edpPacket->_len++] = (unsigned char)(resp_len >> 8);
		edpPacket->_data[edpPacket->_len++] = (unsigned char)(resp_len & 0x00FF);
		
		//写入req---------------------------------------------------------------------
		strncpy((char *)edpPacket->_data + edpPacket->_len, resp, resp_len);
		
		edpPacket->_len += resp_len;
	}

	return 0;

}

//==========================================================
//	函数名称:	EDP_PacketPing
//
//	函数功能:	心跳请求组包
//
//	入口参数:	edpPacket:包指针
//
//	返回参数:	0-成功		1-失败
//
//	说明:		
//==========================================================
bool EDP_PacketPing(EDP_PACKET_STRUCTURE *edpPacket)
{

	EDP_NewBuffer(edpPacket, 2);
	if(edpPacket->_data == NULL)
		return 1;
	
	//Byte0:PINGREQ消息------------------------------------------------------------
	edpPacket->_data[edpPacket->_len++] = PINGREQ;
	
	//Byte1:0----------------------------------------------------------------------
	edpPacket->_data[edpPacket->_len++] = 0;
	
	return 0;

}

2、edpkit.h

#ifndef _EDPKIT_H_
#define _EDPKIT_H_

#include "system.h"


#define edp_alloc_buffer(buffer_size) pvPortCalloc(1, buffer_size);
#define edp_free_buffer(buffer)\
{\
    if (buffer) {\
        vPortFree(buffer);\
        buffer = NULL;\
    }\
}\

//=============================配置==============================
#define MOSQ_MSB(A)         (uint8_t)((A & 0xFF00) >> 8)
#define MOSQ_LSB(A)         (uint8_t)(A & 0x00FF)


/*--------------------------------消息编号--------------------------------*/
#define MSG_ID_HIGH			0x55
#define MSG_ID_LOW			0xAA


/*--------------------------------消息类型--------------------------------*/
/* 连接请求 */
#define CONNREQ             0x10
/* 连接响应 */
#define CONNRESP            0x20
/* 连接关闭 */
#define DISCONNECT			0x40
/* 转发(透传)数据 */
#define PUSHDATA            0x30
/* 存储(转发)数据 */
#define SAVEDATA            0x80
/* 存储确认 */
#define SAVEACK             0x90
/* 命令请求 */
#define CMDREQ              0xA0
/* 命令响应 */
#define CMDRESP             0xB0
/* 心跳请求 */
#define PINGREQ             0xC0
/* 心跳响应 */
#define PINGRESP            0xD0
/* 加密请求 */
#define ENCRYPTREQ          0xE0
/* 加密响应 */
#define ENCRYPTRESP         0xF0


#ifndef NULL
#define NULL (void*)0
#endif


/*--------------------------------SAVEDATA消息支持的格式类型--------------------------------*/
typedef enum
{
	
    kTypeFullJson = 0x01,
	
    kTypeBin = 0x02,
	
    kTypeSimpleJsonWithoutTime = 0x03,
	
    kTypeSimpleJsonWithTime = 0x04,
	
    kTypeString = 0x05
	
} SaveDataType;


/*--------------------------------内存分配方案标志--------------------------------*/
#define MEM_FLAG_NULL		0
#define MEM_FLAG_ALLOC		1
#define MEM_FLAG_STATIC		2


typedef struct Buffer
{
	
	uint8_t	*_data;		//协议数据
	
	uint32_t	_len;		//写入的数据长度
	
	uint32_t	_size;		//缓存总大小
	
	uint8_t	_memFlag;	//内存使用的方案:0-未分配	1-使用的动态分配		2-使用的固定内存
	
} EDP_PACKET_STRUCTURE;


/*--------------------------------删包--------------------------------*/
void EDP_DeleteBuffer(EDP_PACKET_STRUCTURE *edpPacket);

/*--------------------------------解包--------------------------------*/
uint8_t EDP_UnPacketRecv(uint8_t *dataPtr);

/*--------------------------------登录方式1组包--------------------------------*/
bool EDP_PacketConnect1(const int8_t *devid, const int8_t *apikey, uint16_t cTime, EDP_PACKET_STRUCTURE *edpPacket);

/*--------------------------------登录方式2组包--------------------------------*/
bool EDP_PacketConnect2(const int8_t *proid, const int8_t *auth_key, uint16_t cTime, EDP_PACKET_STRUCTURE *edpPacket);

/*--------------------------------连接回复解包--------------------------------*/
uint8_t EDP_UnPacketConnectRsp(uint8_t *rev_data);

/*--------------------------------数据点上传组包--------------------------------*/
uint8_t EDP_PacketSaveData(const int8_t *devid, int16_t send_len, int8_t *type_bin_head, SaveDataType type, EDP_PACKET_STRUCTURE *edpPacket);

/*--------------------------------PushData组包--------------------------------*/
uint8_t EDP_PacketPushData(const int8_t *devid, const int8_t *msg, uint32_t msg_len, EDP_PACKET_STRUCTURE *edpPacket);

/*--------------------------------PushData解包--------------------------------*/
uint8_t EDP_UnPacketPushData(uint8_t *rev_data, int8_t **src_devid, int8_t **req, uint32_t *req_len);

/*--------------------------------命令下发解包--------------------------------*/
uint8_t EDP_UnPacketCmd(uint8_t *rev_data, int8_t **cmdid, uint16_t *cmdid_len, int8_t **req, uint32_t *req_len);

/*--------------------------------命令回复组包--------------------------------*/
bool EDP_PacketCmdResp(const int8_t *cmdid, uint16_t cmdid_len, const int8_t *resp, uint32_t resp_len, EDP_PACKET_STRUCTURE *edpPacket);

/*--------------------------------心跳请求组包--------------------------------*/
bool EDP_PacketPing(EDP_PACKET_STRUCTURE *edpPacket);

#endif

3、data_stream.c

/**
	************************************************************
	************************************************************
	************************************************************
	*	文件名: 	dStream.c
	*
	*	作者: 		张继瑞
	*
	*	日期: 		2017-09-11
	*
	*	版本: 		V1.1
	*
	*	说明: 		cJson格式数据流通用封装
	*
	*	修改记录:	V1.1:修复当数据流flag全为0时封装错误的bug。
	************************************************************
	************************************************************
	************************************************************
**/


//C库
#include "string.h"
#include "stdio.h"
//协议封装文件
#include "data_stream.h"


//==========================================================
//	函数名称:	DSTREAM_toString
//
//	函数功能:	将数值转为字符串
//
//	入口参数:	StreamArray:数据流
//				buf:转换后的缓存
//				pos:数据流中的哪个数据
//				bufLen:缓存长度
//
//	返回参数:	无
//
//	说明:		
//==========================================================
void DSTREAM_toString(DATA_STREAM *streamArray, char *buf, unsigned short pos, unsigned short bufLen)
{
	
	memset(buf, 0, bufLen);

	switch((unsigned char)streamArray[pos].dataType)
	{
		case TYPE_BOOL:
			snprintf(buf, bufLen, "%d", *(bool *)streamArray[pos].dataPoint);
		break;
		
		case TYPE_CHAR:
			snprintf(buf, bufLen, "%d", *(signed char *)streamArray[pos].dataPoint);
		break;
		
		case TYPE_UCHAR:
			snprintf(buf, bufLen, "%d", *(unsigned char *)streamArray[pos].dataPoint);
		break;
		
		case TYPE_SHORT:
			snprintf(buf, bufLen, "%d", *(signed short *)streamArray[pos].dataPoint);
		break;
		
		case TYPE_USHORT:
			snprintf(buf, bufLen, "%d", *(unsigned short *)streamArray[pos].dataPoint);
		break;
		
		case TYPE_INT:
			snprintf(buf, bufLen, "%d", *(signed int *)streamArray[pos].dataPoint);
		break;
		
		case TYPE_UINT:
			snprintf(buf, bufLen, "%d", *(unsigned int *)streamArray[pos].dataPoint);
		break;
		
		case TYPE_LONG:
			snprintf(buf, bufLen, "%ld", *(signed long *)streamArray[pos].dataPoint);
		break;
		
		case TYPE_ULONG:
			snprintf(buf, bufLen, "%ld", *(unsigned long *)streamArray[pos].dataPoint);
		break;
			
		case TYPE_FLOAT:
			snprintf(buf, bufLen, "%f", *(float *)streamArray[pos].dataPoint);
		break;
		
		case TYPE_DOUBLE:
			snprintf(buf, bufLen, "%f", *(double *)streamArray[pos].dataPoint);
		break;
		
		case TYPE_GPS:
			snprintf(buf, bufLen, "{\"lon\":%s,\"lat\":%s}", (char *)streamArray[pos].dataPoint, (char *)(streamArray[pos].dataPoint) + 16);
		break;
		
		case TYPE_STRING:
			snprintf(buf, bufLen, "\"%s\"", (char *)streamArray[pos].dataPoint);
		break;
	}

}

//==========================================================
//	函数名称:	 DSTREAM_GetDataStream_Body
//
//	函数功能:	获取数据流格式消息体
//
//	入口参数:	type:格式类型
//				streamArray:数据流结构
//				streamArrayCnt:数据流个数
//				buffer:缓存
//				maxLen:最大缓存长度
//				offset:偏移
//
//	返回参数:	Body的长度,0-失败
//
//	说明:		
//==========================================================
short DSTREAM_GetDataStream_Body(unsigned char type, DATA_STREAM *streamArray, unsigned short streamArrayCnt, unsigned char *buffer, short maxLen, short offset)
{
	
	short count = 0, numBytes = 0;								//count-循环计数。numBytes-记录数据装载长度
	char stream_buf[96];
	char data_buf[48];
	short cBytes = 0;
	
	unsigned char *dataPtr = buffer + offset;
	
	for(; count < streamArrayCnt; count++)
	{
		if(streamArray[count].flag)
			break;
	}
	
	if(count == streamArrayCnt)
		return -1;
	
	count = 0;
	
	maxLen -= 1;												//预留结束符位置

	switch(type)
	{
		case FORMAT_TYPE1:
		
			if(numBytes + 16 < maxLen)
			{
				memcpy(dataPtr, "{\"datastreams\":[", 16);
				numBytes += 16;
			}
			else
				return 0;
			
			for(; count < streamArrayCnt; count++)
			{
				if(streamArray[count].flag)						//如果使能发送标志位
				{
					DSTREAM_toString(streamArray, data_buf, count, sizeof(data_buf));
					snprintf(stream_buf, sizeof(stream_buf), "{\"id\":\"%s\",\"datapoints\":[{\"value\":%s}]},", streamArray[count].name, data_buf);
					
					cBytes = strlen(stream_buf);
					if(cBytes >= maxLen - numBytes)
					{
						//UsartPrintf(USART_DEBUG, "dStream_Get_dFormatBody Load Failed %d\r\n", numBytes);
						return 0;
					}
					
					memcpy(dataPtr + numBytes, stream_buf, cBytes);
					
					numBytes += cBytes;
					if(numBytes > maxLen)						//内存长度判断
						return 0;
				}
			}
			
			dataPtr[numBytes] = '\0';							//将最后的','替换为结束符
			
			if(numBytes + 1 <= maxLen)
			{
				memcpy(dataPtr + numBytes - 1, "]}", 2);
				numBytes++;
			}
			else
				return 0;
		
		break;
		
		case FORMAT_TYPE3:
			
			if(numBytes + 1 < maxLen)
			{
				memcpy(dataPtr, "{", 1);
				numBytes++;
			}
			else
				return 0;
			
			for(; count < streamArrayCnt; count++)
			{
				if(streamArray[count].flag) 						//如果使能发送标志位
				{
					DSTREAM_toString(streamArray, data_buf, count, sizeof(data_buf));
					snprintf(stream_buf, sizeof(stream_buf), "\"%s\":%s,", streamArray[count].name, data_buf);

					cBytes = strlen(stream_buf);
					if(cBytes >= maxLen - numBytes)
					{
						//UsartPrintf(USART_DEBUG, "dStream_Get_dFormatBody Load Failed %d\r\n", numBytes);
						return 0;
					}
					
					memcpy(dataPtr + numBytes, stream_buf, cBytes);
					
					numBytes += cBytes;
					if(numBytes > maxLen)						//内存长度判断
						return 0;
				}
			}
			
			dataPtr[numBytes] = '\0';							//将最后的','替换为结束符

			memcpy(dataPtr + numBytes - 1, "}", 1);
		
		break;
		
		case FORMAT_TYPE4:
			
			if(numBytes + 1 < maxLen)
			{
				memcpy(dataPtr, "{", 1);
				numBytes++;
			}
			else
				return 0;
			
			for(; count < streamArrayCnt; count++)
			{
				if(streamArray[count].flag) 						//如果使能发送标志位
				{
					DSTREAM_toString(streamArray, data_buf, count, sizeof(data_buf));
					snprintf(stream_buf, sizeof(stream_buf), "\"%s\":{\"2016-08-10T12:31:17\":%s},", streamArray[count].name, data_buf);
					
					cBytes = strlen(stream_buf);
					if(cBytes >= maxLen - numBytes)
					{
						//UsartPrintf(USART_DEBUG, "dStream_Get_dFormatBody Load Failed %d\r\n", numBytes);
						return 0;
					}
					
					memcpy(dataPtr + numBytes, stream_buf, cBytes);
					
					numBytes += cBytes;
					if(numBytes > maxLen)						//内存长度判断
						return 0;
				}
			}
			
			dataPtr[numBytes] = '\0';							//将最后的','替换为结束符

			memcpy(dataPtr + numBytes - 1, "}", 1);
		
		break;
		
		case FORMAT_TYPE5:
		
			if(numBytes + 2 < maxLen)
			{
				memcpy(dataPtr, ",;", 2);
				numBytes += 2;
			}
			else
				return 0;
			
			for(; count < streamArrayCnt; count++)
			{
				if(streamArray[count].flag && streamArray[count].dataType != TYPE_GPS)	//如果使能发送标志位 格式5不支持GPS
				{
					DSTREAM_toString(streamArray, data_buf, count, sizeof(data_buf));
					snprintf(stream_buf, sizeof(stream_buf), "%s,%s;", streamArray[count].name, data_buf);
					
					cBytes = strlen(stream_buf);
					if(cBytes >= maxLen - numBytes - 2)
					{
						//UsartPrintf(USART_DEBUG, "dStream_Get_dFormatBody Load Failed %d\r\n", numBytes);
						return 0;
					}
					
					memcpy(dataPtr + numBytes, stream_buf, cBytes);
					
					numBytes += cBytes;
					if(numBytes > maxLen)						//内存长度判断
						return 0;
				}
			}
		
		break;
		
		default:
		break;
	}
	
	//UsartPrintf(USART_DEBUG, "Body Len: %d\r\n", numBytes);
	return numBytes;

}

//==========================================================
//	函数名称:	 DSTREAM_GetDataStream_Body_Measure
//
//	函数功能:	测量当前使能的数据流长度
//
//	入口参数:	type:格式类型
//				streamArray:数据流结构
//				streamArrayCnt:数据流个数
//				flag:1-测量全部数据流长度	0-测量当前需要发送的数据流长度
//
//	返回参数:	Body的长度
//
//	说明:		
//==========================================================
short DSTREAM_GetDataStream_Body_Measure(unsigned char type, DATA_STREAM *streamArray, unsigned short streamArrayCnt, bool flag)
{

	short count = 0, numBytes = 0;						//count-循环计数。numBytes-记录数据装载长度
	char stream_buf[96];
	char data_buf[48];
	
	for(; count < streamArrayCnt; count++)
	{
		if(streamArray[count].flag)
			break;
	}
	
	if(count == streamArrayCnt)
		return -1;
	
	count = 0;

	switch(type)
	{
		case FORMAT_TYPE1:
			
			numBytes += 16;
			
			for(; count < streamArrayCnt; count++)
			{
				if(streamArray[count].flag || flag)
				{
					DSTREAM_toString(streamArray, data_buf, count, sizeof(data_buf));
					snprintf(stream_buf, sizeof(stream_buf), "{\"id\":\"%s\",\"datapoints\":[{\"value\":%s}]},", streamArray[count].name, data_buf);
					
					numBytes += strlen(stream_buf);
				}
			}
			
			numBytes += 1;
		
		break;
		
		case FORMAT_TYPE3:
			
			numBytes++;
			
			for(; count < streamArrayCnt; count++)
			{
				if(streamArray[count].flag || flag)
				{
					DSTREAM_toString(streamArray, data_buf, count, sizeof(data_buf));
					snprintf(stream_buf, sizeof(stream_buf), "\"%s\":%s,", streamArray[count].name, data_buf);

					numBytes += strlen(stream_buf);
				}
			}
		
		break;
		
		case FORMAT_TYPE4:
			
			numBytes++;
			
			for(; count < streamArrayCnt; count++)
			{
				if(streamArray[count].flag || flag)
				{
					DSTREAM_toString(streamArray, data_buf, count, sizeof(data_buf));
					snprintf(stream_buf, sizeof(stream_buf), "\"%s\":{\"2016-08-10T12:31:17\":%s},", streamArray[count].name, data_buf);
					
					numBytes += strlen(stream_buf);
				}
			}
		
		break;
		
		case FORMAT_TYPE5:
			
			numBytes += 2;
			
			for(; count < streamArrayCnt; count++)
			{
				if(streamArray[count].flag || flag)
				{
					DSTREAM_toString(streamArray, data_buf, count, sizeof(data_buf));
					snprintf(stream_buf, sizeof(stream_buf), "%s,%s;", streamArray[count].name, data_buf);
					
					numBytes += strlen(stream_buf);
				}
			}
		
		break;
		
		default:
		break;
	}
	
	return numBytes;

}

4、data_stream.h


#ifndef __DATA_STREAM_H__
#define __DATA_STREAM_H__

#include "system.h"

typedef enum
{
	TYPE_BOOL = 0,
	TYPE_CHAR,
	TYPE_UCHAR,
	TYPE_SHORT,
	TYPE_USHORT,
	TYPE_INT,
	TYPE_UINT,
	TYPE_LONG,
	TYPE_ULONG,
	TYPE_FLOAT,
	TYPE_DOUBLE,
	TYPE_GPS,
	TYPE_STRING,
} DATA_TYPE;

typedef struct
{
	char *name;
	void *dataPoint;
	DATA_TYPE dataType;
	bool flag;

} DATA_STREAM;

typedef enum
{
	FORMAT_TYPE1 = 1,
	FORMAT_TYPE2,
	FORMAT_TYPE3,
	FORMAT_TYPE4,
	FORMAT_TYPE5

} FORMAT_TYPE;


short DSTREAM_GetDataStream_Body(unsigned char type, DATA_STREAM *streamArray, unsigned short streamArrayCnt, unsigned char *buffer, short maxLen, short offset);
short DSTREAM_GetDataStream_Body_Measure(unsigned char type, DATA_STREAM *streamArray, unsigned short streamArrayCnt, bool flag);



#endif

总结

        更多关于EDP协议的使用可以看OneNET平台的社区帖子或开发文档。本文是直接在无线通讯模块上使用的是socket接口完成的,也就是把无线通讯模块当做一个MCU来使用,如果你只有无线通讯模块,而没有这个模块的SDK代码,只是有一些AT命令来控制这个模块的通信,例如ESP8266模块,GSM模块等等,本文也适用,只需要将socket部分的连接、发送、接收等函数处理修改为自己的代码即可。

        本文还有很多不足之处,读者若有自己的看法和建议,评论留言。

  • 16
    点赞
  • 126
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值