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时返回的idremote_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时返回的iddata_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时返回的iddata_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 1if(EDP_PacketConnect1(id, auth_key, 256, &edpPacket) == 0) //根据devid 和 apikey封装协议包
#elseif(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:设备IDapikey:设备apikeystreamArray:数据流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));elseEDP_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;elseremain_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;elseremain_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 2typedef 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;}elsereturn 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++;}elsereturn 0;break;case FORMAT_TYPE3:if(numBytes + 1 < maxLen){memcpy(dataPtr, "{", 1);numBytes++;}elsereturn 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++;}elsereturn 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;}elsereturn 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部分的连接、发送、接收等函数处理修改为自己的代码即可。
本文还有很多不足之处,读者若有自己的看法和建议,评论留言。
OneNET EDP协议讲解与应用相关推荐
- STM32F407-OV7670(无FIFO)-ONENET-实现摄像头画面上传到onenet(EDP协议)
EDP协议 EDP协议文档说明:https://leoeinstein.lanzous.com/ifX1Ckfkaaf 服务器登录 登录流程: (1)客户端发送连接请求(CONN_REQ) (2)服务 ...
- WIZnet W5500系列培训活动之“MQTT协议讲解和实践(接入OneNET)”
万物联网的时代已经到来,物联网也由当初的概念开始进一步落实.物联网通信协议当中发展最迅速.应用最广泛的就是MQTT,它是轻量级基于代理的发布/订阅的消息传输协议,并且可以通过很少的代码和带宽与远程设 ...
- OneNET云平台-EDP协议数据传输
OneNet真是中移动的良心之作,对比阿里云和庆科云,OneNet不但免费而且功能也足够嵌入式应用,对学生党而言真是大大的福利,感谢中移动!!!. 一.云端创建设备与应用 (1)创建产品:进入开发者中 ...
- 北斗通讯协议4.0 java_北斗4.0协议讲解.doc
北斗4.0协议讲解 北斗用户机用户接口协议 (4.0版本外用) 接口数据传输约定 串口非同步传送,参数定义如下: 传输速率:19200bit/s(默认),可根据用户机具体情况设置其它速率: 1 bit ...
- Camera DVP协议讲解
Camera DVP协议讲解 该文章写于2016年,一直未发表.近期工作中又涉及到了Camera相关的内容,重新温习了下相关知识.决定把这篇文章发表出来,大家共同学习. DVP(Digital V ...
- DP/eDP协议学习--协议简介
最近一段时间由于项目接触到该协议,该协议不像HDMI,USB资料那么多,虽然应用还是很广泛的,但是生态不是很好.自己看了一段时间的协议,想着记录下来大家一起讨论学习. 1综述 eDP(Embedded ...
- 蓝牙DID协议讲解 Bluetooth Device ID
零. 概述 主要介绍下蓝牙协议栈(bluetooth stack)DID(Device ID)协议讲解 btsnoop以及流程在资料中的......\STM32_UBUNTU_BLUETOOTH\2- ...
- C语言实现MQTT协议(一)协议讲解
MQTT介绍 MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议.它的设计思想是轻巧.开放.简单.规范,易于实现.这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器 ...
- DeviceNet现场总线协议讲解
DeviceNet现场总线协议讲解 2008-2-28 10:27:00 来源:作者: 网友评论 0条 点击查看 <script src="http://www.ca800.com/a ...
- BT.656协议讲解与解码
BT.656支持不同位数和分辨率的视频,本文以标准分辨率为720*576i 8bit BT.656(4:2:2)YCbCr SDTV 数字视频信号格式讲解协议和解码方法:采集图像的时候使用隔行扫描,每 ...
最新文章
- usaco Picture(离散化求线段周长)
- log4j打印mybatis执行的sql
- 小强学渲染之OpenGL渲染管线详析
- MyEclipse 15 集成SVN
- History操作历史记录
- SQL Server 创建表
- Spring实战——无需一行xml配置实现自动化注入
- HTML 标签学习总结第一天
- 使用 lanmps 环境套件安装设置新站点 案例
- 用UltraEdit软件替换回车换行的窍门
- 五款最好的免费同步软件
- dart语言和PHP,如何以Dart语言完全转储/打印变量到控制台?
- 记录Android Studio KeyMap 导入的问题
- atomic 原子操作
- zabbix企业应用之自动语音报警平台
- (SEO优化)现身说法教你如何优化百度的收录,权重,关键词排名 SEO优化(一)
- mysql字符集与校对规则设置_MySQL 字符集与校对规则
- tableview的详解
- 无法安装X64 在计算机找到X32,怎么看电脑适合装32位还是64位
- 如何看计算机接口类型,怎么看自己电脑cpu的接口类型
热门文章
- 碎碎念情境记忆法——最适合程序员的背单词方法
- Feescale K60开发笔记3: Tftpd32的使用
- HTML标签关系——双标签和单标签,标签的嵌套与并列
- python符号或非并列,Python运算符
- 如何建设网站才有利于网站优化
- MapReduce 内部实现机制,你真的懂吗?
- DotaMax网站”获取“自己与职业选手(知名玩家)对战记录
- 基于pyod中机器学习与神经网络方法的异常值识别方法合集(含2022年全国服务外包大赛实例)
- WIN7+LR11+IE8无法打开的问题解决方法
- 电脑连接android手机测试,关于如何将手机画面投屏到PC的测试(Android)