IoT Pi CoAP 设备开发

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

IoT Pi CoAP 设备开发

本章将介绍如何在 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 这几个组件,如下图所示:

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

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

    avatar

获取 CoAP 组件

进入 msrtos_base_sdk 根目录,使用 git 工具从 MS-RTOS github 社区open in new window 下载 libcoap组件,命令如下:

git clone https://github.com/ms-rtos/libcoap.git

下载完成后右键 msrtos_base_sdk 工程选择 Refresh 菜单项刷新工程,刷新完成后将出现 libcoap 组件,如下图所示:

avatar

msrtos_base_sdk 根目录下的 Makefile 中添加 libcoap,如下图所示:

avatar

安装 Node CoAP CLI

参考《CoAP 协议介绍》完成 Node CoAP CLI 安装。

工程配置

bspstm32f4xx 配置

配置自启动进程名

bspstm32f4xx/src/board/IOT_PI/iot_pi_init.h 文件中设定了 IoT Pi 开发板开机时会创建进程启用 0x08040000 地址处的程序,修改自启动进程名字,具体代码如下:

ms_process_create("iotpi_coap", (ms_addr_t)0x08040000, 65536, 4096, 9, 0 , 0, MS_NULL, MS_NULL, MS_NULL);

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 与密码 
};

工程编译

libcoap 编译

选中 msrtos_base_sdk 工程,点击 “编译” 按钮,将编译 msrtos_base_sdk 工程的组件,编译完成后,会在 libcoap/Debug/coap_examples/src 目录生成 coap_server_example.bin 文件:

avatar

bspstm32f4xx 编译

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

avatar

验证功能

烧写镜像

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

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

启动 CoAP Server

按下 IoT Pi 开发板的 RESET 按键或者点击 MS-RTOS Auto Tester GO 按钮,MS-RTOS 操作系统启动后,将自动运行 0x08040000 地址处的 CoAP Server 程序,在 MS-RTOS Auto Tester Input 窗口输入 ps 命令查看进程的信息:

avatar

使用 Node CoAP CLI 测试 CoAP Server

  1. 输入 coap get coap://192.168.128.103/test 获取 CoAP Server 的 test 资源(这时没有信息,所以显示 no data),如下图所示:

    avatar

  2. 输入 coap put -p hello_world coap://192.168.128.103/test 修改 CoAP Server 的 test 资源为字符串 hello_world,如下图所示:

    avatar

  3. 输入 coap get coap://192.168.128.103/test 再次获取 CoAP Server 的 test 资源,这时能获取到上一步设置的字符串 hello_world,如下图所示:

    avatar

CoAP Server 代码解析

CoAP Server 代码位于 msrtos_base_sdk/libcoap/src/coap_server_example.c

/* CoAP server Example
   This example code is in the Public Domain (or CC0 licensed, at your option.)
   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>

#include "coap.h"

/* Set this to 9 to get verbose logging from within libcoap */
/* If want to change log level num to open log, don't forget to enlarge coap_example_task size*/
#define COAP_LOGGING_LEVEL 0

// test 资源对应的变量
static char test_data[100];
static int test_data_len = 0;

// test 资源的 GET 请求处理函数
static void
hnd_test_get(coap_context_t *ctx, coap_resource_t *resource,
             coap_session_t *session,
             coap_pdu_t *request, coap_binary_t *token,
             coap_string_t *query, coap_pdu_t *response)
{
    // 将 test_data 变量的内容增加到响应中
    coap_add_data_blocked_response(resource, session, request, response, token,
                                   COAP_MEDIATYPE_TEXT_PLAIN, 0,
                                   (size_t)test_data_len,
                                   (const u_char *)test_data);
}

// test 资源的 PUT 请求处理函数
static void
hnd_test_put(coap_context_t *ctx,
             coap_resource_t *resource,
             coap_session_t *session,
             coap_pdu_t *request,
             coap_binary_t *token,
             coap_string_t *query,
             coap_pdu_t *response)
{
    size_t size;
    unsigned char *data;

    // 通知观察者
    coap_resource_notify_observers(resource, NULL);

    // 设置响应代码
    if (strcmp (test_data, "no data") == 0) {
        response->code = COAP_RESPONSE_CODE(201);
    } else {
        response->code = COAP_RESPONSE_CODE(204);
    }

    // 从请求中获得数据
    (void)coap_get_data(request, &size, &data);

    // 用获得的数据设置 test_data 变量
    if (size == 0) {      /* re-init */
        snprintf(test_data, sizeof(test_data), "no data");
        test_data_len = strlen(test_data);
    } else {
        test_data_len = size > sizeof (test_data) ? sizeof (test_data) : size;
        memcpy (test_data, data, test_data_len);
    }
}

// test 资源的 DELETE 请求处理函数
static void
hnd_test_delete(coap_context_t *ctx,
                  coap_resource_t *resource,
                  coap_session_t *session,
                  coap_pdu_t *request,
                  coap_binary_t *token,
                  coap_string_t *query,
                  coap_pdu_t *response)
{
    // 通知观察者
    coap_resource_notify_observers(resource, NULL);

    // 将 test_data 变量设置为 "no data"
    snprintf(test_data, sizeof(test_data), "no data");
    test_data_len = strlen(test_data);

    // 设置响应代码
    response->code = COAP_RESPONSE_CODE(202);
}

int main(int argc, char **argv)
{
    coap_context_t *ctx = NULL;         // CoAP 协议栈上下文
    coap_address_t serv_addr;           // 多种 sockaddr 的抽象
    coap_resource_t *resource = NULL;   // CoAP 资源指针

    // 初始化 test_data 变量内容
    snprintf(test_data, sizeof(test_data), "no data");
    test_data_len = strlen(test_data);

    // 设置日志级别
    coap_set_log_level(COAP_LOGGING_LEVEL);

    while (1) {
        coap_endpoint_t *ep_udp = NULL;
        coap_endpoint_t *ep_tcp = NULL;
        unsigned wait_ms;

        // 创建一个新的 CoAP 上下文
        ctx = coap_new_context(NULL);
        if (!ctx) {
           continue;
        }

        // 准备 CoAP 服务器套接字
        coap_address_init(&serv_addr);
        serv_addr.addr.sin.sin_family      = AF_INET;
        serv_addr.addr.sin.sin_addr.s_addr = INADDR_ANY;
        serv_addr.addr.sin.sin_port        = htons(COAP_DEFAULT_PORT);
        // 创建 UDP 通信端点
        ep_udp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP); //Add IPv4 endpoint
        if (!ep_udp) {
           goto clean_up;
        }
        // 创建 TCP 通信端点
        ep_tcp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP);
        if (!ep_tcp) {
           goto clean_up;
        }

        // 初始化 test 资源
        resource = coap_resource_init(coap_make_str_const("test"), 0);
        if (!resource) {
           goto clean_up;
        }
		// 设置 test 资源的 GET 请求处理函数
        coap_register_handler(resource, COAP_REQUEST_GET, hnd_test_get);     
		// 设置 test 资源的 PUT 请求处理函数
        coap_register_handler(resource, COAP_REQUEST_PUT, hnd_test_put);     
		// 设置 test 资源的 DELETE 请求处理函数
        coap_register_handler(resource, COAP_REQUEST_DELETE, hnd_test_delete);  
        // 设置 test 资源是否可观察
        //(如果资源是可观察的,并且客户端在请求包中设置了 COAP_OPTION_OBSERVE, 
        // 当资源的状态发生变化时,调用 coap_resource_notify_observers(),
        // 将发送一个 Observer 响应)
        coap_resource_set_get_observable(resource, 1);

        // CoAP 上下文加入 test 资源
        coap_add_resource(ctx, resource);

        // 设置等待时间
        wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;

        while (1) {
            // CoAP 协议栈 IO 处理,传入等待时间,返回前消耗的时间
            int result = coap_io_process(ctx, wait_ms);
            if (result < 0) {
                // 出错
                break;
            } else if (result && (unsigned)result < wait_ms) {
                // 如果消耗时间少于等待时间,则修改等待时间 wait_ms
                wait_ms -= result;
            }
            if (result) {
                // 如果消耗时间大于等于等待时间,则重置等待时间 wait_ms
                wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
            }
        }
    }
clean_up:
    // 释放 CoAP 上下文
    coap_free_context(ctx);
    // 清理 libcoap
    coap_cleanup();
    return 0;
}

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