数据(透传)转发(EDP协议)
该实例代码是在“数据上传(EDP协议)”小节所讲述的实例代码上稍加修改得到,首先我们要在OneNet上有两个已经注册好的设备,设备ID 和APIKey 分别为:
1
2
|
"131988" , "sUwBsToABOTYWAY6VBKMztwyIzwA" "131658" , "mgDiVsQ7E8bPUwfBDtTy4K8yMtMA" |
我们有两台开发板设备,其中设备1 使用ID“131988”,并周期性向设备2 发送字符串“Hi, Im 131988”;设备2 使用ID“131658”,并周期性向设备1 发送字符串“Hi, Im 131658”。直接编译代码并下载到设备1 中;另一方面,对于设备2,需要注释掉代码中的DEVICE1 宏定义,然后编译代码并下载到设备2 中。本例程中的代码支持DHCP 功能,因此不必为每个设备重新配置IP 地址,但是需要路由器开启DHCP 功能。如果读者使用自己定义的设备号和APIKey,则只需把下面的相关宏定义内容改为自己的设备ID 和APIKey。如下图所示:
另外,要实现数据转发,在“数据上传(EDP协议)”小节实例中,main、App_TaskStart两个函数保持不变,主要修改的函数为edp_demo函数,如下图所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
void edp_demo( void * arg) { int sockfd, ret; EdpPacket* send_pkg; /*初始化内存*/ uMEM_Init(); /*自动获取IP地址*/ while (enc28j60_netif.dhcp->state != DHCP_BOUND) OSTimeDly(10); sys_timer = sys_now(); /* 与服务建立socket连接 */ sockfd = Open(SERVER_ADDR, SERVER_PORT); if (sockfd < 0) { return ; } /*产生并发送EDP设备连接请求*/ send_pkg = PacketConnect1(DEV_ID, API_KEY); ret = DoSend(sockfd, send_pkg->_data, send_pkg->_write_pos); DeleteBuffer(&send_pkg); while (1) { /*数据接收和发送函数*/ client_process_func(( void *)&sockfd); /*发生错误,关闭socket连接*/ Close(sockfd); } } |
和“数据上传(EDP协议)”小节中的edp_demo函数最大的不同在于,该函数在while循环中调用了client_process_func函数,来看看client_process_func函数的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
int client_process_func( void * arg) { int sockfd = *( int *)arg; fd_set readset; fd_set writeset; int i, maxfdp1; for (;;) { maxfdp1 = sockfd+1; /*套节字集合清空 */ FD_ZERO(&readset); FD_ZERO(&writeset); /*加入读写套接字集合*/ FD_SET(sockfd, &readset); FD_SET(sockfd, &writeset); /*检测套接字是否可读可写*/ i = select(maxfdp1, &readset, &writeset, 0, 0); /*套接字不可读不可写*/ if (i == 0) continue ; /*检测sockfd是否在读套接字集合里面*/ if (FD_ISSET(sockfd, &readset)) { /*如果可读,接收网络数据*/ if (recv_func(sockfd) < 0) break ; } /*如果socket 准备好可写,调用send_data_to_remote函数发送数据到远程终端*/ if (FD_ISSET(sockfd, &writeset)) { if (send_data_to_remote(sockfd) < 0) break ; } } return -1; } |
该函数对socket进行一系列设置,主要目的就是实现数据实时的接收和发送,接收使用recv_func函数,发送使用send_data_to_remote函数,下面来看这两个函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
int send_data_to_remote( int arg) { unsigned int now = sys_now(); int sockfd = arg; int ret = 0; EdpPacket* send_pkg; /*每10s发送一次数据*/ if (now - sys_timer < 5000) { return 0; } sys_timer = now; /*封装EDP 数据包,包类型为转发(透传)数据包*/ send_pkg = PacketPushdata(REMOTE_ID, GREETING, sizeof (GREETING)); if (send_pkg == NULL) { Printf( "PacketPushdata error\n" ); return -1; } /*发送EDP 数据包*/ ret = DoSend(sockfd, send_pkg->_data, send_pkg->_write_pos); DeleteBuffer(&send_pkg); return ret; } |
该函数主要实现将用户数据(Hi, Im xxxxxx)打包并通过发送到远端主机,数据打包调用库函数PacketPushdata实现,该函数由平台EDP协议相关的SDK(edp_c)提供,该SDK下载地址为:https://github.com/cm-heclouds/edp_c,与之相类似的函数还有:
1
2
3
4
|
/* 实现JSON串数据存储和转发*/ EdpPacket* PacketSavedataJson( const char * dst_devid, cJSON* json_obj); /*实现二进制数据存储和转发*/ EdpPacket* PacketSavedataBin( const char * dst_devid, cJSON* desc_obj, uint8* bin_data, uint32 bin_len); |
recv_func函数的功能是从网络接收数据包,并解析出数据包里面携带的用户数据:
7
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
int recv_func( int arg) { int sockfd = arg; int error = 0; int n, rtn; uint8 mtype, jsonorbin; RecvBuffer* recv_buf = NewBuffer(); EdpPacket* pkg; char * src_devid; char * push_data; uint32 push_datalen; cJSON* save_json; char * save_json_str; cJSON* desc_json; char * desc_json_str; char * save_bin; uint32 save_binlen; int ret = 0; do { /*接收网络数据*/ n = Recv(sockfd, buffer, 512, 0); if (n <= 0) { Printf( "recv error, bytes: %d\n" , n); error = -1; break ; } Printf( "recv from server, bytes: %d\n" , n); WriteBytes(recv_buf, buffer, n); while (1) { if ((pkg = GetEdpPacket(recv_buf)) == 0) { Printf( "need more bytes...\n" ); break ; } /*解析EDP数据包的类型*/ mtype = EdpPacketType(pkg); switch (mtype) { /*连接响应数据包解析*/ case CONNRESP: rtn = UnpackConnectResp(pkg); /*串口打印解析结果*/ Printf( "recv connect resp, rtn: %d\n" , rtn); break ; /*存储(&转发)数据包解析*/ case PUSHDATA: UnpackPushdata(pkg, &src_devid, &push_data, &push_datalen); /*串口打印解析结果*/ Printf( "recv push data, src_devid: %s, push_data: %s, len: %d\n" , src_devid, push_data, push_datalen); uMEM_Free(src_devid); uMEM_Free(push_data); break ; /*转发(透传)数据包解析*/ case SAVEDATA: if (UnpackSavedata(pkg, &src_devid, &jsonorbin) == 0) { if (jsonorbin == 0x01) { /* json */ ret = UnpackSavedataJson(pkg, &save_json); save_json_str=cJSON_Print(save_json); /*串口打印解析结果*/ Printf( "recv save data json, ret = %d, src_devid: %s, json: %s\n" , ret, src_devid, save_json_str); uMEM_Free(save_json_str); cJSON_Delete(save_json); } else if (jsonorbin == 0x02) { /* bin */ UnpackSavedataBin(pkg, &desc_json, (uint8**)&save_bin, &save_binlen); desc_json_str=cJSON_Print(desc_json); /*串口打印解析结果*/ Printf( "recv save data bin, src_devid: %s, desc json: %s, bin: %s, binlen: %d\n" , src_devid, desc_json_str, save_bin, save_binlen); uMEM_Free(desc_json_str); cJSON_Delete(desc_json); uMEM_Free(save_bin); } uMEM_Free(src_devid); } break ; /*心跳响应数据包解析*/ case PINGRESP: UnpackPingResp(pkg); Printf( "recv ping resp\n" ); break ; default : Printf( "recv failed...\n" ); break ; } DeleteBuffer(&pkg); } } while (0); DeleteBuffer(&recv_buf); return error; } |
以上就是要修改的全部代码,代码修改完成后,编译并下载到两块开发板设备,注意编译设备2代码时需要注释掉宏定义#define DEVICE1,然后将设备连接网线,在串口调试工具中设备2打印的调试信息如下:
设备2(设备ID 131658)接收到设备1(设备ID 131988)发送过来的信息“Hi, Im 131988”,设备1(设备ID 131988)接收到设备2(设备ID 131658)发送过来的信息“Hi, Im 131658”,如下图:
转载请注明:Q物联网 » 中国移动物联网开放平台OneNet接入使用教程(11):如何利用Stm32开发板和EDP协议实例连接OneNet平台代码(2)