物联网创新项目实践----tinyOS节点通信组网

以下是我基于VMware软件平台,学习tinyOS节点通信组网的一点经验和总结,分享出来,希望对大家有所帮助。如果有错误的地方可以留言指出来,我一定耐心听取。相信大家只要付出努力,就一定学有所成!

目录

1.实验目的

2.实验内容

3.实验设计

4.实验主要命令行

5.实验核心代码

6.实验总结

大三上小学期物联网专业所学知识,接下来言归正传了呦

1.实验目的

1.1 学习以团队合作的形式完成一个项目,增强统一变量、统一代码等意识。
1.2 进一步学习TinyOS平台下编程开发,既巩固C语言知识、java语言知识,又了解类、配件等代码的核心思想
1.3 初步了解传感器的结构、通信方式,学习软件硬件同步调试
1.4 掌握无线传感器节点之间通信的方式,并且能够自定义数据通信格式,进一步理解网络中的拥塞、丢包的情况

2.实验内容

实验节点拓扑图

         图为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代码进行监听、接收、发送和打印。

3.实验设计

3.1实验设计思路

实验设计思路
为了更好地实现上述功能将整个代码分成4个部分,分别命名为NodeZero(节点0)NodeOne(节点1)、NodeTwo(节点2)、NodeThree(节点3和PC端)。其中,节点0负责产生数据并发送,节点1、2负责转发数据,节点3作为PC端和节点网络通讯的中间节点负责转发来自节点1、2的数据包到PC端,负责转发来自PC端的数据包到节点0。

3.2节点数据格式

定义结构体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位数据包。
数据包传输机制

3.3 核心模块

模块 功能 实现
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();

4.实验主要命令行

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命令

5.实验核心代码

5.1NodeZero

//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;
  }

5.2NodeOne and NodeTwo

//接收函数
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;
  }

5.3 NodeThree

/*节点三接收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);   
	    } 
 }

6.实验总结

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,通过改变数据包长度或者变换原有的逻辑两种方法来实现。

至此,我的讲解就结束了,希望大家可以取得好成绩。有不懂的地方欢迎提问。

猜你喜欢

转载自blog.csdn.net/weixin_43661569/article/details/100863282