翻译自:https://blog.espressif.com/extending-esp-rainmaker-with-services-9ccb82dfb9b7

如果您一直关注乐鑫新闻和博客,应该对 ESP RainMaker 及其丰富的功能有所了解。若您对 ESP RainMaker 尚不了解,建议先点此了解相关信息,以便更好地理解本文内容。


ESP RainMaker 的重要特性之一是轻量化。它能够充当 ESP 节点 (Node) 和移动端 App、Alexa、GVA(谷歌语音助手)等客户端之间的“隧道”,所以具备极大的灵活性与可扩展性。ESP RainMaker 支持您创建任意设备,开发多种设备功能,并最终通过移动端 App 实现设备控制。当然,我们预定义了一些默认类作为参考,您可以对其进行自定义更改,创建自己的设备和参数类型。

ESP RainMaker 节点 (Node) 所支持的功能是通过节点配置 (node configuration) 标记的,分为两个部分:

  1. 设备 (Device):指用户可视化、可交互的功能,例如设备的开关及灯的亮度控制。
  2. 服务 (Service):主要包括底层功能,例如恢复出厂设置、重启设备、时区信息、定时等。

大多数 ESP RainMaker 开发者已在例程中了解并熟悉了设备 (Device) 的概念,而对服务 (Service) 较为陌生。它往往隐藏在 esp_rmaker_system_service_enable()esp_rmaker_schedule_enable()esp_rmaker_timezone_service_enable() 等 API 中,可以被移动端 App 调用。

服务 (Service) 的结构与设备 (Device) 类似,都由名称 (name)、类型 (type) 和一系列的参数构成。因此,所有适用于设备 (Device) 的 API(包含固件 API 和云 API)也都适用于服务 (Service);同理,适用于设备 (Device) 的 GET /user/nodes?node_details=trueGET /user/nodes/config 和 GET/PUT /user/nodes/params API 也同样适用于服务 (Service)。

我们预先定义了一些标准服务 (Service) 例程,您可以点此直接获取。接下来,我们将进一步指导您如何添加自定义服务 (Service)。

定义用例

在创建服务 (Service) 之前,您需要先定义用例,以便于后续定义相应服务 (Service) 的参数。例如“诊断”服务 (Service),就可以让开发者在移动端 App 开启节点 (Node) 诊断功能并获取诊断数据。

创建服务(Service)

创建服务 (Service) 的最基本代码段包含以下 4 个部分:

  1. 创建服务 (Service)
  2. 注册回调
  3. 创建添加适用的参数
  4. 将服务 (Service) 添加至节点 (Node)
/* Create the service using esp_rmaker_service_create(). However, note that a service uses esp_rmaker_device_t
 * as the data type, since it is structurally same as a device.
 */
esp_rmaker_device_t *diag_service = esp_rmaker_service_create("Diagnostics","my.service.diag", NULL);

/* Register the write callback. Read callback would normally be NULL */
esp_rmaker_device_add_cb(diag_service, diag_write_cb, NULL);
 
/* Create and add paramaters of various types as applicable.
 * Parameter types (like my.param.diag-trigger) are not mandatory, but useful to have.
 */
esp_rmaker_device_add_param(diag_service, esp_rmaker_param_create("Trigger", "my.param.diag-trigger", esp_rmaker_bool(false), PROP_FLAG_WRITE));
esp_rmaker_device_add_param(diag_service, esp_rmaker_param_create("Timestamp", "my.param.diag-timestamp", esp_rmaker_int(0), PROP_FLAG_READ));
esp_rmaker_device_add_param(diag_service, esp_rmaker_param_create("Data", "my.param.diag-data", esp_rmaker_obj("{}"), PROP_FLAG_READ));

/* Add the service to the node */
esp_rmaker_node_add_device(node, diag_service);

如果诊断参数的数量较少,我们可以分别定义整型/布尔型/浮点型/字符串类型的参数(如上述“Timestamp”)。但是如果参数数量较多,则推荐直接使用“对象 (Object)”类(如上述“Data”),然后在里面写入所有的 JSON 参数对象。

注意,这里提到的参数并非必要参数,仅供参考。

由于本文展示的“诊断”服务 (Service) 参数类型较多,我们直接使用了对象 (Object) 类。该服务 (Service) 将在节点配置 (Node Configuration) 中显示为服务数组 (Service Array),如下所示:

同理,节点 (Node) 参数对象 (Object) 将如下所示:

处理服务回调 (Service Callback)

在上文的代码中,我们已经将 diag_write_cb  定义为服务 (Service) 的写回调函数。实现过程如下:

#include <json_generator.h>
static esp_err_t diag_write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
         const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
{
    /* This ctx check is just to find if the request was received via Cloud, Local network or Schedule.
     * Having this is not required, but there could be some cases wherein specific operations may be allowed
     * only via specific channels (like only Local network), where this would be useful.
     */
    if (ctx) {
        ESP_LOGI(TAG, "Received write request via : %s", esp_rmaker_device_cb_src_to_str(ctx->src));
    }
  
    /* Check if the write is on the "Trigger" parameter. We aren't really checking true/false as that
     * is not much of a concern in this context. But you can add checks on the values too.
     */
    if (strcmp(esp_rmaker_param_get_name(param), "Trigger") == 0) {
        /* Here we start some dummy diagnostics and populate the appropriate values to be passed
         * to "Timestamp" and "Data".
         */
        ESP_LOGI(TAG, "Starting Diagnostics");
        time_t current_timestamp = 0;
        time(&current_timestamp);
        char buf[100] = {0};
        json_gen_str_t jstr;
        json_gen_str_start(&jstr, buf, sizeof(buf), NULL, NULL);
        json_gen_start_object(&jstr);
        json_gen_obj_set_bool(&jstr, "diag1", true);
        json_gen_obj_set_int(&jstr, "diag2", 30);
        json_gen_obj_set_float(&jstr, "diag3", 54.1643);
        json_gen_obj_set_string(&jstr, "diag4", "diag");
        json_gen_end_object(&jstr);
        json_gen_str_end(&jstr);
 
        /* The values are reported by updating appropriate parameters */
        esp_rmaker_param_update_and_report(esp_rmaker_device_get_param_by_name(device, "Data"),
                    esp_rmaker_obj(buf));
        esp_rmaker_param_update_and_report(esp_rmaker_device_get_param_by_name(device, "Timestamp"),
                esp_rmaker_int((int)current_timestamp));
    }
    return ESP_OK;
}

可以看到,用单个参数对象上报 4 种不同类别、不同数据类型的参数是优于分别上报的,可以避免使节点配置过于臃肿的问题。需要说明的是,上面展示了在 ESP RainMaker 中对 JSON Generator 库应用的方法作为示例,您完全可以选择任何库或函数来创建对象 (Object)。

对服务 (Service) 进行测试

在使用移动端 App 调用服务 (Service) 之前,建议您先通过 RainMaker CLI 对其进行测试。在完成 CLI 配置后,使用以下命令即可启动诊断:

$ ./rainmaker.py setparams --data '{"Diagnostics":{"Trigger":true}}' <node_id>

设备收到命令后,会立即在串行控制台上输出如下结果:

I (74726) esp_rmaker_param: Received params: {"Diagnostics": {"Trigger": true}}
I (74726) app_main: Received write request via : Cloud
I (74726) app_main: Starting Diagnostics
I (74736) esp_rmaker_param: Reporting params: {"Diagnostics":{"Data":{"diag1":true,"diag2":30,"diag3":54.16430,"diag4":"diag"}}}
I (74746) esp_rmaker_param: Reporting params: {"Diagnostics":{"Timestamp":1639738352}}

接下来,您也可以使用命令查询节点 (Node) 的参数:

$ ./rainmaker.py getparams <node_id>

并在控制台上获取结果:


以上是 ESP RainMaker 添加服务 (Service) 的基本流程。您无需在云后台进行任何配置或更改,就可以轻松添加自定义功能,拥有极大的灵活性与可扩展性。

这里是一些可供参考的服务 (Service) 示例,您可以基于此熟悉并在自己的应用中添加自定义服务 (Service):


敬请关注乐鑫新闻博客,及时获取 ESP RainMaker 的最新动态。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐