You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于TI CC1352R cli_ftd(Thread)例程,实现开机自动调用UDP函数咨询

实现CC1352R cli_ftd例程开机自动启动Thread网络 + 自动执行UDP操作

嘿,我对TI这款CC1352R的cli_ftd Thread例程摸得挺熟的,给你一步步说怎么改,不用CLI就能实现需求:

一、先让Thread网络开机自动启动

原来的cli_ftd是靠CLI命令触发启动的,我们要把启动逻辑移到系统初始化阶段:

  • 先找到Thread相关的初始化代码,一般在app.c或者thread_main.c里,找CLI命令start对应的回调函数(比如cli_command_start),里面肯定有调用OpenThread核心API的逻辑,比如otInstanceInitotIp6SetEnabledotThreadSetEnabled这些。
  • 把这些启动代码从CLI回调里抽出来,放到系统初始化完成后的钩子函数里——比如appTaskInit(如果用TI-RTOS的话),或者Board_init之后的代码段里。
  • 给你个示例代码参考,记得要确保硬件初始化完成后再调用:
    // 初始化OpenThread单实例
    otInstance *instance = otInstanceInitSingle();
    if (instance != NULL) {
        // 启用IPv6协议栈
        otIp6SetEnabled(instance, true);
        // 启用Thread协议栈
        otThreadSetEnabled(instance, true);
    
        // 自动加入已有网络(如果设备之前保存过网络参数)
        otOperationalDataset dataset;
        if (otThreadGetOperationalDataset(instance, &dataset) == OT_ERROR_NONE) {
            // 有保存的数据集,直接启动网络
            otThreadSetEnabled(instance, true);
        } else {
            // 新设备的话,预设网络参数(比如PAN ID、网络密钥)
            dataset.mPanId = 0x1234; // 自定义PAN ID
            // 这里可以补充设置网络密钥、信道等参数
            otThreadSetOperationalDataset(instance, &dataset);
            otThreadSetEnabled(instance, true);
        }
    }
    
  • 做完这些,原来的CLI启动命令可以保留(不影响自动启动),也可以注释掉避免混淆。

二、开机自动执行UDP的open/bind/connect/send

重点提醒:UDP操作必须等Thread网络完全建立(设备获取到IPv6地址、成功加入网络)后再执行,不然会直接失败。所以我们要通过Thread的状态回调来触发UDP操作:

  1. 注册Thread状态变化回调
    在Thread初始化完成后,添加回调注册代码:
otSetStateChangedCallback(instance, stateChangedCallback, instance);
  1. 实现状态回调函数,检测网络就绪状态
    当设备从“未连接”变成Leader/Router/End Device角色时,说明网络已建立,这时触发UDP操作:
static void stateChangedCallback(otInstance *instance, uint32_t flags, void *context) {
    if (flags & OT_CHANGED_THREAD_ROLE) {
        otDeviceRole role = otThreadGetDeviceRole(instance);
        // 排除未连接的状态
        if (role != OT_DEVICE_ROLE_DISABLED && role != OT_DEVICE_ROLE_DETACHED) {
            // 调用UDP初始化和发送函数
            setupAndSendUdp(instance);
        }
    }
}
  1. 实现UDP操作的核心函数setupAndSendUdp
    把open、bind、connect、send的逻辑封装在这里:
static void setupAndSendUdp(otInstance *instance) {
    otUdpSocket socket;
    otError error;

    // 1. 打开UDP Socket
    memset(&socket, 0, sizeof(socket));
    error = otUdpOpen(instance, &socket, udpReceiveCallback, instance);
    if (error != OT_ERROR_NONE) {
        // 处理错误,比如打印日志(如果有日志系统的话)
        return;
    }

    // 2. 绑定本地端口(可选,如果需要接收对方回复的话)
    otSockAddr localAddr;
    memset(&localAddr, 0, sizeof(localAddr));
    localAddr.mPort = 1234; // 自定义本地端口
    error = otUdpBind(instance, &socket, &localAddr, OT_NETIF_THREAD);
    if (error != OT_ERROR_NONE) {
        otUdpClose(instance, &socket);
        return;
    }

    // 3. 设置远程地址(UDP的connect只是预设目标,不是TCP式的连接)
    otSockAddr remoteAddr;
    memset(&remoteAddr, 0, sizeof(remoteAddr));
    // 替换成你的目标设备IPv6地址
    otIp6AddressFromString("fdde:ad00:beef:0:1234:5678:9abc:def0", &remoteAddr.mAddress);
    remoteAddr.mPort = 5678; // 目标设备的UDP端口
    error = otUdpConnect(instance, &socket, &remoteAddr);

    // 4. 发送UDP数据
    uint8_t data[] = "Hello from CC1352R Thread Device!";
    otMessage *message = otUdpNewMessage(instance, NULL);
    if (message != NULL) {
        otMessageAppend(message, data, sizeof(data)-1); // 去掉字符串末尾的'\0'
        // 如果connect成功就用otUdpSend,否则用otUdpSendTo指定目标
        if (error == OT_ERROR_NONE) {
            error = otUdpSend(instance, &socket, message);
        } else {
            error = otUdpSendTo(instance, &socket, message, &remoteAddr);
        }
        if (error != OT_ERROR_NONE) {
            otMessageFree(instance, message);
        }
    }

    // 如果不需要保持Socket接收回复,可以在这里关闭;否则留着
    // otUdpClose(instance, &socket);
}
  1. 可选:实现UDP接收回调(如果需要处理回复)
static void udpReceiveCallback(void *context, otMessage *message, const otMessageInfo *messageInfo) {
    otInstance *instance = (otInstance *)context;
    uint8_t buffer[128];
    size_t length = otMessageRead(message, otMessageGetOffset(message), buffer, sizeof(buffer)-1);
    buffer[length] = '\0';
    // 这里可以添加处理逻辑,比如打印接收到的数据
    // Log_info("Received UDP: %s from %s:%d", buffer, otIp6AddressToString(&messageInfo->mPeerAddr, NULL), messageInfo->mPeerPort);
}

三、几个要注意的细节

  • 网络参数要正确:如果是新设备,必须预设PAN ID、网络密钥、信道这些参数,不然设备没法加入网络;如果是之前连过的设备,OpenThread会自动保存参数,开机直接恢复。
  • 目标IPv6地址要准确:得是Thread网络内的有效地址,可以先通过CLI命令获取目标设备的地址,再硬编码到代码里;如果需要动态发现,可以结合DNS-SD功能实现。
  • 错误处理要到位:比如Socket打开失败、发送失败的情况,要做容错处理,避免程序崩溃。
  • 任务上下文:如果用TI-RTOS,这些操作要在任务里执行,别在中断回调里做复杂逻辑。

内容的提问来源于stack exchange,提问作者user10989811

火山引擎 最新活动