以下是我基于VMware软件平台,学习tinyOS节点通信组网的一点经验和总结,分享出来,希望对大家有所帮助。如果有错误的地方可以留言指出来,我一定耐心听取。相信大家只要付出努力,就一定学有所成!
大三上小学期物联网专业所学知识,接下来言归正传了呦
1.1 学习以团队合作的形式完成一个项目,增强统一变量、统一代码等意识。
1.2 进一步学习TinyOS平台下编程开发,既巩固C语言知识、java语言知识,又了解类、配件等代码的核心思想
1.3 初步了解传感器的结构、通信方式,学习软件硬件同步调试
1.4 掌握无线传感器节点之间通信的方式,并且能够自定义数据通信格式,进一步理解网络中的拥塞、丢包的情况
图为4个无线传感器组成的拓扑结构的网络,节点0为数据源节点,产生两种类型的数据,将生成的数据广
出去,节点1,2分别接收一种类型的数据,接着转发给节点3。
实验要求详细说明:
1. 节点0产生的数据包括两种类型,一种是采集的环境温度,一种是100以内的随机数。温度数据经过转
换,以摄氏度为单位。
2. 节点0可以根据节点3传来的不同信号来发送不同的数据包,当收到1-6会返回小组学号,收到0会
按照实验预案要求返回数据。
3. 节点1只接收包含温度的数据包,节点2只接受包含随机数的数据包。当节点1,2将数据包转发送给节
点3时,数据包中必须包含本节点的ID。
4. 节点1和节点2中均增加打印代码,当节点通过串口连接电脑以后,输入打印命令,可以看到节点接收
和转发的数据。
5. 利用led灯来表明工作状态。红灯表示开始工作,黄灯表示接收数据成功,蓝灯表示发送数据成功。
6. 在节点三代码中加入PC端java代码,利用java代码进行监听、接收、发送和打印。
为了更好地实现上述功能将整个代码分成4个部分,分别命名为NodeZero(节点0)NodeOne(节点1)、NodeTwo(节点2)、NodeThree(节点3和PC端)。其中,节点0负责产生数据并发送,节点1、2负责转发数据,节点3作为PC端和节点网络通讯的中间节点负责转发来自节点1、2的数据包到PC端,负责转发来自PC端的数据包到节点0。
定义结构体Msg为:
无符号16位整型nodeid |
---|
无符号16位整型counter |
无符号16位整型nodeflag |
– |
这种数据包格式包含了源id、内容和消息类型,可以帮助节点区分数据包的类型和源地址,并根据数据包和实际需求实现相应的逻辑。
同时需要注意在节点转发数据过程中,会向四周扩散数据,所以我们要想好数据接收机制。利用以下数据格式就可以达到我们的目的了哟。
节点 | 接收 | 发送 |
---|---|---|
节点0 | nodeflag==0 | nodeflag==1,2 |
节点1 | nodeflag==1 | nodeflag==1 |
节点2 | nodeflag==2 | nodeflag==2 |
节点3 | nodeflag==1,2 | nodeflag==0 |
– | – | – |
但要注意PC端上的输入输出命令是由Java语言设计的,而输入输出的是32位的整型。为了适应节点通信的数据包格式,使用位的偏移量将2个32位的整型(id、data)在值不变的情况下压缩成2个16位的整型发送给节点3,再由节点3整合nodeflag形成48位数据包。
模块 | 功能 | 实现 |
---|---|---|
LED灯模块 | 控制节点的灯 | call Leds.led[x]On();call Leds.led[x]Off(); |
定时器模块 | 定时执行相应逻辑 | call Timer[x].startPeriodic(时间参数); |
发送模块 | 发送数据 | AMSend.send(AM_BROADCAST_ADDR, &pkt, sizeof(结构体) |
接收模块 | 接收数据 | event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len){NodeZeroMsg* btrpkt = (结构体*)payload;} |
打印模块 | 打印变量和字符串 | printf("");printfflush(); |
随机数模块 | 产生随机数 | randnumber = call Random.rand16(); |
温度模块 | 测量温度 | call readTemp.read(); |
– | – | – |
sudo su
输入虚拟器密码
source .bashrc
source /opt/tinyos2-1-1/tinyos.sh
cd 文件路径/*控制台切换到文件所在路径*/
motelist/*检测设备端口*/
java 文件名.java/*编译java文件*/
java 文件名 -comm serial@/dev/ttyUSB0:telosb/*执行java文件*/
java net.tinyos.tools.Listen -comm serial@/dev/ttyUSB0:telosb/*监听命令*/
java net.tinyos.tools.PrintfClient -comm serial@/dev/ttyUSB0:telosb/*打印命令*/
对于这些命令,看节点具体需求,0需要监听,1,2需要打印,3需要java命令
//led显示模块逻辑
void setLeds(uint16_t val) {
if (val & 0x01)
call Leds.led0On();
else
call Leds.led0Off();
if (val & 0x02)
call Leds.led1On();
else
call Leds.led1Off();
if (val & 0x04)
call Leds.led2On();
else
call Leds.led2Off();
}
//读取温湿度
event void Timer0.fired() {
call readPhoto.read();
call readHumidity.read();
call readTemp.read();
printf("check the humi and temp\n");
printfflush();
}
//发送数据包
event void Timer1.fired() {
if(tim1 <= 60){
if (!busy) {
NodeZeroMsg* btrpkt =
(NodeZeroMsg*)(call Packet.getPayload(&pkt, sizeof(NodeZeroMsg)));
if (btrpkt == NULL) {
return;
}
call Leds.led0On();
btrpkt->nodeid = TOS_NODE_ID;
btrpkt->counter = TempData;
btrpkt->nodeflag = 1;
if (call AMSend.send(AM_BROADCAST_ADDR,
&pkt, sizeof(NodeZeroMsg)) == SUCCESS) {
printf("Send temperature %u by id %u and flag is %u\n", btrpkt ->counter, btrpkt -> nodeid, btrpkt -> nodeflag);
printfflush();
busy = TRUE;
}
call Leds.led0Off();
tim1++;
}
}
else if(tim1 <= 120&&tim1 >= 61){
randnumber = call Random.rand16();
if (!busy) {
NodeZeroMsg* btrpkt =
(NodeZeroMsg*)(call Packet.getPayload(&pkt, sizeof(NodeZeroMsg)));
if (btrpkt == NULL) {
return;
}
call Leds.led0On();
btrpkt->nodeid = TOS_NODE_ID;
btrpkt->counter = (int16_t)(randnumber % 100);
btrpkt->nodeflag = 2;
if (call AMSend.send(AM_BROADCAST_ADDR,
&pkt, sizeof(NodeZeroMsg)) == SUCCESS) {
busy = TRUE;
printf("Send randomNumber %u by id %u and flag is %u\n", btrpkt ->counter, btrpkt -> nodeid, btrpkt -> nodeflag);
printfflush();
}
call Leds.led0Off();
tim1++;
}
else{ call Timer1.stop();
}
}
}
//湿度检测
event void readTemp.readDone(error_t result, uint16_t val) {
if (result == SUCCESS){
val = -40.1+ 0.01*val;//转换成摄氏度值,这个公式根据SHT11数据手册
TempData = val;
}
else TempData = 0xffff;
}
event void readHumidity.readDone(error_t result, uint16_t val) {
if (result == SUCCESS){
HumidityData = -4 + 4.05*val/100 + (-28/1000/10000)*(val*val);//转换成带温度补偿的湿度值
HumidityData = (TempData-25)*(1/100+8*val/100/1000)+HumidityData;//但结果不知道这个转换公式转换的结果对不对
}
else HumidityData = 0xffff;
}
//接收数据函数
event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len){
if (len == sizeof(NodeZeroMsg)) {
NodeZeroMsg* btrpkt2 = (NodeZeroMsg*)payload;
s[0]=btrpkt2->counter/1000+'0';
s[1]=btrpkt2->counter/100%10+'0';
s[2]=btrpkt2->counter/10%10+'0';
s[3]=btrpkt2->counter%10+'0';
s[4]='\0';
printf("Receive %u from id %u and flag is %u\n", btrpkt2 ->counter, btrpkt2 -> nodeid, btrpkt2 -> nodeflag);
printfflush();
if(btrpkt2 -> nodeflag != 0){
return msg;
}
if (!busy) {
if(s[3] == '0'){
tim1 = 1;
call Timer1.startPeriodic(1000);
}
else{
NodeZeroMsg* btrpkt =
(NodeZeroMsg*)(call Packet.getPayload(&pkt, sizeof(NodeZeroMsg)));
if (btrpkt == NULL) {
return msg;
}
btrpkt -> nodeid = TOS_NODE_ID;
printf("my id is %u\n",btrpkt -> nodeid);
if(s[3] == '1'){
btrpkt->counter = 1633;
btrpkt->nodeflag = 1;
printf("**%u**",btrpkt->counter);
printfflush();
}
else if(s[3] == '2'){
btrpkt->counter = 3455;
printf("**%u**",btrpkt->counter);
printfflush();
}
else if(s[3] == '3'){
btrpkt->counter = 1111;
btrpkt->nodeflag = 1;
printf("**%u**",btrpkt->counter);
printfflush();
}
else if(s[3] == '4'){
btrpkt->counter = 1212;
btrpkt->nodeflag = 1;
printf("**%u**",btrpkt->counter);
printfflush();
}
else if(s[3] == '5'){
btrpkt->counter = 1108;
btrpkt->nodeflag = 1;
printf("**%u**",btrpkt->counter);
printfflush();
}
else if(s[3] == '6'){
btrpkt->counter = 1125;
btrpkt->nodeflag = 1;
printf("**%u**",btrpkt->counter);
printfflush();
}
else if(s[3] == '7'){
btrpkt->counter = HumidityData;
btrpkt->nodeflag = 1;
printf("**%u**",btrpkt->counter);
printfflush();
}
else if(s[3] == '8'){
btrpkt->counter = PhotoData;
btrpkt->nodeflag = 1;
printf("**%u**",btrpkt->counter);
printfflush();
}
if (call AMSend.send(AM_BROADCAST_ADDR, &pkt, sizeof(NodeZeroMsg)) == SUCCESS){
busy = TRUE;
printf("Send %u by id %u and flag is %u\n", btrpkt ->counter, btrpkt -> nodeid, btrpkt -> nodeflag);
}
}
}
}
return msg;
}
//接收函数
event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len){
if (len == sizeof(NodeOneMsg)) {
NodeOneMsg* btrpkt1 = (NodeOneMsg*)payload;
call Leds.led1On();
printf("Receive %u from id %u and flag is %u\n",btrpkt1->counter,btrpkt1->nodeid,btrpkt1->nodeflag);
printfflush();
if (!busy) {
NodeOneMsg* btrpkt = (NodeOneMsg*)(call Packet.getPayload(&pkt, sizeof(NodeOneMsg)));
if (btrpkt == NULL) {
return msg;
}
if(btrpkt1->nodeflag == 1){
btrpkt->nodeid = TOS_NODE_ID;
btrpkt->counter = btrpkt1->counter;
btrpkt->nodeflag = btrpkt1->nodeflag;
if (call AMSend.send(AM_BROADCAST_ADDR,
&pkt, sizeof(NodeOneMsg)) == SUCCESS) {
printf("Send %u by id %u and flag is %u\n",btrpkt->counter,btrpkt->nodeid,btrpkt->nodeflag);
printfflush();
call Leds.led2On();
busy = TRUE;
}
}
else if(btrpkt1->nodeflag == 2){
printf("I don't send the Packet!\n");
printfflush();
}
}
call Leds.led1Off();
}
return msg;
}
/*节点三接收PC端消息并转发给其他节点*/
event message_t* SerialReceive.receive
(message_t* bufPtr, void* payload,uint8_t len) {
Msg1_t* msg1 = (Msg1_t*)payload;//receive the pc
Msg_t* msg = (Msg_t*)call SerialPacket.getPayload(&pack,sizeof(Msg_t));
msg->id=msg1->id;
msg->data=msg1->data;
msg->flag=0;
if (call RadioSend.send(AM_BROADCAST_ADDR,&pack, sizeof(Msg_t)) == SUCCESS) { call Leds.led0On();//TODO: 使用无线电接口转发数据包
}
return bufPtr;
}
/* 节点三接收其他节点消息并转发至PC端*/
event message_t* RadioReceive.receive(message_t* bufPtr, void* payload,uint8_t len){
//TODO: 使用串口接口转发数据包
Msg_t* msg = (Msg_t*)payload;//receive the pc
Msg1_t* msg1 = ((Msg1_t*)call RadioPacket.getPayload(&pack,sizeof(Msg1_t)); msg1->id=msg->id;
msg1->data=msg->data;
if (call SerialSend.send(AM_BROADCAST_ADDR,&pack, sizeof(Msg1_t)) == SUCCESS) {
call Leds.led1On();//转发至PC端
}
return bufPtr;
}
/*PC端接收代码*/
public void messageReceived(int to, Message message) {
NodeThreeMsg msg = (NodeThreeMsg)message;
System.out.println("Received packet id " + msg.get_id() + "and the message is "+ msg.get_data());//打印收到的消息
}
/*PC端发送代码*/
public void sendPackets() {
int counter = 1;
NodeThreeMsg payload = new NodeThreeMsg(); //payload为消息变量
try {
while (true) {
payload.set_id(3);//id默认为3
payload.set_data(int); //内容为int型
moteIF.send(0, payload);//发送内容
counter++;//计量数据包
}
} catch (IOException exception) {
System.err.println("Exception thrown when sending packets. Exiting."); System.err.println(exception);
}
}
TinyOs平台集合了非常强大的功能,包括定时器、亮灭灯、数据包接收和发送测试温度湿度等,这些功能组合在一起可以极大程度地做到节点网络的实现,和温度湿度等的测量。该平台是物联网技术中非常实用的平台,代码简洁流畅,硬件功耗低,持续时间长
可能遇到的一些新的难点,我讲解一些主要的难点:
1、实验逻辑设计:采用功能逻辑图,一步步绘出我们想要实现的功能和模块,最后进行有条不紊的编写。
2、产生随机数:对于产生随机数,我们调用了库函数,由于库函数产生的随机数是一个16位二进制数,所以我们自己编写一个算法将随机数的范围缩小至0-100
3、NodeZero代码中温湿度测试:翻看上学期物联网通信课程相关课件,学习和编写
4、代码中如何区分节点0,1,2,3发送的数据:将代码格式定为三个变量的结构体,内含有flag,id,counter这三个信息,利用flag和id相互配合就可以实现0节点只接收3节点的命令,1节点和2节点区分接受0节点的命令,3节点只接收1和2节点的命令,并区分接收和打印。
5、Java代码中数据格式和位长等信息和nesc代码中存在差异,导致数据传输错误:利用多处打印发现传输成功却解码不成功,最终在java代码伴生文件中发现这个bug,通过改变数据包长度或者变换原有的逻辑两种方法来实现。
至此,我的讲解就结束了,希望大家可以取得好成绩。有不懂的地方欢迎提问。