IoT Pi SDDC 设备开发

更新时间:
2023-11-30
下载文档

IoT Pi SDDC 设备开发

本章将介绍如何在 IoT Pi 上使用 MS-RTOS 和 libsddc 开发一个能接入到 EdgerOS 的 Wi-Fi 物联网设备。

软件准备

IoT Pi 开发环境搭建

参考《IoT Pi 快速入门》,完成以下步骤:

  • 完成 MS-RTOS 开发工具下载和安装

  • 在 MS-RTOS 云开发平台上完成 msrtos_base_sdk 配置和下载, 配置 MS-RTOS 时,需要手动勾选 libmsdriver、esp_at_net、fatfs、libsddc、mbedtls、cjson 这几个组件,如下图所示:

    avatar
  • 在 IoT studio 上完成 msrtos_base_sdk 工程导入和编译

  • 在 IoT studio 上完成 bspstm32f4xx 工程的下载和导入,如下图所示:

    avatar

工程配置

bspstm32f4xx 配置

Wi-Fi AP 连接模式配置

bspstm32f4xx/src/board/IOT_PI/iot_pi_cfg.h 文件为 bspstm32f4xx 工程的 IoT Pi 开发板的配置头文件,与 IoT Pi 连接 Wi-Fi AP 相关的配置宏如下:

avatar
含义
BSP_ESP8266_AUTO_JOIN自动连接模式,尝试连接上一次连接的 Wi-Fi AP
BSP_ESP8266_SMART_CFGSmartConfig 模式
BSP_ESP8266_MANUAL_CFG手动连接模式,连接 ap_list[] 中指定的 Wi-Fi AP
BSP_CFG_ESP8266_MODE连接模式, 以上三种连接模式的组合,默认模式为自动连接模式

Wi-Fi AP 列表配置

bspstm32f4xx/src/board/IOT_PI/iot_pi_init.c 为 IoT Pi 开发板的初始化源文件,在此源文件中的 ap_list[] 变量用于指定手动连接模式下尝试连接到的 Wi-Fi AP 列表:

/**
 * WiFi AP list.
 */
static const ms_esp_at_net_ap_t ap_list[] = {
    { "EOS-00000F",    "123456789" }, // Spirit 1 的 Wi-Fi AP SSID 与密码 
};

libsddc 配置

msrtos_base_sdk/libsddc/src/sddc_config.h 文件为 libsddc 工程的配置头文件,内容如下:

#define SDDC_CFG_PORT              680U            // EdgerOS 端口
#define SDDC_CFG_RECV_BUF_SIZE     1460U           // 接收缓冲区大小
#define SDDC_CFG_SEND_BUF_SIZE     1460U           // 发送缓冲区大小

#define SDDC_CFG_NET_IMPL          "ms_esp_at_net" // MS-RTOS 网络实现名字

#define SDDC_CFG_MQUEUE_SIZE       6U              // 消息队列大小
#define SDDC_CFG_RETRIES_INTERVAL  500U            // 消息重传时间间隔,单位:毫秒
#define SDDC_CFG_EDGEROS_ALIVE     40U             // EdgerOS 保活时间,单位:消息重传时间间隔
#define SDDC_CFG_CONNECTOR_TIMEOUT 5000U           // 数据连接器接收超时时间,单位:毫秒

#define SDDC_CFG_DBG_EN            1U              // 是否使能调试信息
#define SDDC_CFG_WARN_EN           1U              // 是否使能警告信息
#define SDDC_CFG_ERR_EN            1U              // 是否使能出错信息
#define SDDC_CFG_CRIT_EN           1U              // 是否使能临界信息
#define SDDC_CFG_INFO_EN           1U              // 是否使能打印信息

#define SDDC_CFG_SECURITY_EN       1U              // 是否使能数据加密通信

#undef __FREERTOS__                                // 使用 FreeRTOS 时定义

工程编译

bspstm32f4xx 编译

选中 bspstm32f4xx 工程,点击编译按钮,将编译 bspstm32f4xx 工程,编译完成后,会在 Debug 目录生成 bspiotpi.bin 文件:

avatar

libsddc 编译

选中 msrtos_base_sdk 工程,点击编译按钮,将编译 msrtos_base_sdk 工程的组件,编译完成后,会在 libsddc/Debug/sddc_examples/src/example/demo 目录生成 sddc_demo.bin 文件:

avatar

验证功能

烧写镜像

使用 MS-RTOS AutoTester 烧写镜像,请参考《IoT Pi 快速入门》完成 bspiotpi.binsddc_demo.bin 镜像烧写,注意不同的镜像需要烧写到不同的地址,如下表所示:

镜像烧写地址
bspiotpi.bin0x08000000
sddc_demo.bin0x08040000

IoT Pi 连接 EdgerOS

按下 IoT Pi 开发板的 RESET 按键,MS-RTOS 操作系统启动后,将自动运行 0x08040000 地址处的 SDDC demo 程序:

avatar

如果在前面配置 bspstm32f4xx 工程时,将 Wi-Fi AP 连接模式配置为手动连接模式,并配置了正确的 Spirit 1 的 Wi-Fi AP SSID 和密码,IoT Pi 开发板将能成功连接 Spirit 1 的 Wi-Fi 网络,接下来请参考《SDDC introduction》章节的“添加设备流程”去添加 IoT Pi 开发板。

IoT Pi SmartConfig

如果在前面配置 bspstm32f4xx 工程时,未配置正确的 Spirit 1 的 Wi-Fi AP SSID 和密码,或在后续使用过程中修改了 Spirit 1 的 Wi-Fi AP SSID 或密码,则可以通过 SmartConfig 方式令 IoT Pi 开发板重新加入 Spirit 1 的 Wi-Fi 网络,操作方式请参考《SDDC introduction》章节的“SmartConfig 流程”。

EdgerOS 安装 IoT Pi App

  1. 打开爱智世界:

    avatar

  2. 搜索 IoT Pi App:

    avatar

    点击获取安装 IoT Pi App。

  3. 安装 IoT Pi App:

    等待 IoT Pi App 下载和安装完成。

    avatar

给 IoT Pi App 分配设备权限

  1. 打开设置:

    avatar

  2. 点击隐私设置:

    avatar

  3. 点击设备权限:

    avatar

  4. 选择 IoT Pi 开发板:

    avatar

  5. 给 IoT Pi App 分配设备权限:

    avatar

  6. 退出设置。

使用 IoT Pi App 控制 IoT Pi 开发板

  1. 打开 IoT Pi App:

    avatar

    点击 IoT Pi 开发板,进入 IoT Pi 控制界面。

  2. 控制 IoT Pi 开发板:

    avatar

libsddc demo 代码解析

libsddc demo 代码位于 msrtos_base_sdk/libsddc/src/example/demo/sddc_demo.c

#include <ms_rtos.h>
#include "sddc.h"
#include "cJSON.h"

static int led1_fd;    // LED1 文件描述符
static int led2_fd;    // LED2 文件描述符
static int led3_fd;    // LED3 文件描述符

static int key1_fd;    // KEY1 文件描述符
static int key2_fd;    // KEY2 文件描述符
static int key3_fd;    // KEY3 文件描述符

static int sockfd;     // socket 文件描述符

static sddc_bool_t  led_state_bak[3]; // LED 状态备份

// 报告 IoT Pi LED 状态
static void  iot_pi_led_state_report(sddc_t *sddc, const uint8_t *uid, sddc_bool_t  *led_state)
{
    cJSON *root;
    char *str;

    // 构建 LED 状态消息
    root = cJSON_CreateObject();
    sddc_return_value_if_fail(root, -1);
    if (led_state != MS_NULL) {
        cJSON_AddBoolToObject(root, "led1", led_state[0]);
        cJSON_AddBoolToObject(root, "led2", led_state[1]);
        cJSON_AddBoolToObject(root, "led3", led_state[2]);

    } else {
        cJSON_AddBoolToObject(root, "led1", led_state_bak[0]);
        cJSON_AddBoolToObject(root, "led2", led_state_bak[1]);
        cJSON_AddBoolToObject(root, "led3", led_state_bak[2]);
    }

    str = cJSON_Print(root);
    sddc_goto_error_if_fail(str);

    // 发送 LED 状态消息
    sddc_send_message(sddc, uid, str, strlen(str), 1, MS_FALSE, MS_NULL);
    cJSON_free(str);

    cJSON_Delete(root);

    return 0;

error:
    cJSON_Delete(root);

    return -1;
}

// 处理 MESSAGE 报文
static sddc_bool_t  iot_pi_on_message(sddc_t *sddc, const uint8_t *uid, const char *message, ms_size_t len)
{
    // 分析消息
    cJSON *root = cJSON_Parse(message);
    cJSON *led;
    sddc_bool_t  led_state[3];
    char *str;

    sddc_return_value_if_fail(root, SDDC_TRUE);
    memcpy(led_state, led_state_bak, sizeof(led_state_bak));

    // 打印消息
    str = cJSON_Print(root);
    sddc_goto_error_if_fail(str);

    sddc_printf("iot_pi_on_message: %s\n", str);
    cJSON_free(str);

    led = cJSON_GetObjectItem(root, "led1");
    if (cJSON_IsBool(led)) {
        // 控制 LED1
        if (cJSON_IsTrue(led)) {
            ms_uint8_t on = 0;
            ms_io_write(led1_fd, &on, 1);
            led_state[0] = 1;
        } else {
            ms_uint8_t off = 1;
            ms_io_write(led1_fd, &off, 1);
            led_state[0] = 0;
        }
    }

    led = cJSON_GetObjectItem(root, "led2");
    if (cJSON_IsBool(led)) {
        // 控制 LED2
        if (cJSON_IsTrue(led)) {
            ms_uint8_t on = 0;
            ms_io_write(led2_fd, &on, 1);
            led_state[1] = 1;
        } else {
            ms_uint8_t off = 1;
            ms_io_write(led2_fd, &off, 1);
            led_state[1] = 0;
        }
    }

    led = cJSON_GetObjectItem(root, "led3");
    if (cJSON_IsBool(led)) {
        // 控制 LED3
        if (cJSON_IsTrue(led)) {
            ms_uint8_t on = 0;
            ms_io_write(led3_fd, &on, 1);
            led_state[2] = 1;
        } else {
            ms_uint8_t off = 1;
            ms_io_write(led3_fd, &off, 1);
            led_state[2] = 0;
        }
    }

    // 报告 IoT Pi LED 状态
    iot_pi_led_state_report(sddc, uid, led_state);
    memcpy(led_state_bak, led_state, sizeof(led_state_bak));

error:
    cJSON_Delete(root);

    return MS_TRUE;
}

// 处理 MESSAGE ACK
static void iot_pi_on_message_ack(sddc_t *sddc, const uint8_t *uid, uint16_t  seqno)
{
}

// 处理 MESSAGE 丢失
static void iot_pi_on_message_lost(sddc_t *sddc, const uint8_t *uid, uint16_t  seqno)
{
}

// 处理 EdgerOS 失联
static void iot_pi_on_edgeros_lost(sddc_t *sddc, const uint8_t *uid)
{
}

// 处理 UPDATE 报文
static sddc_bool_t  iot_pi_on_update(sddc_t *sddc, const uint8_t *uid, const char *update_data, ms_size_t len)
{
    cJSON *root = cJSON_Parse(udpate_data);
    char *str;

    sddc_return_value_if_fail(root, SDDC_FALSE);

    // 分析 UPDATE 报文
    str = cJSON_Print(root);
    sddc_goto_error_if_fail(str);

    // 打印 UPDATE 报文
    sddc_printf("iot_pi_on_update: %s\n", str);
    cJSON_free(str);
    cJSON_Delete(root);

    return SDDC_TRUE;

error:
    cJSON_Delete(root);

    return SDDC_FALSE;
}

// 处理 INVITE 报文
static sddc_bool_t  iot_pi_on_invite(sddc_t *sddc, const uint8_t *uid, const char *invite_data, size_t len)
{
    cJSON *root = cJSON_Parse(invite_data);
    char *str;

    sddc_return_value_if_fail(root, SDDC_FALSE);

    // 分析 INVITE 报文
    str = cJSON_Print(root);
    sddc_goto_error_if_fail(str);

    // 打印 INVITE 报文
    sddc_printf("iot_pi_on_invite: %s\n", str);
    cJSON_free(str);
    cJSON_Delete(root);

    return SDDC_TRUE;

error:
    cJSON_Delete(root);

    return SDDC_FALSE;
}

// 处理 INVITE 结束
static sddc_bool_t  iot_pi_on_invite_end(sddc_t *sddc, const uint8_t *uid)
{
    // 报告 IoT Pi LED 状态
    iot_pi_led_state_report(sddc, uid, MS_NULL);

    return MS_TRUE;
}

// 创建 REPORT 数据
static char *iot_pi_report_data_create(void)
{
    cJSON *root;
    cJSON *report;
    char *str;

    root = cJSON_CreateObject();
    sddc_return_value_if_fail(root, NULL);

    report = cJSON_CreateObject();
    sddc_return_value_if_fail(report, NULL);

    cJSON_AddItemToObject(root, "report", report);
    cJSON_AddStringToObject(report, "name",   "IoT Pi");
    cJSON_AddStringToObject(report, "type",   "device");
    cJSON_AddBoolToObject(report,   "excl",   SDDC_FALSE);
    cJSON_AddStringToObject(report, "desc",   "翼辉 IoT Pi");
    cJSON_AddStringToObject(report, "model",  "1");
    cJSON_AddStringToObject(report, "vendor", "ACOINFO");
    /*
     * Add extension here
     */

    str = cJSON_Print(root);
    sddc_return_value_if_fail(str, NULL);

    sddc_printf("REPORT DATA: %s\n", str);

    cJSON_Delete(root);

    return str;
}

// 创建 INVITE 数据
static char *iot_pi_invite_data_create(void)
{
    cJSON *root;
    cJSON *report;
    char *str;

    root = cJSON_CreateObject();
    sddc_return_value_if_fail(root, NULL);

    report = cJSON_CreateObject();
    sddc_return_value_if_fail(report, NULL);

    cJSON_AddItemToObject(root, "report", report);
    cJSON_AddStringToObject(report, "name",   "IoT Pi");
    cJSON_AddStringToObject(report, "type",   "device");
    cJSON_AddBoolToObject(report,   "excl",   SDDC_FALSE);
    cJSON_AddStringToObject(report, "desc",   "翼辉 IoT Pi");
    cJSON_AddStringToObject(report, "model",  "1");
    cJSON_AddStringToObject(report, "vendor", "ACOINFO");
    /*
     * Add extension here
     */

    str = cJSON_Print(root);
    sddc_return_value_if_fail(str, NULL);

    sddc_printf("INVITE DATA: %s\n", str);

    cJSON_Delete(root);

    return str;
}

// IoT Pi 按键扫描线程
static void iot_pi_key_thread(ms_ptr_t arg)
{
    fd_set  rfds;
    sddc_t *sddc = arg;
    ms_uint8_t key1_press = 0;
    ms_uint64_t key1_press_begin = 0;

    while (1) {
        // 等按键按下
        FD_ZERO(&rfds);
        FD_SET(key1_fd, &rfds);
        FD_SET(key2_fd, &rfds);
        FD_SET(key3_fd, &rfds);

        if (select(key3_fd + 1, &rfds, MS_NULL, MS_NULL, MS_NULL) > 0) {
            cJSON *root;
            char *str;

            root = cJSON_CreateObject();
            sddc_return_if_fail(root);

            if (FD_ISSET(key1_fd, &rfds)) {
                // KEY1 按下
                key1_press++;
                if (key1_press == 1) {
                    // 记录第一次按下的时间
                    key1_press_begin = ms_time_get_ms();

                } else if (key1_press == 3) {
                    // 按下了三次
                    key1_press = 0;

                    // 三个按下的时间小于 800MS
                    if ((ms_time_get_ms() - key1_press_begin) < 800) {
                        static struct ifreq ifreq;

                        ifreq.ifr_flags = !ifreq.ifr_flags;

                        if (ifreq.ifr_flags) {
                            // 启动 SmartConfig
                            sddc_printf("Start smart configure...\n");
                        } else {
                            // 停止 SmartConfig
                            sddc_printf("Stop smart configure...\n");
                        }

                        // 操作网卡
                        ioctl(sockfd, SIOCSIFPFLAGS, &ifreq);
                        continue;
                    }
                }

                cJSON_AddBoolToObject(root, "key1", MS_TRUE);

                // 反转 LED1
                ms_io_write(led1_fd, &led_state_bak[0], 1);
                led_state_bak[0] = !led_state_bak[0];
                cJSON_AddBoolToObject(root, "led1", led_state_bak[0]);
            }

            if (FD_ISSET(key2_fd, &rfds)) {
                // KEY2 按下
                cJSON_AddBoolToObject(root, "key2", MS_TRUE);

                // 反转 LED2
                ms_io_write(led2_fd, &led_state_bak[1], 1);
                led_state_bak[1] = !led_state_bak[1];
                cJSON_AddBoolToObject(root, "led2", led_state_bak[1]);

                key1_press = 0;
            }

            if (FD_ISSET(key3_fd, &rfds)) {
                // KEY3 按下

                cJSON_AddBoolToObject(root, "key3", MS_TRUE);

                // 反转 LED3
                ms_io_write(led3_fd, &led_state_bak[2], 1);
                led_state_bak[2] = !led_state_bak[2];
                cJSON_AddBoolToObject(root, "led3", led_state_bak[2]);

                key1_press = 0;
            }

            str = cJSON_Print(root);
            sddc_return_if_fail(str);
            // 给连接的 EdgerOS 广播 MESSAGE
            sddc_broadcast_message(sddc, str, strlen(str), 1, MS_FALSE, MS_NULL);
            cJSON_free(str);

            cJSON_Delete(root);
        }
    }
}

// 初始化 IoT Pi 的 LED
static int iot_pi_led_init(void)
{
    ms_gpio_param_t param;

    // 打开 LED1
    led1_fd = ms_io_open("/dev/led1", O_WRONLY, 0666);
    sddc_return_value_if_fail(led1_fd >= 0, -1);

    // 打开 LED2
    led2_fd = ms_io_open("/dev/led2", O_WRONLY, 0666);
    sddc_return_value_if_fail(led2_fd >= 0, -1);

    // 打开 LED3
    led3_fd = ms_io_open("/dev/led3", O_WRONLY, 0666);
    sddc_return_value_if_fail(led3_fd >= 0, -1);

    // 设置 LED 的 GPIO 模式
    param.mode  = MS_GPIO_MODE_OUTPUT_PP;
    param.pull  = MS_GPIO_PULL_UP;
    param.speed = MS_GPIO_SPEED_HIGH;
    ms_io_ioctl(led1_fd, MS_GPIO_CMD_SET_PARAM, &param);
    ms_io_ioctl(led2_fd, MS_GPIO_CMD_SET_PARAM, &param);
    ms_io_ioctl(led3_fd, MS_GPIO_CMD_SET_PARAM, &param);

    // 读 LED 状态到备份变量
    ms_io_read(led1_fd, &led_state_bak[0], 1);
    ms_io_read(led2_fd, &led_state_bak[1], 1);
    ms_io_read(led3_fd, &led_state_bak[2], 1);

    led_state_bak[0] = !led_state_bak[0];
    led_state_bak[1] = !led_state_bak[1];
    led_state_bak[2] = !led_state_bak[2];

    return 0;
}

// 初始化 IoT Pi 的 按键
static int iot_pi_key_init(void)
{
    ms_gpio_param_t param;

    // 打开 KEY1
    key1_fd = ms_io_open("/dev/key1", O_WRONLY, 0666);
    sddc_return_value_if_fail(key1_fd >= 0, -1);

    // 打开 KEY2
    key2_fd = ms_io_open("/dev/key2", O_WRONLY, 0666);
    sddc_return_value_if_fail(key2_fd >= 0, -1);

    // 打开 KEY3
    key3_fd = ms_io_open("/dev/key3", O_WRONLY, 0666);
    sddc_return_value_if_fail(key3_fd >= 0, -1);

    // 设置 KEY 的 GPIO 模式
    param.mode  = MS_GPIO_MODE_IRQ_FALLING;
    param.pull  = MS_GPIO_PULL_UP;
    param.speed = MS_GPIO_SPEED_HIGH;
    ms_io_ioctl(key1_fd, MS_GPIO_CMD_SET_PARAM, &param);
    ms_io_ioctl(key2_fd, MS_GPIO_CMD_SET_PARAM, &param);
    ms_io_ioctl(key3_fd, MS_GPIO_CMD_SET_PARAM, &param);

    return 0;
}

// 应用入口函数
int main(int argc, char *argv[])
{
    struct ifreq ifreq;
    struct sockaddr_in *psockaddrin = (struct sockaddr_in *)&(ifreq.ifr_addr);
    sddc_t *sddc;
    char *data;
    int ret;

    // 初始化 IoT Pi 的三个 LED 灯
    ret = iot_pi_led_init();
    sddc_return_value_if_fail(ret == 0, -1);

    // 初始化 IoT Pi 的三个 KEY
    ret = iot_pi_key_init();
    sddc_return_value_if_fail(ret == 0, -1);

    // 设置网络实现
#ifdef SDDC_CFG_NET_IMPL
    ret = ms_net_set_impl(SDDC_CFG_NET_IMPL);
    sddc_return_value_if_fail(ret == MS_ERR_NONE, -1);
#endif

    // 创建 SDDC 协议对象
	sddc = sddc_create(SDDC_CFG_PORT);
    sddc_return_value_if_fail(sddc, -1);

    // 设置事件响应函数
    sddc_set_on_message(sddc, iot_pi_on_message);              // 设置接收消息请求时的回调函数
    sddc_set_on_message_ack(sddc, iot_pi_on_message_ack);      // 设置接收消息确认时的回调函数
    sddc_set_on_message_lost(sddc, iot_pi_on_message_lost);    // 设置丢失消息时的回调函数
    sddc_set_on_invite(sddc, iot_pi_on_invite);                // 设置接受邀请请求时的回调函数
    sddc_set_on_invite_end(sddc, iot_pi_on_invite_end);        // 设置发送邀请后的回调函数
    sddc_set_on_update(sddc, iot_pi_on_update);                // 设置接收更新请求时的回调函数
    sddc_set_on_edgeros_lost(sddc, iot_pi_on_edgeros_lost);    // 设置 EdgerOS 断连时的回调函数

     // 设置设备密码
#if SDDC_CFG_SECURITY_EN > 0                                   // SDDC_CFG_SECURITY_EN 宏控制是否支持数据加密通信
    ret = sddc_set_token(sddc, "1234567890");
    sddc_return_value_if_fail(ret == 0, -1);
#endif

    // 创建并设置 Report 报文数据
    data = iot_pi_report_data_create();
    sddc_return_value_if_fail(data, -1);
    sddc_set_report_data(sddc, data, strlen(data));

    // 创建并设置 Invite 报文数据
    data = iot_pi_invite_data_create();
    sddc_return_value_if_fail(data, -1);
    sddc_set_invite_data(sddc, data, strlen(data));

    // 获取并打印网卡 mac 地址
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    sddc_return_value_if_fail(sockfd >= 0, -1);

    ioctl(sockfd, SIOCGIFHWADDR, &ifreq);

    sddc_printf("MAC addr: %02x:%02x:%02x:%02x:%02x:%02x/n",
              (ms_uint8_t)ifreq.ifr_hwaddr.sa_data[0],
              (ms_uint8_t)ifreq.ifr_hwaddr.sa_data[1],
              (ms_uint8_t)ifreq.ifr_hwaddr.sa_data[2],
              (ms_uint8_t)ifreq.ifr_hwaddr.sa_data[3],
              (ms_uint8_t)ifreq.ifr_hwaddr.sa_data[4],
              (ms_uint8_t)ifreq.ifr_hwaddr.sa_data[5]);

    // 使用网卡 mac 地址设置设备唯一标识 UID
    sddc_set_uid(sddc, (const ms_uint8_t *)ifreq.ifr_hwaddr.sa_data);

    // 获取并打印 IP 地址
    if (ioctl(sockfd, SIOCGIFADDR, &ifreq) == 0) {
        char ip[sizeof("255.255.255.255")];

        inet_ntoa_r(psockaddrin->sin_addr, ip, sizeof(ip));

        sddc_printf("IP addr: %s/n", ip);
    } else {
        sddc_printf("Failed to get IP address, Wi-Fi AP not online!/n");
    }

    // 创建按键扫描线程,快速连续按下 KEY 1 按键 三次,IoT Pi 将会进入 SmartConfig 模式
    ret = ms_thread_create("t_key",
                           iot_pi_key_thread,
                           sddc,
                           2048U,
                           30U,
                           70U,
                           MS_THREAD_OPT_USER | MS_THREAD_OPT_REENT_EN,
                           MS_NULL);
    sddc_return_value_if_fail(ret == MS_ERR_NONE, -1);

    // 运行 SDDC 协议循环
    while (1) {
        sddc_printf("SDDC running.../n");

        sddc_run(sddc);

        sddc_printf("SDDC quit!/n");
    }

    // 销毁 SDDC 协议对象
    sddc_destroy(sddc);

    return 0;
}

文档内容是否对您有所帮助?
有帮助
没帮助