EDP协议是OneNET自主定制的协议,应用在即须要设备上传数据点到平台,又须要下发命令到设备的协议,具体关于OneNET的EDP协议的解释,请看OneNET EDP协议概述,对于协议的具体讲解,请看设备终端接入协议-EDP,SDK请看EDP-SDK,工具请看EDP调试工具,工具使用可参考博客OneNET云平台-EDP协议数据传输。html
本文简单讲解使用EDP协议的关键,对于EDP使用大概步骤为:链接平台-->链接设备-->发送心跳保持设备在线-->上报数据流到设备-->平台下发命令到设备-->设备处理命令并执行对应操做。本文用socket的方式链接、上发、接收平台来说解EDP的应用,贴出关键代码,代码除socket部分,链接设备、上报数据、发送心跳以及命令下发处理有些是参考OneNET工程师——张继瑞的代码修改的,代码可能存在bug,请读者自行辨别,仅做为参考。本文不讲解数据转发,对EPD协议的数据转发有兴趣的读者请自行研究。git
若是你是首次使用OneNET,不知道如何建立产品和设备等,可参考个人另一篇博客OneNET MQTT的简单使用,前面有讲解,也能够直接到OneNET平台开发文档查看资料,都很是详细。json
一、建立socketapi
/************************************************************** 函数名称 : 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; } }
二、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; }
三、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; }
四、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; }
五、关闭socket链接dom
/************************************************************** 函数名称 : 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;
一、链接请求 socket
(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);
链接设备成功以后,能够在页面看到设备在线状态:
二、心跳保持
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);/* 链接设备成功,开始发心跳 */ }
三、数据上发到平台
在这里,做为测试,我使用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);
上传成功后,可在设备查看到对应的数据:
四、接收平台下发命令
对应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);
上传成功,可在设备查看到对应的数据流:
如下代码做者是OneNET工程师——张继瑞,本人只是根据本身的使用的平台修改了内存申请和释放函数。
一、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; }
二、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
三、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; }
四、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部分的链接、发送、接收等函数处理修改成本身的代码便可。
本文还有不少不足之处,读者如有本身的见解和建议,评论留言。