当前位置: 首页 > 工具软件 > TinyOS > 使用案例 >

TinyOS - 点对点通信

万博涛
2023-12-01

一、基础知识

1.1 组件:AMSenderC

虚拟化的活动消息发送抽象。 AMSenderC的每个实例都有其自己的深度为1的队列。 因此,它不必与队列空间的其他AMSenderC实例竞争。 基础实现使用某种形式的公平共享队列来调度这些队列中的数据包。

1.1.1 AMSenderC组件接口:AMSend

cancel命令

  • error_t cancel(message_t *msg)
  • 功能:取消请求的传输。
  • 参数:需取消传输的message_t 类型的数据包的地址。
  • 返回值:取消成功返回SUCCESS
  • 示例:无

getPayload命令

  • void *getPayload(message_t *msg, uint8_t len)
  • 功能:返回一个指向包中协议有效负载区域的指针。
  • 参数:*msg - 需获取负载的message_t 类型的数据包的地址;len - 数据包的长度
  • 返回值:获取成功返回指向有效负载的指针;获取失败返回NULL
  • 补充说明:
    他的命令的行为与Packet.getPayload相同(减去length参数),并且为方便起见包含在此接口中。
  • 示例:
//App.Packet -> AMSenderC;
//interface Packet;
typedef nx_struct radio_count_msg {
  nx_uint16_t counter;
} radio_count_msg_t;

message_t packet;
 
radio_count_msg_t* rcm = (radio_count_msg_t*)call Packet.getPayload(&packet, sizeof(radio_count_msg_t));
if (rcm == NULL) {return;}
rcm->counter = counter;//修改payload的值

send命令

  • error_t send(am_addr_t addr, message_t *msg, uint8_t len)
  • 功能:将数据有效载荷为len的数据包,发送到地址addr
  • 参数:
    addr - 包发送到的目标地址;
    *msg - 指向需要发送的消息包的指针;
    len - 有效负载的长度。
  • 返回值:发送成功返回SUCCESS
  • 详细说明:
    将数据有效载荷为len的数据包发送到地址addr。 要确定最大可用大小,请使用提供AMSend的组件的Packet接口。 如果send返回SUCCESS,则该组件将在将来发出sendDone事件的信号;否则,该组件将发出信号。 如果发送返回错误,则不会发出事件信号。 注意,一个组件可以接受一个发送请求,后来发现它不能满足要求。 在这种情况下,它将用错误代码向sendDone发出信号。
  • 示例:
// App.AMSend -> AMSenderC;
//interface AMSend;
typedef nx_struct radio_count_msg {
  nx_uint16_t counter;
} radio_count_msg_t;

message_t packet;//定义packet

radio_count_msg_t* rcm = (radio_count_msg_t*)call Packet.getPayload(&packet, sizeof(radio_count_msg_t));//获取指向packet中有效负载的指针
rcm->counter = counter;//设定有效负载的值
if (call AMSend.send(AM_BROADCAST_ADDR, &packet, sizeof(radio_count_msg_t)) == SUCCESS) //发送数据包packet
{
	//do something	...
}

sendDone事件

  • event void sendDone(message_t *msg, error_t error)
  • 功能:发送成功后触发的一个事件
  • 参数:
    msg - 已经发送成功的包
    error - 指示发送是否出现小错误而导致的发送失败。
  • 返回值:
    如果成功发送,则为SUCCESS ;
    如果未成功,则为FAIL ;
    如果已取消,则为ECANCEL
  • 示例:
 // App.AMSend -> AMSenderC;
  //interface AMSend;
  message_t packet;
  //省略了对packet的一些操作
  event void AMSend.sendDone(message_t* bufPtr, error_t error) {
    if (&packet == bufPtr) {
      locked = FALSE;
    }
  }

maxPayloadLength命令

  • uint8_t maxPayloadLength()
  • 功能:返回此通信层可以提供的最大有效载荷长度。
  • 参数:无
  • 返回值:最大负载长度
  • 示例:无

1.1.2 AMSenderC组件接口:Packet

简介:基本消息数据类型访问器。 协议可以将附加的分组接口用于其协议特定的数据/元数据。

clear 命令 清理packet

  • void clear(message_t *msg)
  • 功能:清空这个packet
  • 参数:
    *msg - 需清空的message_t 类型的数据包的地址;
  • 返回值:无
  • 详细说明:
    这是一个深层操作和全部操作:在任何层上调用clear()将完全清除数据包。

getPayload命令 获取有效负载

  • void *getPayload(message_t *msg, uint8_t len)
  • 功能:返回一个指向包中协议有效负载区域的指针。
  • 参数:
    *msg - 包的指针
    len - 所需负载的长度
  • 返回值:获取成功返回指向有效负载的指针;获取失败返回NULL
  • 示例:
//interface Packet;
typedef nx_struct radio_count_msg {
  nx_uint16_t counter;
} radio_count_msg_t;

message_t packet;
 
radio_count_msg_t* rcm = (radio_count_msg_t*)call Packet.getPayload(&packet, sizeof(radio_count_msg_t));

if (rcm == NULL) {return;}

rcm->counter = counter;//修改payload的值

1.2 组件:AMReceiverC

1.2.1 AMReceiverC组件接口:Receive

receive 事件
  • event message_t *receive(message_t *msg, void *payload, uint8_t len)
  • 功能:接收一个包缓存
  • 参数:
    *msg - 接收到的数据包
    *payload- 指向数据包有效负载的指针;
    len - 指向有效负载的数据区域的长度
  • 返回值:堆栈的数据包缓冲区,用于下一个接收到的数据包。
  • 示例:
event message_t* Receive.receive(message_t* bufPtr, void* payload, uint8_t len) 
{  
    if (len != sizeof(radio_count_msg_t)) //验证长度是否正确
    	return bufPtr;
    else 
    {
      radio_count_msg_t* rcm = (radio_count_msg_t*)payload;//复制有效负载的值
    }  
  }

1.3 ActiveMessageC

1.3.1 接口SplitControl

这是StdContol接口的分相副本。 它应用于在提供它的组件的打开和关闭电源状态之间切换。 对于每个start()或stop()命令,如果该命令返回SUCCESS,则必须发出相应的startDone()或stopDone()事件的信号。

命令 start()

启动此组件及其所有子组件。

命令 stop()

停用此组件及其所有子组件。

事件 startDone

  • event void startDone(error_t error)
  • 功能:通知调用方(Notify caller)该组件已启动并准备接收其他命令。
  • 参数:
    error_t error :SUCCESS 说明成功开启;FAIL表示失败。
  • 返回值:无

事件 stopDone

二、源码分析

Makefile

这里面定义了频段CFLAGS += -DCC2420_DEF_CHANNEL=14,设置自己的频段避免与其他的设备造成冲突干扰

COMPONENT=RadioCountToLedsAppC
CFLAGS += -DCC2420_DEF_CHANNEL=14
include $(MAKERULES)

RadioCountToLeds.h

  • 定义了一个名为 radio_count_msg_t 的结构体类型,作为我们的数据包类型。
  • AM_RADIO_COUNT_MSG,定义标识号为6
#ifndef RADIO_COUNT_TO_LEDS_H
#define RADIO_COUNT_TO_LEDS_H

typedef nx_struct radio_count_msg {
  nx_uint16_t counter;
} radio_count_msg_t;

enum {
  AM_RADIO_COUNT_MSG = 6,
};

#endif

RadioCountToLedsAppC.nc

#include "RadioCountToLeds.h"

configuration RadioCountToLedsAppC {}
implementation {

    //main leds
    components MainC, RadioCountToLedsC as App, LedsC;
    App.Boot -> MainC.Boot;
    App.Leds -> LedsC;
    
    //timer
    components new TimerMilliC();
    App.MilliTimer -> TimerMilliC;
    
    //radio 无线通信
    components new AMSenderC(AM_RADIO_COUNT_MSG);
    components new AMReceiverC(AM_RADIO_COUNT_MSG);
    components ActiveMessageC;
    App.Receive -> AMReceiverC;
    App.AMSend -> AMSenderC;
    App.AMControl -> ActiveMessageC;
    App.Packet -> AMSenderC;   
}



RadioCountToLedsC.nc

发送流程:

  1. 开机上电后,去开启我们的无线通信控制器
    call AMControl.start();
  2. 开启无线通信控制器成功后会开启1S为周期的定时器,失败则重新开启无线通信控制器。
  3. 定时器到达定时时间,广播发送一个定义的不断自增的计数器count的数据。

接收流程:

  1. 开机上电后,去开启我们的无线通信控制器
    call AMControl.start();
  2. 编写接收事件函数
    首先过滤有效负载长度不正确的数据
    然后给有效负载的数据起个别名rcm,并转化成我们定义的结构体类型。
    最后根据rcm->counter 的方式读出counter值,并根据counter执行相应操作。
#include "Timer.h"
#include "RadioCountToLeds.h"
 

module RadioCountToLedsC @safe() {
  uses {
    interface Leds;
    interface Boot;
    interface Receive;
    interface AMSend;
    interface Timer<TMilli> as MilliTimer;
    interface SplitControl as AMControl;
    interface Packet;
  }
}
implementation {

  message_t packet;

  bool locked;
  uint16_t counter = 0;
  
  event void Boot.booted() {
    call AMControl.start();
  }

  event void AMControl.startDone(error_t err) {
    if (err == SUCCESS) {
      call MilliTimer.startPeriodic(1000);
    }
    else {
    //失败重新开启控制器
      call AMControl.start();
    }
  }

  event void AMControl.stopDone(error_t err) {
    // do nothing
  }
  
  event void MilliTimer.fired() {
    counter++;
    dbg("RadioCountToLedsC", "RadioCountToLedsC: timer fired, counter is %hu.\n", counter);
//没有锁上,就进行发送数据
    if (locked) {
      return;
    }
    else {
    //rcm指向有效负载的地址
      radio_count_msg_t* rcm = (radio_count_msg_t*)call Packet.getPayload(&packet, sizeof(radio_count_msg_t));
      if (rcm == NULL) {
	return;
      }

      rcm->counter = counter;
      //AM_BROADCAST_ADDR = 0xffff,表示广播
      // 0x0001,表示发送给1号节点
      if (call AMSend.send(AM_BROADCAST_ADDR, &packet, sizeof(radio_count_msg_t)) == SUCCESS) {
	dbg("RadioCountToLedsC", "RadioCountToLedsC: packet sent.\n", counter);	
	locked = TRUE;
      }
    }
  }

//发送成功解锁
  event void AMSend.sendDone(message_t* bufPtr, error_t error) {
    if (&packet == bufPtr) {
      locked = FALSE;
    }
  }

  event message_t* Receive.receive(message_t* bufPtr, 
				   void* payload, uint8_t len) {
    dbg("RadioCountToLedsC", "Received packet of length %hhu.\n", len);
    if (len != sizeof(radio_count_msg_t)) {return bufPtr;}
    else {
      radio_count_msg_t* rcm = (radio_count_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;
	    
         }
  }

}
 类似资料: