物联网课程设计-通过Wifi控制Zigbee网络中执行器节点
如果要看具体的操作过程和思路,请耐心看完;
如果只是要实现控制功能,可以直接看第三部分python代码
一、基本介绍
陵阳爱普的物联网实验箱的ARM板上自带Zigbee、蓝牙、Wifi、和IPV6的服务器,并对用户提供了3个接口:
- /cgi-bin/topology.cgi 为服务器拓扑结构、网络数据的返回接口
- /cgi-bin/node.cgi 为传感器数据和节点信息的返回接口
- /cgi-bin/send_node.cgi 为向服务器发送控制信息的接口
所有的对硬件的操作都可以通过这三个接口进行
二、通过Wifi控制Zigbee网络中执行器节点基本思路
先方两段代码:
这一段为数据的解析代码,就是实验箱提供的web端的数据解析为.js文件,在进入web页面时会加载这个文件,用于解析上面三个url中的数据,可以在web端的网页源码中找到
具体功能就是把接口中的数据解析并显示到web端界面
function onNodeClicked(addr) { var qdata; $("#nodeInfo").html("加载中..."); var qdata = {'addr':addr.addr}; $.ajax({ async:true, url: "/cgi-bin/node.cgi", dataType: 'json', data: qdata, timeout: 4000, success: function (res) { if(nodeInfoContext != null) displayNodeInfo(nodeInfoContext, res); }, error: function(xhr) { if(nodeInfoContext != null) displayNodeInfo(nodeInfoContext, null); } }); } function sendExecuteBCtrl(btn) { btn.disabled = true; var addr = $("#nodeInfoTab .nwkAddrCell").html(); var cell = $(btn).parent(); var id = cell.attr('typeID'); var v = 0; v += (($("#exeb_V1_"+id).attr('checked') == undefined) ? 0 : 1); v += (($("#exeb_V2_"+id).attr('checked') == undefined) ? 0 : 2); v += (($("#exeb_V4_"+id).attr('checked') == undefined) ? 0 : 4); v += (($("#exeb_V8_"+id).attr('checked') == undefined) ? 0 : 8); v = v.toString(16); $.ajax({ async:true, url: "/cgi-bin/send_node.cgi", dataType: "json", data: {'type':cell.attr('typeCode'),'id':cell.attr('typeID'),'data':v}, timeout: 4000, context: btn, success: function (json) { btn.disabled = false; }, error: function(xhr) { btn.disabled = false; } }); } function refreshNodeData(btn) { btn.disabled = true; var addr = $("#nodeInfoTab .nwkAddrCell").html(); $.ajax({ async:true, url: "/cgi-bin/node.cgi", dataType: "json", data: {'addr':addr}, timeout: 4000, context: btn, success: function (json) { btn.disabled = false; if(json == null) return; var cell = $(this).parent(); var i; for(i = 0; i < json.length; i++) { var j; for(j = 0; j < json[i].funcList.length; j++) { if( (parseInt(json[i].funcList[j].typeCode) == parseInt(cell.attr('typeCode'))) && (parseInt(json[i].funcList[j].id) == parseInt(cell.attr('typeID'))) ) { var data = json[i].funcList[j].data; var id = json[i].funcList[j].id; if(json[i].funcList[j].typeCode == 11) { var html = ''; var v = parseInt('0x'+data); if(v & 0x01) html += '<input type="checkbox" id="exeb_V1_'+id+'" checked />AU'; else html += '<input type="checkbox" id="exeb_V1_'+id+'" />AU'; if(v & 0x02) html += '<input type="checkbox" id="exeb_V2_'+id+'" checked />AD'; else html += '<input type="checkbox" id="exeb_V2_'+id+'" />AD'; if(v & 0x04) html += '<input type="checkbox" id="exeb_V4_'+id+'" checked />BU'; else html += '<input type="checkbox" id="exeb_V4_'+id+'" />BU'; if(v & 0x08) html += '<input type="checkbox" id="exeb_V8_'+id+'" checked />BD'; else html += '<input type="checkbox" id="exeb_V8_'+id+'" />BD'; $(this).parent().prev().html(html); } else { $(this).parent().prev().html(data); } } } } }, error: function(xhr) { btn.disabled = false; } }); } function displayNodeInfo(dom, jdata) { var i; var html = ""; if(jdata == null) { dom.html("通信故障!"); return; } var func = jdata[0].funcList; html += "<table id=\"nodeInfoTab\">\n"; html += "<tr><td>网络地址</td><td class=\"nwkAddrCell\">" + jdata[0].nwkAddr +"</td></tr>\n"; html += "<tr><td>父节点地址</td><td>" + jdata[0].parAddr +"</td></tr>\n"; html += "<tr><td>物理地址</td><td>" + jdata[0].macAddr +"</td></tr>\n"; html += "<tr><td colspan=2>\n"; html += "<table width=100%><tr><td colspan=5 style=\"text-align: center;\">功能列表</td></tr>\n"; html += "<tr><td>类型</td><td>序号</td><td>刷新周期</td><td>当前数据</td><td></td></tr>\n"; for(i = 0; i < func.length; i++) { html += "<tr>"; html += "<td>" + func[i].type + "</td>"; html += "<td>" + func[i].id + "</td>"; html += "<td>" + func[i].cycle + "</td>"; if(func[i].data != null) { if(func[i].type == '控制B') { var v = parseInt('0x'+func[i].data); html += '<td class="funcDataCell">'; if(v & 0x01) html += '<input type="checkbox" id="exeb_V1_'+func[i].id+'" checked />AU'; else html += '<input type="checkbox" id="exeb_V1_'+func[i].id+'" />AU'; if(v & 0x02) html += '<input type="checkbox" id="exeb_V2_'+func[i].id+'" checked />AD'; else html += '<input type="checkbox" id="exeb_V2_'+func[i].id+'" />AD'; if(v & 0x04) html += '<input type="checkbox" id="exeb_V4_'+func[i].id+'" checked />BU'; else html += '<input type="checkbox" id="exeb_V4_'+func[i].id+'" />BU'; if(v & 0x08) html += '<input type="checkbox" id="exeb_V8_'+func[i].id+'" checked />BD'; else html += '<input type="checkbox" id="exeb_V8_'+func[i].id+'" />BD'; html += '</td>'; html += '<td typeCode="'+func[i].typeCode+'" typeID="'+func[i].id+'"><input type=button value="刷新" onclick="refreshNodeData(this);"/><input type=button value="控制" onclick="sendExecuteBCtrl(this);"/></td>'; } else { html += "<td class=\"funcDataCell\">" + func[i].data + "</td>"; html += "<td typeCode=\""+func[i].typeCode+"\" typeID=\""+func[i].id+"\"><input type=button value=\"刷新\" onclick=\"refreshNodeData(this);\"/></td>"; } } else { html += "<td colspan=2></td>"; } html += "</tr>\n"; } html += "</table></td></tr>\n"; html += "</table>\n"; dom.html(html); }
第二段为 /cgi-bin/send_node.cgi接口调用时的数据处理代码
当你向这个接口发送数据的时候服务器会通过这段代码解析并给出应答
若你在浏览器直接输入http://xxx.xxx.xxx.xxx/cgi-bin/send_node.cgi服务器会返回代码中
errString = “无效的节点地址” 部分
代码中有如下注释部分:
// input: [get]
// type=%d&id=%d&data=%s
// output: json data or jsonp proc
意思是这段代码的输入数据使用 GET 请求进行发送,其向服务器发送的数据为 json或jsonp的格式
所以,如果你要实现控制就需要使用get请求向接口发送数据
如果在浏览器直接输入这个接口地址+要发送的数据,则可以直接发送到服务器,
接口会返回 "完成"
http://xxx.xxx.xxx.xxx/cgi-bin/send_node.cgi/?type=11&id=3&data=0a
具体的说一下这个url
http://xxx.xxx.xxx.xxx/cgi-bin/send_node.cgi/ 这一部分就是接口本身的url
?type=11&id=3&data=0a 这一部分为要发送的数据
英文的 ? 为数据发送的标志
后面为数据位 type=%d&id=%d&data=%s 这段代码的作用就是解析数据位中的内容并将内容返回到服务器
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <libbt4comm.h> #define MAIN_NODE_CONFIG #include <node_config.h> // input: [get] // type=%d&id=%d&data=%s // output: json data or jsonp proc static int _str2bin(void *outBuf, const char *inBuf) { int len = 0; unsigned char *o = (unsigned char *)outBuf; while(*inBuf) { char t[3] = ""; memcpy(t, inBuf, 2); if(t[1] == 0) { inBuf += 1; char *stopAt = NULL; unsigned char c = strtoul(t, &stopAt, 16); if(stopAt != &t[1]) break; *o++ = c; len++; break; } inBuf += 2; char *stopAt = NULL; unsigned char c = strtoul(t, &stopAt, 16); if(stopAt != &t[2]) break; *o++ = c; len++; } return len; } int main(int argc, char *argv[]) { const char *query_string = getenv("QUERY_STRING"); char *callback = NULL; unsigned char type = 0xFF; unsigned char id = 0xFF; char *data = NULL; int dataLen = 0; if((query_string != NULL) && (strlen(query_string) > 0)) { char *bk = strdup(query_string); char *cb = strstr(bk, "callback="); char *tp = strstr(bk, "type="); char *d = strstr(bk, "id="); char *dt = strstr(bk, "data="); if(cb != NULL) { char *value = strchr(cb, '='); if(value != NULL) { value++; char *stop = strchr(value, '&'); if(stop != NULL) *stop = 0; if(strlen(value) > 0) callback = strdup(value); } } if(tp != NULL) { char *value = strchr(tp, '='); if(value != NULL) { value++; char *stop = strchr(value, '&'); if(stop != NULL) *stop = 0; type = strtoul(value, &stop, 10); if(stop == value) type = 0xFF; } } if(d != NULL) { char *value = strchr(d, '='); if(value != NULL) { value++; char *stop = strchr(value, '&'); if(stop != NULL) *stop = 0; id = strtoul(value, &stop, 10); if(stop == value) id = 0xFF; } } if(dt != NULL) { char *value = strchr(dt, '='); if(value != NULL) { value++; char *stop = strchr(value, '&'); if(stop != NULL) *stop = 0; data = (char *)malloc((strlen(value) + 1) >> 1); dataLen = _str2bin(data, value); if(dataLen <= 0) { free(data); data = NULL; } } } free(bk); } printf("%s\r\n\r\n", "Content-type: application/json"); if(callback != NULL) printf("%s(", callback); int result = 1; char *errString = ""; if(type == 0xFF) { result = 0; errString = "无效的节点地址"; } else if((dataLen <= 0) || (data == NULL)) { result = 0; errString = "无效的数据"; } else { bt4comm_sendNode_byType("127.0.0.1", type, id, data, dataLen); result = 1; errString = "完成"; } printf("{\"result\":%s,\"errString\":\"%s\"}", result ? "true": "false", errString); if(callback != NULL) printf(")"); printf("\n"); printf("\n"); if(callback != NULL) free(callback); if(data != NULL) free(data); return 0; }
三、python使用接口实现对Zigbee执行节点的控制
同样先上代码:
import time import urllib import requests while True: str0 = "0a" data = { 'type': 11, 'id': 3, 'data': str0 } postdata = urllib.parse.urlencode(data).encode('utf-8')r = requests.get('http://192.168.10.130/cgi-bin/send_node.cgi', postdata) print(postdata) print() print(r) print(r.text) time.sleep(3)
写成死循环为后续使用过程中要用到,在这里没有什么意义,这段代码只是一段测试代码,可以实现对Zigbee执行节点的控制
// input: [get]
// type=%d&id=%d&data=%s
// output: json data or jsonp proc
可以看到第二部分的数据接收格式为 type=%d&id=%d&data=%s
postdata = urllib.parse.urlencode(data).encode('utf-8')
这一句的作用就是将data中的数据转换为 type=%d&id=%d&data=%s这种格式
r = requests.get('http://192.168.10.130/cgi-bin/send_node.cgi', postdata)
这一句是使用get请求发送数据(其实就相当于对两部分进行拼接)
一定要注意是get请求而不是post请求,博主就是因为一个post看了一上午才发现问题
只不过要注意他会在两部分数据之间添加 ?
http://xxx.xxx.xxx.xxx/cgi-bin/send_node.cgi/ ? type=11&id=3&data=0a
python这面的核心就是这两句
最后说一下数据的格式
type为 /cgi-bin/node.cgi 接口执行器节点的 ‘typeCode’ 字段
id为 /cgi-bin/node.cgi 接口执行器节点的 ‘id’ 字段
data :先看一下四个灯的位置
每个灯的控制代码(数据)在图中已经给出
这些数据与data相对于
全灭为0X00
如果要选择其中两个或三个或四个点亮将代码相加就行
值得注意的是如果直接写这样的16进制格式,在python中并不能直接控制代码相加和为10以上的几个灯
因为 若为 0X0A 则会当0X00处理(在16进制基础上减去10)
其他也一样
此时
data = { 'type': 11, 'id': 3, 'data': “0X0A” }
这段代码中的data为需要改为对应字符串,而不是16进制数据(如图所示)
控制过程的基本流程就是这样
如果觉得对你有帮助可以点个赞再走嘛???
- 点赞
- 收藏
- 分享
- 文章举报
- 《网络通信程序设计课程设计》要求
- 留念——网络课程设计
- zigbee 节点退出网络 NLME_LeaveReq()
- 计算机网络课程设计先导集:安装Linux FTP服务器
- 网络课程设计出错汇总
- ZigBee无线温度传感网络设计
- 计算机网络课程设计第一集:客户端和服务端的第一次邂逅
- 【科研论文】一种多协议融合的物联网节点设计
- 通达OA 小飞鱼老师OA工作流设计课程教学网络公开课之HTML基础(二)
- 计算机网络课程设计(四)--发送数据包
- zigbee中限制节点加入网络
- 旧文备份:在CANopen网络中通过LSS服务设置节点地址和网络波特率
- VC++实战《星际传奇》网游课程第一部分:网络游戏开发基础篇(游戏引擎设计)
- 基于WEB2.0的网络课程交互环境设计探讨
- 通达OA 小飞鱼老师OA工作流设计课程教学网络公开课之HTML基础(二)
- 机器学习 2014斯坦福大学课程: 4-3 神经网络设计及评价
- MSDN中文网络广播预告:.NET 4.0新特性系列课程(2):契约式设计 (Level 200)