点对基站实验
一、实验目的
通过本实验的课程教学,主要是让用户了解以下内容:
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;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)