点对基站实验

 

一、实验目的

通过本实验的课程教学,主要是让用户了解以下内容:

1.了解802.15.4Zigbee网络协议基本概念

2.掌握使用TinyOS下点对点通信的组件使用方法

3.修改节点工作频段(信道)和节点无线模块发射功率

4.了解基站收包的运用以及数据包的格式

二、知识介绍

1. 802.15.4/Zigbee协议

无线传感网作为物联网的一个典型的应用,最近的几年来受到了非常广泛的关注,在其上也发展出了一系列的通信协议。这些协议考虑了传感网的低功耗、低复杂度的需求,对物联网通信协议的设计也起到了很大的借鉴作用。其中802.15.4/ZigBee协议是最早出现在无线传感网领域的无线通信协议,也是无线传感网领域最为著名的无线通信协议。网络化、智能化的程度十分有限,缺少有效的数据处理与信息共享能力。由于传感网和物联网的一些相似点,作为物联网的一个应用,无线传感网的网络协议也能为物联网的协议设计提供一些启发。同因特网的协议架构类似,这一部分我们将从协议栈的角度来介绍802.15.4/ZigBee协议,主要包括开放系统互连(OSI)五层模型的物理层、介质访问控制层、网络层、传输层,以及应用层。其中802.15.4主要规定了物理层和链路层的规范,物理层包括射频收发器和底层控制模块,介质访问控制子层为高层提供了访问物理信道的服务接口。ZigBee主要提供了在物理层和链路层之上的网络层、传输层和应用层规范。

2. 802.15.4/Zigbee协议的实现

现在我们就结合TinyOS操作系统来具体讲一讲802.15.4/ZigBee协议栈在一个具体操作系统中的实现方式。TinyOS是在无线传感网领域应用最多的操作系统之一,它是一个以事件为基础的非阻塞IO的操作系统。为了适应无线传感网节点的低能量和低计算能力的特点,在低功耗低计算能力的节点上部分操作可能耗时较长,从而给整个程序带来延迟。采用时间为基础,是将耗时较长的I/O操作都分为两个阶段,第一阶段为要进行提出一个IO命令,向操作系统发出指示需要执行该命令,然后用户可以进行其他操作。命令完成后操作系统会给出一个完成的事件信号来通知用户,用户再继续完成后的处理,这样就节省了节点由于I/O操作需要的等待时间。TinyOS中对802.15.4/ZigBee协议有部分的实现。在物理层上,TinyOS支持多种不同传感器节点类型,如Telos、Mica系列等等。

9-1TinyOS支持的典型硬件的部分参数

名称

参数

频段(Frequency band)

2.4GHz-2.4835GHz

发送数据率(data rate)

250kbps

天线发送能量(RF Power)

-24dBm-0dBm

接受灵敏度(Receive sensitivity)

-90dBm(最小灵敏度)

-94dBm(典型的灵敏度)

室外通信半径(outdoor range)

75米-100米

室内通信半径(indoor range)

20m-30m

 

电流

接收状态(receive mode)

23mA

发送状态(transmit mode)

18mA

空闲状态(idle)

21mA

睡眠状态(sleep)

1mA

TinyOS所支持的硬件主要使用的频段包括2.4GHz(CC2420等)和868/915MHz(CC1000)等。表12-2显示的是一个典型的TinyOS支持的通信模块的参数。

在介质访问控制层上,TinyOS实现的是最基本的载波侦探多路访问(CSMA)协议,每一个节点在发送数据包前都会对链路上的信号进行侦听(CCA)。链路侦听是通过对链路的信号指示强度(RSSI,radio signal strength indicator)测量来完成的。如果这个只是强度明显高于噪声强度,那么信道就会被认为不是空闲。通常的硬件(如CC2424)都提供直接读取RSSI的功能。如果链路空闲,就会在初始的退避(initial backoff)之后开始发送数据包,如果链路繁忙,会随机退避(congestion backoff)一段时间,然后再发送数据包。除此之外,TinyOS还提供了低功耗侦听(Low Power Listening)的介质访问控制层的实现,用来减少能量消耗。每个节点都可以使用采样的办法来侦听信道的信息,而且采样的周期可以由用户设定。在网络层上,在TinyOS的基础上有很多不同网络层上的路由协议的实现,比如包括最简单的基于最短路径的路由协议,AODV,也有根据信号质量(RSSI)的路由协议,还有根据信号的数据收发率建立路由,优化两点间的最小期望传输次数(ETX)的路由和数据收集协议(CTP,collection tree protocol)。除此之外,TinyOS还提供一个数据分发功能(dissemination)的接口。利用这个接口,节点能够把配置信息可靠的分发到每一个节点上。

三、实验步骤

在以后的实验中我们不再只是针对一个节点做实验了,我们将会用到多个节点。

1.上传编译代码

选择上传BlinkToRadio和BaseStation代码,分2次编译这2个实验代码。


图1-1 实验代码上传


图1-2 BlinkToRadio编译界面


图1-3 BaseSation编译界面

2.烧录代码

分2次烧录,选择1号点烧录BlinkToRadio代码,0号点烧录BaseStation代码,烧录提示信息如下:


图1-4 烧录提示信息

3.监听数据包

选中0号节点,点击收数按钮,收数成功会在右方提示信息里显示。


图1-5 收数操作


图1-6 操作提示信息

4.实验结果显示

点击下一步查看实验结果。如果想停止收数,回到Step2选中节点点击停止按钮即可。


图1-7 实验结果展示

四、实验分析

1.TinyOS编程模式分析

TinyOS的编程方式采用nesc语言,这是一种类C语言,nesc语言有几个最重要概念:组件,接口,模块。如下图BlinkToRadioC组件图:

图1-10 BlinkToRadioC组件图

此实验是节点1发包给节点0,即1号是发送者,0号是接收者,下面说明其信息包格式。

2.信息包格式

每个来自节点的信息包包含了很多数据区域。第1个字节(00)指明这个信息包是AM类型。接下去就是一般的活动消息区,剩下的就是消息的有效载荷区域,在BlinkToRadio.h文件里定义。

 
源码
/*-----------代码截取于BlinkToRadio/BlinkToRadio.h----------*/  
    
typedef nx_struct BlinkToRadioMsg {  
    nx_uint16_t nodeid;//2个字节 将节点ID赋值进来  
    nx_uint16_t counter;//2个字节 包的计数值  
}BlinkToRadioMsg;  
   

 

00  FF  FF  00  01  04  00  06  00 01  08  7F

AM类型(1 byte)

目标地址 (2 bytes)

链路层源地址 (2 bytes)

消息包长度 (1 byte)

网络组号(1 byte)

AM层类型 (1 byte)

有效载荷区(默认最大28 bytes)

如图信息包格式。

图1-11 信息包样例

3.代码解析

在这里重点讲解一下BlinkToRadioC中,发送数据包部分的代码。

BlinkToRadioC.nc

 
源码
/*-----------代码截取于BlinkToRadio/BBlinkToRadioC.nc----------*/  
   
event void Timer0.fired() {  
    counter++;  
    if (!busy) {  
        BlinkToRadioMsg* btrpkt =(BlinkToRadioMsg*)(call Packet.getPayload(&pkt,   
            sizeof(BlinkToRadioMsg)));  
        if (btrpkt == NULL) { return; }  
        btrpkt->nodeid = TOS_NODE_ID;  //将本节点ID加入到包负载  
        btrpkt->counter = counter;    //将发包计数值加入到包负载  
          
        //将包(pkt)广播出去,AM_BROADCAST_ADDR默认为广播,size of是计算BlinkToRadioMsg长度   
        if (call AMSend.send(AM_BROADCAST_ADDR, pkt, sizeof(BlinkToRadioMsg)) == SUCCESS)   
        { busy = TRUE; }  
    }  
}  
    

BaseStation

//BaseStationC.nc
configuration BaseStationC {
}
implementation {
  components MainC, BaseStationP, LedsC;
  components ActiveMessageC as Radio, SerialActiveMessageC as Serial;
  
  MainC.Boot <- BaseStationP;

  BaseStationP.RadioControl -> Radio;
  BaseStationP.SerialControl -> Serial;
  
  BaseStationP.UartSend -> Serial;
  BaseStationP.UartReceive -> Serial.Receive;
  BaseStationP.UartPacket -> Serial;
  BaseStationP.UartAMPacket -> Serial;
  
  BaseStationP.RadioSend -> Radio;
  BaseStationP.RadioReceive -> Radio.Receive;
  BaseStationP.RadioSnoop -> Radio.Snoop;
  BaseStationP.RadioPacket -> Radio;
  BaseStationP.RadioAMPacket -> Radio;
  
  BaseStationP.Leds -> LedsC;
}
BaseStationP.nc
 //BaseStationP.nc
#include "AM.h"
#include "Serial.h"

module BaseStationP @safe() {
  uses {
    interface Boot;
    interface SplitControl as SerialControl;
    interface SplitControl as RadioControl;

    interface AMSend as UartSend[am_id_t id];
    interface Receive as UartReceive[am_id_t id];
    interface Packet as UartPacket;
    interface AMPacket as UartAMPacket;
    
    interface AMSend as RadioSend[am_id_t id];
    interface Receive as RadioReceive[am_id_t id];
    interface Receive as RadioSnoop[am_id_t id];
    interface Packet as RadioPacket;
    interface AMPacket as RadioAMPacket;

    interface Leds;
  }
}

implementation
{
  enum {
    UART_QUEUE_LEN = 12,
    RADIO_QUEUE_LEN = 12,
  };

  message_t  uartQueueBufs[UART_QUEUE_LEN];
  message_t  * ONE_NOK uartQueue[UART_QUEUE_LEN];
  uint8_t    uartIn, uartOut;
  bool       uartBusy, uartFull;

  message_t  radioQueueBufs[RADIO_QUEUE_LEN];
  message_t  * ONE_NOK radioQueue[RADIO_QUEUE_LEN];
  uint8_t    radioIn, radioOut;
  bool       radioBusy, radioFull;

  task void uartSendTask();
  task void radioSendTask();

  void dropBlink() {
    call Leds.led2Toggle();
  }

  void failBlink() {
    call Leds.led2Toggle();
  }

  event void Boot.booted() {
    uint8_t i;

    for (i = 0; i < UART_QUEUE_LEN; i++)
      uartQueue[i] = &uartQueueBufs[i];
    uartIn = uartOut = 0;
    uartBusy = FALSE;
    uartFull = TRUE;

    for (i = 0; i < RADIO_QUEUE_LEN; i++)
      radioQueue[i] = &radioQueueBufs[i];
    radioIn = radioOut = 0;
    radioBusy = FALSE;
    radioFull = TRUE;

    call RadioControl.start();
    call SerialControl.start();
  }

  event void RadioControl.startDone(error_t error) {
    if (error == SUCCESS) {
      radioFull = FALSE;
    }
  }

  event void SerialControl.startDone(error_t error) {
    if (error == SUCCESS) {
      uartFull = FALSE;
    }
  }

  event void SerialControl.stopDone(error_t error) {}
  event void RadioControl.stopDone(error_t error) {}

  uint8_t count = 0;

  message_t* ONE receive(message_t* ONE msg, void* payload, uint8_t len);
  
  event message_t *RadioSnoop.receive[am_id_t id](message_t *msg,
						    void *payload,
						    uint8_t len) {
    return receive(msg, payload, len);
  }
  
  event message_t *RadioReceive.receive[am_id_t id](message_t *msg,
						    void *payload,
						    uint8_t len) {
    return receive(msg, payload, len);
  }

  message_t* receive(message_t *msg, void *payload, uint8_t len) {
    message_t *ret = msg;

    atomic {
      if (!uartFull)
	{
	  ret = uartQueue[uartIn];
	  uartQueue[uartIn] = msg;

	  uartIn = (uartIn + 1) % UART_QUEUE_LEN;
	
	  if (uartIn == uartOut)
	    uartFull = TRUE;

	  if (!uartBusy)
	    {
	      post uartSendTask();
	      uartBusy = TRUE;
	    }
	}
      else
	dropBlink();
    }
    
    return ret;
  }

  uint8_t tmpLen;
  
  task void uartSendTask() {
    uint8_t len;
    am_id_t id;
    am_addr_t addr, src;
    message_t* msg;
    atomic
      if (uartIn == uartOut && !uartFull)
	{
	  uartBusy = FALSE;
	  return;
	}

    msg = uartQueue[uartOut];
    tmpLen = len = call RadioPacket.payloadLength(msg);
    id = call RadioAMPacket.type(msg);
    addr = call RadioAMPacket.destination(msg);
    src = call RadioAMPacket.source(msg);
    call UartPacket.clear(msg);
    call UartAMPacket.setSource(msg, src);

    if (call UartSend.send[id](addr, uartQueue[uartOut], len) == SUCCESS)
      call Leds.led1Toggle();
    else
      {
	failBlink();
	post uartSendTask();
      }
  }

  event void UartSend.sendDone[am_id_t id](message_t* msg, error_t error) {
    if (error != SUCCESS)
      failBlink();
    else
      atomic
	if (msg == uartQueue[uartOut])
	  {
	    if (++uartOut >= UART_QUEUE_LEN)
	      uartOut = 0;
	    if (uartFull)
	      uartFull = FALSE;
	  }
    post uartSendTask();
  }

  event message_t *UartReceive.receive[am_id_t id](message_t *msg,
						   void *payload,
						   uint8_t len) {
    message_t *ret = msg;
    bool reflectToken = FALSE;

    atomic
      if (!radioFull)
	{
	  reflectToken = TRUE;
	  ret = radioQueue[radioIn];
	  radioQueue[radioIn] = msg;
	  if (++radioIn >= RADIO_QUEUE_LEN)
	    radioIn = 0;
	  if (radioIn == radioOut)
	    radioFull = TRUE;

	  if (!radioBusy)
	    {
	      post radioSendTask();
	      radioBusy = TRUE;
	    }
	}
      else
	dropBlink();

    if (reflectToken) {
      //call UartTokenReceive.ReflectToken(Token);
    }
    
    return ret;
  }

  task void radioSendTask() {
    uint8_t len;
    am_id_t id;
    am_addr_t addr,source;
    message_t* msg;
    
    atomic
      if (radioIn == radioOut && !radioFull)
	{
	  radioBusy = FALSE;
	  return;
	}

    msg = radioQueue[radioOut];
    len = call UartPacket.payloadLength(msg);
    addr = call UartAMPacket.destination(msg);
    source = call UartAMPacket.source(msg);
    id = call UartAMPacket.type(msg);

    call RadioPacket.clear(msg);
    call RadioAMPacket.setSource(msg, source);
    
    if (call RadioSend.send[id](addr, msg, len) == SUCCESS)
      call Leds.led0Toggle();
    else
      {
	failBlink();
	post radioSendTask();
      }
  }

  event void RadioSend.sendDone[am_id_t id](message_t* msg, error_t error) {
    if (error != SUCCESS)
      failBlink();
    else
      atomic
	if (msg == radioQueue[radioOut])
	  {
	    if (++radioOut >= RADIO_QUEUE_LEN)
	      radioOut = 0;
	    if (radioFull)
	      radioFull = FALSE;
	  }
    
    post radioSendTask();
  }
}  

BlinkToRadio

//Makefile
COMPONENT=BaseStationC
CFLAGS += -DCC2420_NO_ACKNOWLEDGEMENTS
CFLAGS += -DCC2420_NO_ADDRESS_RECOGNITION
CFLAGS += -DTASKLET_IS_TASK
CFLAGS += -DCC2420_DEF_CHANNEL=14
include $(MAKERULES)

 

//BlinkToRadio.h
#ifndef BLINKTORADIO_H
#define BLINKTORADIO_H

enum {
  AM_BLINKTORADIO = 6,
  TIMER_PERIOD_MILLI = 250
};

typedef nx_struct BlinkToRadioMsg {
  nx_uint16_t nodeid;
  nx_uint16_t counter;
 
} BlinkToRadioMsg;

#endif

 

//BlinkToRadioAppC.nc
#include <Timer.h>
#include "BlinkToRadio.h"

configuration BlinkToRadioAppC {
}
implementation {
  components MainC;
  components LedsC;
  components BlinkToRadioC as App;
  components new TimerMilliC() as Timer0;
  components ActiveMessageC;
  components new AMSenderC(AM_BLINKTORADIO);
  components new AMReceiverC(AM_BLINKTORADIO);

  App.Boot -> MainC;
  App.Leds -> LedsC;
  App.Timer0 -> Timer0;
  App.Packet -> AMSenderC;
  App.AMPacket -> AMSenderC;
  App.AMControl -> ActiveMessageC;
  App.AMSend -> AMSenderC;
  App.Receive -> AMReceiverC;
}
//BlinkToRadioC.nc
#include <Timer.h>
#include "BlinkToRadio.h"
module BlinkToRadioC {
  uses interface Boot;
  uses interface Leds;
  uses interface Timer<TMilli> as Timer0;
  uses interface Packet;
  uses interface AMPacket;
  uses interface AMSend;
  uses interface Receive;
  uses interface SplitControl as AMControl;
}
implementation {

  uint16_t counter;
  message_t pkt;
  bool busy = FALSE;

  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 Boot.booted() {
    call AMControl.start();
  }

  event void AMControl.startDone(error_t err) {
    if (err == SUCCESS) {
      
     call Timer0.startPeriodic(TIMER_PERIOD_MILLI);
    }
    else {
      call AMControl.start();
    }
  }

  event void AMControl.stopDone(error_t err) {
  }

  event void Timer0.fired() {
    counter++;
    if (!busy) {
      BlinkToRadioMsg* btrpkt = 
	(BlinkToRadioMsg*)(call Packet.getPayload(&pkt, sizeof(BlinkToRadioMsg)));
      if (btrpkt == NULL) {
	return;
      }
      btrpkt->nodeid = TOS_NODE_ID;
      btrpkt->counter = counter;
      if (call AMSend.send(AM_BROADCAST_ADDR, 
          &pkt, sizeof(BlinkToRadioMsg)) == SUCCESS) {
        busy = TRUE;                                  
      }
    }
  }

  event void AMSend.sendDone(message_t* msg, error_t err) {
    if (&pkt == msg) {
      busy = FALSE;
    }
  }

  event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len){
    if (len == sizeof(BlinkToRadioMsg)) {
      BlinkToRadioMsg* btrpkt = (BlinkToRadioMsg*)payload;
      setLeds(btrpkt->counter);
    }
    return msg;
  }
}

 

posted @   枫让  阅读(271)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示