乐鑫 Rainmaker 轻松入门指南

前言

  1. 个人邮箱:zhangyixu02@gmail.com

手机端操作

  1. 我们手机端需要在如下软件下载安装包,或者应用商城中搜索 ESP RainMaker。

安卓:

  • https://github.com/espressif/esp-rainmaker-android
  • https://play.google.com/store/apps/details?id=com.espressif.rainmaker
    iOS:
  • https://github.com/espressif/esp-rainmaker-ios
  • https://apps.apple.com/us/app/esp-rainmaker/id1497491540
    1. 安卓用户在 github 中点击下框部分


    3. 点击 apk 文件下载,后续传到手机中安装即可。

    ESP32 操作

    创建一个新工程

    1. 在 VScode 中输入 Ctrl + Shift + P ,然后输入 Example 打开例程。


    2. 点击 sample_project 创建一个项目。

    拉取 rainmaker 仓库

    git clone --recursive https://github.com/espressif/esp-rainmaker.git
    

    修改配置

    1. 进入 main 文件夹,添加 idf_component.yml 文件
    vim idf_component.yml
    
    1. 在该文件中包含内容。The ESP Component Registry 搜 rainmaker。
    dependencies:
      espressif/esp_rainmaker: "^1.3.0"
    
    1. 修改分区表。需要注意,esp_secure_cert 和 fctry 这两个分区是 Rainmaker 的必要条件。其他内容你可以根据自己需求来设定。
    vim partitions.csv
    
    # Name,   Type, SubType, Offset,  Size, Flags
    # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
    nvs,              data, nvs,     0x9000,  24K,
    phy_init,         data, phy,     0xf000,  4K,
    factory,          app,  factory, 0x10000, 2500K,
    esp_secure_cert,  0x3F, ,        ,        0x2000, encrypted
    fctry,            data, nvs,     ,        0x6000,
    
    1. 修改 menuconfig,创建 sdkconfig.defaults 文件,并且删除 sdkconfig 和 sdkconfig.old,之后执行 idf.py menuconfig 即可完成配置。
    rm sdkconfig sdkconfig.old
    vim sdkconfig.defaults
    idf.py menuconfig
    


    5. 将上面拉取的 rainmaker 仓库中的两个文件复制到当前工程根目录。注意,新老版本的的文件名会不一样,根据自己的版本选择其一即可。

    mkdir components
    # 老版本是叫 app_wifi
    cp ${esp-rainmaker}/examples/common/app_wifi ./components -r 
    # 新版本是叫 app_network
    cp ${esp-rainmaker}/examples/common/app_network ./components -r 
    cp ${esp-rainmaker}/examples/common/qrcode ./components -r 
    
    1. 进入根目录的 CMakeList.txt 文件,加入颜色编译。方便我们 debug。
    # For more information about build system see
    # https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
    # The following five lines of boilerplate have to be in your project's
    # CMakeLists in this exact order for cmake to work correctly
    cmake_minimum_required(VERSION 3.16)
    
    include($ENV{IDF_PATH}/tools/cmake/project.cmake)
    add_compile_options(-fdiagnostics-color=always)
    project(main)
    
    

    烧录证书

    1. 像 esp32-c2 是需要烧录证书的,如果是esp32-c3无需。
    2. 如果出现如下报错,说明当前设备没有证书,需要进行烧录。
    E (918) esp_rmaker_core: Failed to initialise Node Id. Please perform "claiming" using RainMaker CLI.
    E (929) esp-toothbrush: Could not initialise node. Aborting!!!
    
    1. 我们可以执行如下命令生成证书。需要注意, –addr 后面加的地址是前面配置分区表时的 fctry 起始地址。如果不知道 fctry 起始地址是多少,可以执行 idf.py partition-table 获取。
    # 生成证书命令
    python3 ${esp-rainmaker}/cli/rainmaker.py claim /dev/ttyUSB0 --platform esp32-c2 --mac AABBCCDDEE22 --addr 0x283000
    
    # 编译分区表命令
    idf.py partition-table
    # 最终生成的内容
    
    *******************************************************************************
    # ESP-IDF Partition Table
    # Name, Type, SubType, Offset, Size, Flags
    nvs,data,nvs,0x9000,24K,
    phy_init,data,phy,0xf000,4K,
    factory,app,factory,0x10000,2500K,
    esp_secure_cert,63,6,0x281000,8K,encrypted
    fctry,data,nvs,0x283000,24K,
    *******************************************************************************
    
    1. 烧录证书。write_flash 后接 fctry 起始地址
    esptool.py --chip esp32c2 --port /dev/ttyUSB0 write_flash 0x283000 ~/.espressif/rainmaker/claim_data/d5bda8f5-9b16-4988-ae7c-0a5966cbe58f/AABBCCDDEE22/AABBCCDDEE22.bin
    

    Rainmaker 初始化

    1. 如下为一个 Rainmaker 的初始化代码。
    static const char *valid_strs[] = {"Gentle Mode","Standard Mode","Strong Mode","Deep Clean Mode"};
    
    static void event_handler(void *arg, esp_event_base_t event_base,
                              int32_t event_id, void *event_data)
    {
        if (event_base != RMAKER_COMMON_EVENT) {
            return;
        }
        switch (event_id) {
        case RMAKER_MQTT_EVENT_CONNECTED:
            ESP_LOGI(TAG, "RMAKER connected");
            break;
        case RMAKER_MQTT_EVENT_DISCONNECTED:
            ESP_LOGI(TAG, "RMAKER disconnected");
            break;
        default:
            break;
        }
    }
    
    esp_rmaker_device_t* rainmaker_init(esp_rmaker_device_write_cb_t write_cb)
    {
        /* 初始化 Wi-Fi, 注意该功能应该在 esp_rmaker_init() 之前被调用 */
        app_wifi_init();
    
        /* 初始化 ESP RainMaker 代理
         * 首先应该调用 app_wifi_init(), 接着调用该函数, 最后调用 app_wifi_start()
         */
        esp_rmaker_config_t rainmaker_cfg = {
            .enable_time_sync = false,
        };
        /* 初始化一个设备节点,该节点名为 esp-toothbrush 的设备,类型为 toothbrush */    
        esp_rmaker_node_t *node = esp_rmaker_node_init(&rainmaker_cfg, "esp-toothbrush", "toothbrush");
        if (!node) {
            ESP_LOGE(TAG, "Could not initialise node. Aborting!!!");
            vTaskDelay(5000/portTICK_PERIOD_MS);
            abort();
        }
    
        /* 创建一个名为 esp-toothbrush 的设备,设备类型为 Other */
       esp_rmaker_device_t *g_rainmaker_device = esp_rmaker_device_create("esp-toothbrush", ESP_RMAKER_DEVICE_OTHER, NULL);
        /* 添加回调函数 */
        esp_rmaker_device_add_cb(g_rainmaker_device, write_cb, NULL);
        /* 创建 name 参数 */
        esp_rmaker_device_add_param(g_rainmaker_device, esp_rmaker_name_param_create("name", "esp-toothbrush"));
    
        esp_rmaker_param_t *power_param = esp_rmaker_param_create("Battery Level", NULL, esp_rmaker_str("100 %"), PROP_FLAG_READ);
        esp_rmaker_param_add_ui_type(power_param, ESP_RMAKER_UI_TEXT);
        esp_rmaker_device_add_param(g_rainmaker_device, power_param);
        esp_rmaker_device_assign_primary_param(g_rainmaker_device, power_param);
    
        /* 增加一个 Brushing Mode 参数, 值初始化为 "Gentle Mode" */
        esp_rmaker_param_t *vibration_param = esp_rmaker_param_create("Brushing Mode", NULL, esp_rmaker_str("Gentle Mode"), PROP_FLAG_READ | PROP_FLAG_WRITE);
        esp_rmaker_param_add_valid_str_list(vibration_param, valid_strs, 4);
        esp_rmaker_param_add_ui_type(vibration_param, ESP_RMAKER_UI_DROPDOWN);
        esp_rmaker_device_add_param(g_rainmaker_device, vibration_param);
    
        esp_rmaker_param_t *battery_param = esp_rmaker_param_create("state of charge", ESP_RMAKER_PARAM_POWER, esp_rmaker_bool(false), PROP_FLAG_READ);
        esp_rmaker_param_add_ui_type(battery_param, ESP_RMAKER_UI_TOGGLE);
        esp_rmaker_device_add_param(g_rainmaker_device, battery_param);
    
        esp_rmaker_param_t *brush_time = esp_rmaker_param_create("Set brush time(s)", NULL, esp_rmaker_int(180), PROP_FLAG_WRITE);
        esp_rmaker_param_add_ui_type(power_param, ESP_RMAKER_UI_TEXT);
        esp_rmaker_device_add_param(g_rainmaker_device, brush_time);
    
        esp_rmaker_param_t *medication_record_one = esp_rmaker_param_create("Brushing time(s)", NULL, esp_rmaker_float(0.0), PROP_FLAG_READ | PROP_FLAG_TIME_SERIES);
        esp_rmaker_device_add_param(g_rainmaker_device, medication_record_one);
        
        /* 将当前设备添加进入节点 */
        esp_rmaker_node_add_device(node, g_rainmaker_device);
    
        /* 在系统事件循环中创建一个事件处理程序
         * 该事件处理程序用于判断 Rainmaker 是否连接成功
         */
        ESP_ERROR_CHECK(esp_event_handler_register(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    
        /* 使能 ESP Rainmaker 代理 */
        esp_rmaker_start();
    
        /* 启动 Wi-Fi */
        esp_err_t err = app_wifi_start(RAINMAKER_NAME,RAINMAKER_POP);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Could not start Wifi. Aborting!!!");
            vTaskDelay(5000/portTICK_PERIOD_MS);
            nvs_flash_erase();
            abort();
        }
    
    	ESP_LOGI(TAG, "app_wifi_start finish\r\n");
        return g_rainmaker_device;
    }
    
    1. 最终的显示效果如下:

    Rainmaker 常用 API 说明

    创建节点

    1. 该函数用于创建一个 Rainmaker 节点。需要先调用该函数,才能够调用 ESP Rainmaker API 。
    esp_rmaker_node_t *esp_rmaker_node_init(const esp_rmaker_config_t *config, const char *name, const char *type);
    

    创建设备

    1. 该函数可以用于创建一个设备,第一个参数是设备的设备名称,第二个参数时设备类型。根据第二个参数,界面能够有不同的显示。能够选择的类型参考 : 标准预定义类型
    esp_rmaker_device_t *esp_rmaker_device_create(const char *name, const char *type, void *priv);
    
    /********** STANDARD DEVICE TYPES **********/
    
    #define ESP_RMAKER_DEVICE_SWITCH        "esp.device.switch"
    #define ESP_RMAKER_DEVICE_LIGHTBULB     "esp.device.lightbulb"
    #define ESP_RMAKER_DEVICE_FAN           "esp.device.fan"
    #define ESP_RMAKER_DEVICE_TEMP_SENSOR   "esp.device.temperature-sensor"
    #define ESP_RMAKER_DEVICE_LIGHT         "esp.device.light"
    #define ESP_RMAKER_DEVICE_OUTLET        "esp.device.outlet"
    #define ESP_RMAKER_DEVICE_PLUG          "esp.device.plug"
    #define ESP_RMAKER_DEVICE_SOCKET        "esp.device.socket"
    #define ESP_RMAKER_DEVICE_LOCK          "esp.device.lock"
    #define ESP_RMAKER_DEVICE_BLINDS_INTERNAL   "esp.device.blinds-internal"
    #define ESP_RMAKER_DEVICE_BLINDS_EXTERNAL   "esp.device.blinds-external"
    #define ESP_RMAKER_DEVICE_GARAGE_DOOR   "esp.device.garage-door"
    #define ESP_RMAKER_DEVICE_GARAGE_LOCK   "esp.device.garage-door-lock"
    #define ESP_RMAKER_DEVICE_SPEAKER       "esp.device.speaker"
    #define ESP_RMAKER_DEVICE_AIR_CONDITIONER   "esp.device.air-conditioner"
    #define ESP_RMAKER_DEVICE_THERMOSTAT    "esp.device.thermostat"
    #define ESP_RMAKER_DEVICE_TV            "esp.device.tv"
    #define ESP_RMAKER_DEVICE_WASHER        "esp.device.washer"
    #define ESP_RMAKER_DEVICE_OTHER         "esp.device.other"
    #define ESP_RMAKER_DEVICE_ZIGBEE_GATEWAY   "esp.device.zigbee_gateway"
    

    添加设备回调函数

    1. 当我们创建设备成功后,我们需要调用该函数注册回调函数。当我们手机进行操作时,ESP32 会触发 write_cb 回调函数。read_cb 感觉没用,直接传入 NULL 即可。
    esp_err_t esp_rmaker_device_add_cb(const esp_rmaker_device_t *device, esp_rmaker_device_write_cb_t write_cb, esp_rmaker_device_read_cb_t read_cb);
    
    1. 如下为一个回调函数的示例。
    2. 需要重点关注的三个参数:
  • device_name : 发生写事件的设备名称,因为我们这里仅注册了一个设备,因此该参数可以无视。
  • param_name : 写事件的具体参数名称。
  • val.val : 该参数是一个联合体,根据需求进行配置。
  • b : 如果该参数是 bool 类型数据,使用这个
  • i : int 类型
  • f : float 类型
  • s : 字符串类型
  • static esp_err_t rainmaker_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)
    {
        if (ctx) { /* If this is not a null pointer, print the data source */
            ESP_LOGI(TAG, "Received write request via : %s", esp_rmaker_device_cb_src_to_str(ctx->src));
        }
        const char *device_name = esp_rmaker_device_get_name(device);
        const char *param_name = esp_rmaker_param_get_name(param);
    	
    	/* 打印获取的 */
        ESP_LOGD(TAG, "Device name: %s, Device param: %s, Val: %d", device_name, param_name,val.val.i);
    
        if (strcmp(param_name, "Brushing Mode") == 0) {
            ESP_LOGI(TAG,"Brushing Mode %s ",val.val.s);
            ESP_LOGD(TAG,"Brushing Mode %d ",rainmaker_get_brushing_mode_num(val.val.s));
            brush_strength_set(rainmaker_get_brushing_mode_num(val.val.s));
            brush_frequency_level = brush_strength_get();
            motor_set_params(brush_frequency_level, brush_amplitude_level);
        } else if (strcmp(param_name, "Set brush time(s)") == 0) {
            ESP_LOGI(TAG,"Set brush time(s) %d",val.val.i);
            set_brush_total_time(val.val.i);
            esp_rmaker_param_update_and_report(
                    esp_rmaker_device_get_param_by_name(g_rainmaker, "Set brush time(s)"),
                    esp_rmaker_int(val.val.i)); 
        } 
    
        return ESP_OK;
    }
    

    创建设备参数

    1. 这个函数用于创建设备参数。
  • param_name : 设备参数的名称。
  • type : 设备参数类型。拥有的参数类型。如果不想用它预设的参数,传入 NULL 即可。
  • val : 设备参数的初始值。你需要使用 use esp_rmaker_bool(), esp_rmaker_int(), esp_rmaker_float() or esp_rmaker_str() 来进行设置。
  • esp_rmaker_param_t *esp_rmaker_param_create(const char *param_name, const char *type,
            esp_rmaker_param_val_t val, uint8_t properties);
    
    /********** STANDARD PARAM TYPES **********/
    
    #define ESP_RMAKER_PARAM_NAME           "esp.param.name"
    #define ESP_RMAKER_PARAM_POWER          "esp.param.power"
    #define ESP_RMAKER_PARAM_BRIGHTNESS     "esp.param.brightness"
    #define ESP_RMAKER_PARAM_HUE            "esp.param.hue"
    #define ESP_RMAKER_PARAM_SATURATION     "esp.param.saturation"
    #define ESP_RMAKER_PARAM_INTENSITY      "esp.param.intensity"
    #define ESP_RMAKER_PARAM_CCT            "esp.param.cct"
    #define ESP_RMAKER_PARAM_SPEED          "esp.param.speed"
    #define ESP_RMAKER_PARAM_DIRECTION      "esp.param.direction"
    #define ESP_RMAKER_PARAM_TEMPERATURE    "esp.param.temperature"
    #define ESP_RMAKER_PARAM_OTA_STATUS     "esp.param.ota_status"
    #define ESP_RMAKER_PARAM_OTA_INFO       "esp.param.ota_info"
    #define ESP_RMAKER_PARAM_OTA_URL        "esp.param.ota_url"
    #define ESP_RMAKER_PARAM_TIMEZONE       "esp.param.tz"
    #define ESP_RMAKER_PARAM_TIMEZONE_POSIX       "esp.param.tz_posix"
    #define ESP_RMAKER_PARAM_SCHEDULES      "esp.param.schedules"
    #define ESP_RMAKER_PARAM_SCENES         "esp.param.scenes"
    #define ESP_RMAKER_PARAM_REBOOT         "esp.param.reboot"
    #define ESP_RMAKER_PARAM_FACTORY_RESET  "esp.param.factory-reset"
    #define ESP_RMAKER_PARAM_WIFI_RESET     "esp.param.wifi-reset"
    #define ESP_RMAKER_PARAM_LOCAL_CONTROL_POP      "esp.param.local_control_pop"
    #define ESP_RMAKER_PARAM_LOCAL_CONTROL_TYPE     "esp.param.local_control_type"
    #define ESP_RMAKER_PARAM_TOGGLE         "esp.param.toggle"
    #define ESP_RMAKER_PARAM_RANGE          "esp.param.range"
    #define ESP_RMAKER_PARAM_MODE           "esp.param.mode"
    #define ESP_RMAKER_PARAM_BLINDS_POSITION     "esp.param.blinds-position"
    #define ESP_RMAKER_PARAM_GARAGE_POSITION     "esp.param.garage-position"
    #define ESP_RMAKER_PARAM_LIGHT_MODE     "esp.param.light-mode"
    #define ESP_RMAKER_PARAM_AC_MODE        "esp.param.ac-mode"
    #define ESP_RMAKER_PARAM_ADD_ZIGBEE_DEVICE     "esp.param.add_zigbee_device"
    

    添加 UI 属性

    1. 我们可以调用如下函数添加 UI 属性。可选的 UI 元素。
    esp_err_t esp_rmaker_param_add_ui_type(const esp_rmaker_param_t *param, const char *ui_type);
    
    /********** STANDARD UI TYPES **********/
    
    #define ESP_RMAKER_UI_TOGGLE            "esp.ui.toggle"
    #define ESP_RMAKER_UI_SLIDER            "esp.ui.slider"
    #define ESP_RMAKER_UI_DROPDOWN          "esp.ui.dropdown"
    #define ESP_RMAKER_UI_TEXT              "esp.ui.text"
    #define ESP_RMAKER_UI_HUE_SLIDER        "esp.ui.hue-slider"
    #define ESP_RMAKER_UI_HUE_CIRCLE        "esp.ui.hue-circle"
    #define ESP_RMAKER_UI_PUSHBUTTON        "esp.ui.push-btn-big"
    #define ESP_RMAKER_UI_TRIGGER           "esp.ui.trigger"
    #define ESP_RMAKER_UI_HIDDEN            "esp.ui.hidden"
    #define ESP_RMAKER_UI_QR_SCAN           "esp.ui.qr-scan"
    

    将参数加入设备

    1. 当我们配置完参数后,需要调用该函数将参数加入到设备。
    esp_err_t esp_rmaker_device_add_param(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param);
    

    设置首要参数

    1. 如果我们需要在设备首页即可看到某个参数,那么可以使用如下函数。
    esp_err_t esp_rmaker_device_assign_primary_param(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param);
    

    创建设备名

    1. 一个设备需要名称,我们可以调用如下函数进行添加名称元素。后续我们就可以在手机端更改设备名称了。
    esp_rmaker_param_t *esp_rmaker_name_param_create(const char *param_name, const char *val);
    

    添加字符串类型参数

    1. 有时候,我们可能会需要添加可下滑选择的参数。
    2. 而这些参数我们大概率是希望以字符串的形式存在,因此就需要使用到该参数。
    esp_err_t esp_rmaker_param_add_valid_str_list(const esp_rmaker_param_t *param, const char *strs[], uint8_t count);
    
    // 使用示例
    static const char *valid_strs[] = {"Gentle Mode","Standard Mode","Strong Mode","Deep Clean Mode"};
    esp_rmaker_param_t *vibration_param = esp_rmaker_param_create("Brushing Mode", NULL, esp_rmaker_str("Gentle Mode"), PROP_FLAG_READ | PROP_FLAG_WRITE);
    esp_rmaker_param_add_valid_str_list(vibration_param, valid_strs, 4);
    esp_rmaker_param_add_ui_type(vibration_param, ESP_RMAKER_UI_DROPDOWN);
    esp_rmaker_device_add_param(g_rainmaker_device, vibration_param);
    

    列表效果

    1. 如果我们希望有列表记录的效果,可以直接按照如下代码进行编写。
    esp_rmaker_param_t *medication_record_one = esp_rmaker_param_create("Brushing time(s)", NULL, esp_rmaker_float(0.0), PROP_FLAG_READ | PROP_FLAG_TIME_SERIES);
    esp_rmaker_device_add_param(g_rainmaker_device, medication_record_one);
    

    将设备添加进入节点

    1. 设备设置完成之后,我们需要调用如下函数将设备添加进入节点。
    esp_err_t esp_rmaker_node_add_device(const esp_rmaker_node_t *node, const esp_rmaker_device_t *device);
    

    启动 Rainmaker

    1. 当上面配置全部完成以后,我们可以需要调用该函数最终启动 Rainmaker。
    esp_err_t esp_rmaker_start(void);
    

    数据上报

    1. 如果需要上报数据,我们可以先调用 esp_rmaker_device_get_param_by_name 函数获取参数名句柄,然后调用 esp_rmaker_param_update_and_report 函数更新数据。
    2. 需要注意的一点是,你在创建参数时候,用的是什么类型的数据,那么更新时候也应当用对应类型的数据。
    // 获取指定参数名的句柄
    esp_rmaker_param_t *esp_rmaker_device_get_param_by_name(const esp_rmaker_device_t *device, const char *param_name);
    // 更新参数数据
    esp_err_t esp_rmaker_param_update_and_report(const esp_rmaker_param_t *param, esp_rmaker_param_val_t val);
    
    // 使用示例
    esp_rmaker_param_update_and_report(esp_rmaker_device_get_param_by_name(g_rainmaker, "state of charge"),esp_rmaker_bool(is_charge_status));
    
    esp_rmaker_param_update_and_report(esp_rmaker_device_get_param_by_name(g_rainmaker, "Brushing Mode"),esp_rmaker_str("Standard Mode")); 
    
    static char power_str[] = "%d %";
    sprintf(power_str, "%d %%", get_power_value());
    esp_rmaker_param_update_and_report(esp_rmaker_device_get_param_by_name(g_rainmaker, "Battery Level"),esp_rmaker_str(power_str)); 
        
    esp_rmaker_param_update_and_report(esp_rmaker_device_get_param_by_name(g_rainmaker, "Brushing time(s)"),esp_rmaker_float(5.3));  
    

    弹窗效果

    1. 我们可以使用如下函数实现弹窗效果。
    2. 需要注意,如果你调用该命令,发现 Rainmaker 并没有出现弹窗,那是因为你手机没有给 ESP Rainmaker 设置通知权限,请自行在设置中打开。
    esp_rmaker_raise_alert("Please connect the charger");
    

    参考

    1. B站:ESP-IDF VSCode 开发 【沉浸式教程】
    2. ESP RainMaker 官方文档
    3. ESP RainMaker API
    4. RainMaker APP Usage

    作者:风正豪

    物联沃分享整理
    物联沃-IOTWORD物联网 » 乐鑫 Rainmaker 轻松入门指南

    发表回复