bug1:串行栈bug + bug2
背景知识介绍
- 内容涉及到BaseStation节点与PC端数据通信的基本实现;
注意:TinyOS2.X中数据包的格式是与平台无关的,因此PC端的程序可以与任意节点通信,而TinyOS1.X中的数据包与平台相关。
串行栈的相关知识
- TinyOS2.X串口协议栈(即在节点上)的4个功能组件从下至上的分别为:
1、原始UART:功能为:为串口通信提供字节级数据的收发(例如:UartC组件提供的SerialByteCom接口) + 刷新缓存的功能(UartC组件提供的SerialFlush接口)
2、编码器/装帧器:将原始字节数据转换为串口协议数据包格式.(该层的HdlcTranslaterC组件使用了SerialByteComm接口和提供了SerialFrameComm接口)
3、传输协议:负责接收或发送协议数据包(Serial组件提供了面向字节的接口用于收发数据包),提供如下两个接口;
②、发送(SendBytePacket接口):从分派器发送到Uart
①、接收(ReceiveBytePacket):从Uart接收到的数据传到分派器(会有一个确认操作,因此会有延迟)
4、分派器:负责处理协议组件接收的数据包(SerialDispatcherC组件)
①、使用了SendBytePacket和ReceiveBytePacket,提供Send和Receive接口
②、接收数据的过程:SerialDispatcherC收到SerialC发送的一个字节数据后,然后调用offset()确定偏移量,再将数据填充到message_t结构的缓存中。 - SerialActiveMessageC组件的简单介绍
1、该组件是串行通信栈中顶层的配置组件 - 在串口栈中,数据包在连接过程中有如下格式,各协议字段分别和特定的组件对应
1、帧字节(编码器/装帧器) + 协议字节(SerialP) + 序列字节(SerialP) + 数据包格式分派字节(SerialDispatcherC) + 有效负载payload(SerialDispatcher) + CR校验序列(Serial) + 帧字节
2、payload:由组件SerialDispatcher读入的连续数据包。
MIG
- MIG(Message Interface Generator)消息接口产生器:通过该接口自动生成一个java类,用于将平台应用程序中的二进制数据打包成相应的消息数据类,来能耗的解析和显示。
- TestSerial应用程序就是用到了MIG:该程序中,利用MIG来生成对于BlinkToRadio数据包的msg代码
- 生成相应MSG代码历来与MakeFile文件,详情见《TinyOS是用编程》P141。
数据包源
- “数据包源”是传感器节点和PC直接通信的媒介,可用于接收节点上传的数据包,并向节点发送数据包。TinyOS常用的方式是串行代理连接(SerialForwarder)方式。
(应该可以这样理解:串行栈位于节点上,而数据包源位于PC端) - 数据包源的格式例如:serial@COM1:115200表示:串口使用直接连接方式 + 使用串口端口COM1 + 波特率为115200B
- SerialForwarder程序实现从串口中读取数据并在互联网中转发
串口通信测试TestSerial程序
- 程序功能:测试PC和节点之间的正常通信,节点和PC美妙会通过串口互发一个数据包。节点收到数据包在LED灯上打印,而PC收到数据包则打印数据包内容。
- TestSerial源码分析:
http://blog.sina.com.cn/s/blog_5bce66f00100sj3d.html
1、TestSerial.h文件
//定义数据包的有效数据
typedef nx_struct test_serial_msg {
nx_uint16_t counter; //计数
} test_serial_msg_t;
2、TestSerialC.nc
module TestSerialC {
uses {
// 串口的开关接口
interface SplitControl as Control;
interface Leds;
interface Boot;
interface Receive;
interface AMSend;
interface Timer<TMilli> as MilliTimer;
interface Packet;
}
}
implementation {
message_t packet;//数据包
bool locked = FALSE;//节点是否忙,同busy,发送数据时true,发送成功后置为false
uint16_t counter = 0;//数据包计数
event void Boot.booted() {
call Control.start(); //打开串口操作;向下找startDone。
}
//时间每次启动调用的函数
event void MilliTimer.fired() {
counter++;
if (locked) {
return;
}
else {
//设置数据包的有效负载,rcm指向数据包的有效负载
test_serial_msg_t* rcm = (test_serial_msg_t*)call Packet.getPayload(&packet, sizeof(test_serial_msg_t));
if (rcm == NULL) {return;}
//所发的数据超界
if (call Packet.maxPayloadLength() < sizeof(test_serial_msg_t)) {
return;
}
//设置有效负载的数据(即数据包的数据)
rcm->counter = counter;
//发送该数据包三个参数分别为:目的地址 + 数据包 + 有效负载的长度。寻找sendDone
if (call AMSend.send(AM_BROADCAST_ADDR, &packet, sizeof(test_serial_msg_t)) == SUCCESS) {
locked = TRUE;
}
}
}
//接收数据程序,接收到数据后三位设置led灯,三个参数分别为:数据包 + 有效负载 + 有效负载长度。
event message_t* Receive.receive(message_t* bufPtr,
void* payload, uint8_t len) {
if (len != sizeof(test_serial_msg_t)) {return bufPtr;}
else {
test_serial_msg_t* rcm = (test_serial_msg_t*)payload;
if (rcm->counter & 0x1) {
call Leds.led0On();
}
else {
call Leds.led0Off();
}
if (rcm->counter & 0x2) {
call Leds.led1On();
}
else {
call Leds.led1Off();
}
if (rcm->counter & 0x4) {
call Leds.led2On();
}
else {
call Leds.led2Off();
}
return bufPtr;
}
}
event void AMSend.sendDone(message_t* bufPtr, error_t error) {
if (&packet == bufPtr) {
locked = FALSE;//发送成功,节点空闲
}
}
event void Control.startDone(error_t err) {
if (err == SUCCESS) {
call MilliTimer.startPeriodic(1000); //开始成功,调用计时器,每隔一秒触发fired
}
}
event void Control.stopDone(error_t err) {} //结束成功
}
3、TestSerialAppC.nc
#include "TestSerial.h"
configuration TestSerialAppC {}
implementation {
components TestSerialC as App, LedsC, MainC;
components SerialActiveMessageC as AM;
components new TimerMilliC();
App.Boot -> MainC.Boot;
App.Control -> AM;
//参数化接口
App.Receive -> AM.Receive[AM_TEST_SERIAL_MSG];
App.AMSend -> AM.AMSend[AM_TEST_SERIAL_MSG];
App.Leds -> LedsC;
App.MilliTimer -> TimerMilliC;
App.Packet -> AM;
}
4、TestSerial.java实现PC端实现与串口的连接和数据收发通信:
import java.io.IOException;
import net.tinyos.message.*;
import net.tinyos.packet.*;
import net.tinyos.util.*;
public class TestSerial implements MessageListener {
private MoteIF moteIF;
public TestSerial(MoteIF moteIF) {
this.moteIF = moteIF;
this.moteIF.registerListener(new TestSerialMsg(), this); //为节点添加监听事件
}
//发送数据包,由PC发给节点发送数据
public void sendPackets() {
int counter = 0;
TestSerialMsg payload = new TestSerialMsg();
try {
while (true) {
System.out.println("Sending packet " + counter); //输出接收的数据包,接收数据包的个数
payload.set_counter(counter);
moteIF.send(0, payload);//计算机给节点发送数据
counter++;
try {Thread.sleep(1000);}
catch (InterruptedException exception) {}
}
}
catch (IOException exception) {
System.err.println("Exception thrown when sending packets. Exiting.");
System.err.println(exception);
}
}
//接收节点向PC发送的数据,MessageListener的实现
public void messageReceived(int to, Message message) {
TestSerialMsg msg = (TestSerialMsg)message;
System.out.println("Received packet sequence number " + msg.get_counter());
}
private static void usage() {
System.err.println("usage: TestSerial [-comm <source>]");
}
//调用程序,传入参数为-comm serial@/dev/ttyUSB0:telosb
public static void main(String[] args) throws Exception {
String source = null;
if (args.length == 2) {
if (!args[0].equals("-comm")) {
usage();//提示信息
System.exit(1);
}
source = args[1]; //参数serial@/dev/ttyUSB0:telosb
}
else if (args.length != 0) {
usage();
System.exit(1);
}
PhoenixSource phoenix; //??????
if (source == null) {
phoenix = BuildSource.makePhoenix(PrintStreamMessenger.err);
}
else {
phoenix = BuildSource.makePhoenix(source, PrintStreamMessenger.err);//连接串口
}
MoteIF mif = new MoteIF(phoenix);//设置节点
TestSerial serial = new TestSerial(mif);
serial.sendPackets();
}
}
Bug3:DIP bug
涉及的背景知识:TinyOS中的网络协议。
背景知识介绍
TinyOS中两种基本的多跳路由协议:分发协议和汇聚协议。
- 分发(Dissemination)协议实现的功能:将小数据项可靠地传送到网络中每一个节点。
- 分发协议中有一个共享变量,当共享变量发生改变时会通知应用程序,从而实现整个网络最终的一致性(即实现共享变量的网络一致性),网络中每一个共享变量都有一个该数据的副本。
相关的接口和组件
- 分发协议涉及的接口:
//读取网络中的共享数据并且更新
interface DisseminationValue<typedef t>{
command const t *get();//获取分发变量的指针;
command void set(const t *);//为该变量设置值(例如初始值)
event void changed();//当分发(共享)的变量值发生改变时,触发该事件
}
//产生(或者说是更新)需要分发的共享数据,
interface DisseminationUpdate<typedef t>{
command void change(t *newVal);//更新要发粪的值
}