nesC/TinyOS中PacketAcknowledgements接口使用问题及代码排查
我帮你排查代码里的问题,再给你一个PacketAcknowledgements接口的标准使用示例:
问题排查与修复
1. 核心问题:发送失败后未重置发送锁
你在AMSend.sendDone事件里,只在收到ACK时把locked设为FALSE,一旦某次发送没收到ACK,locked就会一直保持TRUE,后续定时器触发时直接返回,再也发不出数据包了——这就是你看到数据无法传输的主要原因。
修复这段代码:
event void AMSend.sendDone(message_t* bufPtr, error_t error) { if(call PacketAcknowledgements.wasAcked(bufPtr)){ dbg("RadioCountToLedsC", "ACKED!\n"); } else { dbg("RadioCountToLedsC", "NOT ACKED!\n"); } // 不管有没有收到ACK,都要释放发送锁,允许下次发送 locked = FALSE; }
2. 次要问题:调试输出格式错误
在MilliTimer.fired里的dbg语句,你传了counter参数,但格式化字符串里没有对应的占位符,会导致输出乱码,修正一下:
dbg("RadioCountToLedsC", "RadioCountToLedsC: packet sent, counter is %hu.\n", counter);
3. 接收端ACK回复确认
你的配置文件已经绑定了AMReceiverC,在TinyOS里这个组件默认会自动处理ACK请求——只要收到带ACK标记的数据包,就会自动回复ACK,所以这部分配置是没问题的,但要确保所有接收节点都用了正确的配置(包含AMReceiverC和ActiveMessageC)。
PacketAcknowledgements接口正确使用示例
下面是一个精简的完整示例,涵盖发送端和接收端的标准实现:
发送端模块(SenderModuleC)
#include "Timer.h" #include "AM.h" // 定义自定义消息结构 typedef nx_struct test_msg { nx_uint16_t seq_num; // 序列号 } test_msg_t; module SenderModuleC @safe() { uses { interface Boot; interface AMSend; interface Timer<TMilli> as MilliTimer; interface SplitControl as AMControl; interface Packet; interface PacketAcknowledgements; } } implementation { message_t packet; bool is_sending = FALSE; nx_uint16_t seq = 0; event void Boot.booted() { dbg("Sender", "Booted, starting radio...\n"); call AMControl.start(); } event void AMControl.startDone(error_t err) { if (err == SUCCESS) { dbg("Sender", "Radio started, starting timer...\n"); call MilliTimer.startPeriodic(2000); // 每2秒发送一次 } else { dbg("Sender", "Radio start failed, retrying...\n"); call AMControl.start(); } } event void MilliTimer.fired() { if (is_sending) { dbg("Sender", "Still sending, skip this timer tick...\n"); return; } // 获取数据包的payload空间 test_msg_t* msg = (test_msg_t*)call Packet.getPayload(&packet, sizeof(test_msg_t)); if (msg == NULL) { dbg("Sender", "Failed to get payload space...\n"); return; } // 设置消息内容 msg->seq_num = seq++; // 请求接收端回复ACK call PacketAcknowledgements.requestAck(&packet); // 发送数据包到广播地址 if (call AMSend.send(AM_BROADCAST_ADDR, &packet, sizeof(test_msg_t)) == SUCCESS) { dbg("Sender", "Sent packet with seq: %u\n", msg->seq_num); is_sending = TRUE; } else { dbg("Sender", "Failed to send packet...\n"); } } event void AMSend.sendDone(message_t* bufPtr, error_t error) { test_msg_t* msg = (test_msg_t*)bufPtr->data; if (call PacketAcknowledgements.wasAcked(bufPtr)) { dbg("Sender", "Packet seq %u was ACKED!\n", msg->seq_num); } else { dbg("Sender", "Packet seq %u was NOT ACKED!\n", msg->seq_num); } // 释放发送锁,允许下次发送 is_sending = FALSE; } }
发送端配置文件(SenderAppC)
configuration SenderAppC {} implementation { components MainC, SenderModuleC as App; // 注册AM消息ID(AM_TEST_MSG需在头文件中定义,比如#define AM_TEST_MSG 0x89) components new AMSenderC(AM_TEST_MSG); components new TimerMilliC(); components ActiveMessageC; // 接口绑定 App.Boot -> MainC.Boot; App.AMSend -> AMSenderC; App.AMControl -> ActiveMessageC; App.MilliTimer -> TimerMilliC; App.Packet -> AMSenderC; App.PacketAcknowledgements -> AMSenderC.Acks; }
接收端模块(ReceiverModuleC)
#include "AM.h" #include "Leds.h" // 和发送端一致的消息结构 typedef nx_struct test_msg { nx_uint16_t seq_num; } test_msg_t; module ReceiverModuleC @safe() { uses { interface Boot; interface Receive; interface SplitControl as AMControl; interface Leds; } } implementation { event void Boot.booted() { dbg("Receiver", "Booted, starting radio...\n"); call AMControl.start(); } event void AMControl.startDone(error_t err) { if (err != SUCCESS) { dbg("Receiver", "Radio start failed, retrying...\n"); call AMControl.start(); } else { dbg("Receiver", "Radio ready, waiting for packets...\n"); } } event message_t* Receive.receive(message_t* bufPtr, void* payload, uint8_t len) { if (len != sizeof(test_msg_t)) { dbg("Receiver", "Received packet with wrong length!\n"); return bufPtr; } test_msg_t* msg = (test_msg_t*)payload; dbg("Receiver", "Received packet seq: %u\n", msg->seq_num); // 用LED反馈:序列号奇数亮LED0,偶数灭LED0 if (msg->seq_num % 2 == 1) { call Leds.led0On(); } else { call Leds.led0Off(); } // 返回bufPtr,让系统自动处理ACK回复(如果发送端请求了的话) return bufPtr; } }
接收端配置文件(ReceiverAppC)
configuration ReceiverAppC {} implementation { components MainC, ReceiverModuleC as App, LedsC; components new AMReceiverC(AM_TEST_MSG); // 和发送端相同的AM消息ID components ActiveMessageC; // 接口绑定 App.Boot -> MainC.Boot; App.Receive -> AMReceiverC; App.AMControl -> ActiveMessageC; App.Leds -> LedsC; }
内容的提问来源于stack exchange,提问作者ybk1996
相关产品推荐
相关产品推荐

