You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:26:26