乐鑫 Rainmaker 轻松入门指南
前言
- 个人邮箱:zhangyixu02@gmail.com
手机端操作
- 我们手机端需要在如下软件下载安装包,或者应用商城中搜索 ESP RainMaker。
安卓:
iOS:
- 安卓用户在 github 中点击下框部分
3. 点击 apk 文件下载,后续传到手机中安装即可。
ESP32 操作
创建一个新工程
- 在 VScode 中输入 Ctrl + Shift + P ,然后输入 Example 打开例程。
2. 点击 sample_project 创建一个项目。
拉取 rainmaker 仓库
git clone --recursive https://github.com/espressif/esp-rainmaker.git
修改配置
- 进入 main 文件夹,添加 idf_component.yml 文件
vim idf_component.yml
- 在该文件中包含内容。The ESP Component Registry 搜 rainmaker。
dependencies:
espressif/esp_rainmaker: "^1.3.0"
- 修改分区表。需要注意,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,
- 修改 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
- 进入根目录的 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)
烧录证书
- 像 esp32-c2 是需要烧录证书的,如果是esp32-c3无需。
- 如果出现如下报错,说明当前设备没有证书,需要进行烧录。
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!!!
- 我们可以执行如下命令生成证书。需要注意, –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,
*******************************************************************************
- 烧录证书。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 初始化
- 如下为一个 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;
}
- 最终的显示效果如下:
Rainmaker 常用 API 说明
创建节点
- 该函数用于创建一个 Rainmaker 节点。需要先调用该函数,才能够调用 ESP Rainmaker API 。
esp_rmaker_node_t *esp_rmaker_node_init(const esp_rmaker_config_t *config, const char *name, const char *type);
创建设备
- 该函数可以用于创建一个设备,第一个参数是设备的设备名称,第二个参数时设备类型。根据第二个参数,界面能够有不同的显示。能够选择的类型参考 : 标准预定义类型
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"
添加设备回调函数
- 当我们创建设备成功后,我们需要调用该函数注册回调函数。当我们手机进行操作时,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);
- 如下为一个回调函数的示例。
- 需要重点关注的三个参数:
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;
}
创建设备参数
- 这个函数用于创建设备参数。
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 属性
- 我们可以调用如下函数添加 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"
将参数加入设备
- 当我们配置完参数后,需要调用该函数将参数加入到设备。
esp_err_t esp_rmaker_device_add_param(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param);
设置首要参数
- 如果我们需要在设备首页即可看到某个参数,那么可以使用如下函数。
esp_err_t esp_rmaker_device_assign_primary_param(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param);
创建设备名
- 一个设备需要名称,我们可以调用如下函数进行添加名称元素。后续我们就可以在手机端更改设备名称了。
esp_rmaker_param_t *esp_rmaker_name_param_create(const char *param_name, const char *val);
添加字符串类型参数
- 有时候,我们可能会需要添加可下滑选择的参数。
- 而这些参数我们大概率是希望以字符串的形式存在,因此就需要使用到该参数。
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);
列表效果
- 如果我们希望有列表记录的效果,可以直接按照如下代码进行编写。
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_err_t esp_rmaker_node_add_device(const esp_rmaker_node_t *node, const esp_rmaker_device_t *device);
启动 Rainmaker
- 当上面配置全部完成以后,我们可以需要调用该函数最终启动 Rainmaker。
esp_err_t esp_rmaker_start(void);
数据上报
- 如果需要上报数据,我们可以先调用
esp_rmaker_device_get_param_by_name
函数获取参数名句柄,然后调用esp_rmaker_param_update_and_report
函数更新数据。 - 需要注意的一点是,你在创建参数时候,用的是什么类型的数据,那么更新时候也应当用对应类型的数据。
// 获取指定参数名的句柄
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));
弹窗效果
- 我们可以使用如下函数实现弹窗效果。
- 需要注意,如果你调用该命令,发现 Rainmaker 并没有出现弹窗,那是因为你手机没有给 ESP Rainmaker 设置通知权限,请自行在设置中打开。
esp_rmaker_raise_alert("Please connect the charger");
参考
- B站:ESP-IDF VSCode 开发 【沉浸式教程】
- ESP RainMaker 官方文档
- ESP RainMaker API
- RainMaker APP Usage
作者:风正豪