最近有接触matter这个功能,想在esp32上尝试一下,故而这里记录一下
关于matter的说明,可以参考这个文章
Matter开发,看这一篇就够了
主要参考:(主要还是得参考下面那个文章)
【ESP32-Matter】基于matter协议 chip-tool 控制 esp32-c3 开发板点灯
Developing with the SDK
1、环境搭建
简单来说:
1、matter是个智能家居协议,两个设备无论是不是同一个厂家的产品,只要都支持matter,就可以一起控制,matter生态和其他的生态也一样,需要有一个主控,拓扑如下所示:
上面图中还有个bridge,bridge也是和其他生态的bridge一样,可以把不支持matter生态的产品通过bridge介入matter生态中。
2、esp32,也就是乐鑫,一般开发esp32用的是esp-idf来开发的,乐鑫针对matter的sdk是esp-matter,使用esp-matter就可以让esp32支持matter
下面是编译记录吧,这里面搭环境有很多坑,建议参考我上面的那个官方的资料(然后整个过程需要全局科学上网,这一点非常重要)
这里我用的开发环境是wsl的ubuntu22.04,直接在windows的商店安装完成即可。然后直接更新软件源,这也是参考的官方资料:
下面先下载esp-idf,这个是肯定的,就是安装idf的环境
git clone --recursive https://github.com/espressif/esp-idf.git
cd esp-idf; git checkout v5.2.1; git submodule update --init --recursive;
./install.sh
cd ..
之后检查一下环境:
cd esp-idf
source ./export.sh
cd ..
看到这个输出就没啥问题了(这里一般不会有什么问题)
下面开始安装esp-matter,还是先拉git
git clone --depth 1 https://github.com/espressif/esp-matter.git
cd esp-matter
下面开始拉第三方库,这个可能会失败,失败了就重复执行,科学上网在这个时候的重要性就体现出来了
git submodule update --init --depth 1
之后开始拉matter的sdk
cd ./connectedhomeip/connectedhomeip
./scripts/checkout_submodules.py --platform esp32 darwin --shallow
开始编译matter
cd ../..
./install.sh
会看到下面的输出,这个过程还是有可能会失败,这个失败了我建议是删掉esp-matter,重来一遍即可。
我遇到过的原因是因为这里的文件拉取的不全导致的,这个repo下面会有这么多文件是正常的
编译的过程是这样的:
之后开始安装python库
下面开始编译chip-tools,发现上面其实已经编译了
下面开始测试matter,进入下面的目录,选一下板子,发现需要安装一下cmake,apt安装一下即可
之后再次尝试
使用idf.py build来进行编译
编译OK,开始烧录
总的来说,这个过程一定要连外网,不然很多包真的是很难下载,下面是个安装过程遇到过的问题:
解决方案是:
sudo apt install build-essential python3-dev
sudo apt-get install pkg-config
sudo apt-get install libglib2.0-dev libglib2.0-dev-bin libgio2.0-cil-dev
然后不是每次启动都要配一下环境吗,就可以这样设置,就在~/.bashrc
的结尾加上这两行
ource /home/lx/Desktop/esp32/esp-idf/export.sh
source /home/lx/Desktop/esp32/esp-matter/export.sh
这样每次打开终端就都会执行
2、正式测试,烧录chip-light
本次测试用的是比较通用的esp32最小系统(注:需要改一点代码才能跑起来的)
前面代码已经编译,下面直接编译看看什么情况,使用idf烧录之前需要先给USB提权
sudo chmod 777 /dev/ttyUSB0
下面先擦除,不然上次的绑定关系会有影响
idf.py erase-flash
如下所示
idf.py flash monitor
(烧录后直接进入串口)
这里可以看到一些信息
如果要退出这个终端,直接ctrl+c是不行的,需要按下ctrl+]退出串口终端
烧录证书(证书的生成后面会说到)
esptool.py -p /dev/ttyUSB0 write_flash 0x10000 /home/lx/Desktop/esp32/esp-matter/tools/mfg_tool/out/fff2_8001/7ab72773-c568-46fc-9300-892b480cdb3d/7ab72773-c568-46fc-9300-892b480cdb3d-partition.bin
证书烧录的比较快
重新烧录
生成的证书在这里
可以对一下证书
然后这里我用的是onip的方式连接,需要matter先配个网,输入下面的内容
从后面的打印中可以看到已经配上网了
在路由器主页也可以看到
可以用手机app看一下是不是处于配对模式(选择udp,然后输入matterc搜索)
之后在一个终端输入chip-tool pairing onnetwork 0x11 22865251(这个码是前面证书的)
之后继续在这个终端输入这样的命令来完成开关灯
可以观察开发板查看灯的亮灭情况
chip-tool onoff on 0x11 0x2
chip-tool onoff off 0x11 0x2
下面说下证书是怎么生成的,输入下面的内容,我这里是生成5个
esp-matter-mfg-tool -n 5 -cn "My bulb" -v 0xFFF2 -p 0x8001 --pai \
-k ~/Desktop/esp32/esp-matter/connectedhomeip/connectedhomeip/credentials/test/attestation/Chip-Test-PAI-FFF2-8001-Key.pem \
-c ~/Desktop/esp32/esp-matter/connectedhomeip/connectedhomeip/credentials/test/attestation/Chip-Test-PAI-FFF2-8001-Cert.pem \
-cd ~/Desktop/esp32/esp-matter/connectedhomeip/connectedhomeip/credentials/test/certification-declaration/Chip-Test-CD-FFF2-8001.der
也可以用ios的apple home绑定(只是会提示是未认证,点击确认即可)
3、代码修改部分
这里需要说明下,官方代码用的是一个这个开发板来做的,实在是太贵了
我用的是自己受伤的替代版本,所以官方代码肯定是能跑,但是跑起来肯定不是那个样,因此需要改动一点才行,这里只改了一个文件
我这个开发板的灯是led2
#include "driver/gpio.h"
#include <app/server/CommissioningWindowManager.h>
#include <app/server/Server.h>
static const char *TAG = "app_main";
uint16_t light_endpoint_id = 0;
uint16_t light_endpoint2_id = 0;
#define BLINK_GPIO GPIO_NUM_2
新增了一个endpoint和gpio的初始化
然后在收到更新回调的地方,判断是不是我这个endpoint触发的,如果是的话就根据改变的情况改变gpio状态
完整代码:
/*
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 <esp_err.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <esp_matter.h>
#include <esp_matter_console.h>
#include <esp_matter_ota.h>
#include <common_macros.h>
#include <app_priv.h>
#include <app_reset.h>
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
#include <platform/ESP32/OpenthreadLauncher.h>
#endif
#include "driver/gpio.h"
#include <app/server/CommissioningWindowManager.h>
#include <app/server/Server.h>
static const char *TAG = "app_main";
uint16_t light_endpoint_id = 0;
uint16_t light_endpoint2_id = 0;
#define BLINK_GPIO GPIO_NUM_2
using namespace esp_matter;
using namespace esp_matter::attribute;
using namespace esp_matter::endpoint;
using namespace chip::app::Clusters;
constexpr auto k_timeout_seconds = 300;
#if CONFIG_ENABLE_ENCRYPTED_OTA
extern const char decryption_key_start[] asm("_binary_esp_image_encryption_key_pem_start");
extern const char decryption_key_end[] asm("_binary_esp_image_encryption_key_pem_end");
static const char *s_decryption_key = decryption_key_start;
static const uint16_t s_decryption_key_len = decryption_key_end - decryption_key_start;
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg)
{
switch (event->Type) {
case chip::DeviceLayer::DeviceEventType::kInterfaceIpAddressChanged:
ESP_LOGI(TAG, "Interface IP Address changed");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningComplete:
ESP_LOGI(TAG, "Commissioning complete");
break;
case chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired:
ESP_LOGI(TAG, "Commissioning failed, fail safe timer expired");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStarted:
ESP_LOGI(TAG, "Commissioning session started");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStopped:
ESP_LOGI(TAG, "Commissioning session stopped");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowOpened:
ESP_LOGI(TAG, "Commissioning window opened");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowClosed:
ESP_LOGI(TAG, "Commissioning window closed");
break;
case chip::DeviceLayer::DeviceEventType::kFabricRemoved:
{
ESP_LOGI(TAG, "Fabric removed successfully");
if (chip::Server::GetInstance().GetFabricTable().FabricCount() == 0)
{
chip::CommissioningWindowManager & commissionMgr = chip::Server::GetInstance().GetCommissioningWindowManager();
constexpr auto kTimeoutSeconds = chip::System::Clock::Seconds16(k_timeout_seconds);
if (!commissionMgr.IsCommissioningWindowOpen())
{
/* After removing last fabric, this example does not remove the Wi-Fi credentials
* and still has IP connectivity so, only advertising on DNS-SD.
*/
CHIP_ERROR err = commissionMgr.OpenBasicCommissioningWindow(kTimeoutSeconds,
chip::CommissioningWindowAdvertisement::kDnssdOnly);
if (err != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "Failed to open commissioning window, err:%" CHIP_ERROR_FORMAT, err.Format());
}
}
}
break;
}
case chip::DeviceLayer::DeviceEventType::kFabricWillBeRemoved:
ESP_LOGI(TAG, "Fabric will be removed");
break;
case chip::DeviceLayer::DeviceEventType::kFabricUpdated:
ESP_LOGI(TAG, "Fabric is updated");
break;
case chip::DeviceLayer::DeviceEventType::kFabricCommitted:
ESP_LOGI(TAG, "Fabric is committed");
break;
case chip::DeviceLayer::DeviceEventType::kBLEDeinitialized:
ESP_LOGI(TAG, "BLE deinitialized and memory reclaimed");
break;
default:
break;
}
}
// This callback is invoked when clients interact with the Identify Cluster.
// In the callback implementation, an endpoint can identify itself. (e.g., by flashing an LED or light).
static esp_err_t app_identification_cb(identification::callback_type_t type, uint16_t endpoint_id, uint8_t effect_id,
uint8_t effect_variant, void *priv_data)
{
ESP_LOGI(TAG, "Identification callback: type: %u, effect: %u, variant: %u", type, effect_id, effect_variant);
return ESP_OK;
}
// This callback is called for every attribute update. The callback implementation shall
// handle the desired attributes and return an appropriate error code. If the attribute
// is not of your interest, please do not return an error code and strictly return ESP_OK.
static esp_err_t app_attribute_update_cb(attribute::callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id,
uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data)
{
esp_err_t err = ESP_OK;
if (type == PRE_UPDATE) {
/* Driver update */
if(endpoint_id == light_endpoint2_id)
{
if (cluster_id == OnOff::Id) {
if (attribute_id == OnOff::Attributes::OnOff::Id) {
gpio_set_level(BLINK_GPIO, val->val.b);
return err;
}
}
}
app_driver_handle_t driver_handle = (app_driver_handle_t)priv_data;
err = app_driver_attribute_update(driver_handle, endpoint_id, cluster_id, attribute_id, val);
}
return err;
}
extern "C" void app_main()
{
esp_err_t err = ESP_OK;
/* Initialize the ESP NVS layer */
nvs_flash_init();
/* Initialize driver */
app_driver_handle_t light_handle = app_driver_light_init();
app_driver_handle_t button_handle = app_driver_button_init();
app_reset_button_register(button_handle);
/* Create a Matter node and add the mandatory Root Node device type on endpoint 0 */
node::config_t node_config;
// node handle can be used to add/modify other endpoints.
node_t *node = node::create(&node_config, app_attribute_update_cb, app_identification_cb);
ABORT_APP_ON_FAILURE(node != nullptr, ESP_LOGE(TAG, "Failed to create Matter node"));
extended_color_light::config_t light_config;
light_config.on_off.on_off = DEFAULT_POWER;
light_config.on_off.lighting.start_up_on_off = nullptr;
light_config.level_control.current_level = DEFAULT_BRIGHTNESS;
light_config.level_control.on_level = DEFAULT_BRIGHTNESS;
light_config.level_control.lighting.start_up_current_level = DEFAULT_BRIGHTNESS;
light_config.color_control.color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature;
light_config.color_control.enhanced_color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature;
light_config.color_control.color_temperature.startup_color_temperature_mireds = nullptr;
// endpoint handles can be used to add/modify clusters.
endpoint_t *endpoint = extended_color_light::create(node, &light_config, ENDPOINT_FLAG_NONE, light_handle);
ABORT_APP_ON_FAILURE(endpoint != nullptr, ESP_LOGE(TAG, "Failed to create extended color light endpoint"));
light_endpoint_id = endpoint::get_id(endpoint);
ESP_LOGI(TAG, "Light created with endpoint_id %d", light_endpoint_id);
/* Mark deferred persistence for some attributes that might be changed rapidly */
cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id);
attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id);
attribute::set_deferred_persistence(current_level_attribute);
cluster_t *color_control_cluster = cluster::get(endpoint, ColorControl::Id);
attribute_t *current_x_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::CurrentX::Id);
attribute::set_deferred_persistence(current_x_attribute);
attribute_t *current_y_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::CurrentY::Id);
attribute::set_deferred_persistence(current_y_attribute);
attribute_t *color_temp_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::ColorTemperatureMireds::Id);
attribute::set_deferred_persistence(color_temp_attribute);
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD && CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION
// Enable secondary network interface
secondary_network_interface::config_t secondary_network_interface_config;
endpoint = endpoint::secondary_network_interface::create(node, &secondary_network_interface_config, ENDPOINT_FLAG_NONE, nullptr);
ABORT_APP_ON_FAILURE(endpoint != nullptr, ESP_LOGE(TAG, "Failed to create secondary network interface endpoint"));
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
/* Set OpenThread platform config */
esp_openthread_platform_config_t config = {
.radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
.host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
.port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
};
set_openthread_platform_config(&config);
#endif
// on_off_light::app_driver_attribute_update
on_off_light::config_t light_config2;
light_config2.on_off.on_off = DEFAULT_POWER;
light_config2.on_off.lighting.start_up_on_off = DEFAULT_POWER;
endpoint_t *endpoint2 = on_off_light::create(node, &light_config2, ENDPOINT_FLAG_NONE, 0);
light_endpoint2_id = endpoint::get_id(endpoint2);
/* Matter start */
err = esp_matter::start(app_event_cb);
ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to start Matter, err:%d", err));
/*GPIO设置*/
gpio_reset_pin(BLINK_GPIO);
/* Set the GPIO as a push/pull output */
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
gpio_set_level(BLINK_GPIO, DEFAULT_POWER);
/* Starting driver with default values */
app_driver_light_set_defaults(light_endpoint_id);
#if CONFIG_ENABLE_ENCRYPTED_OTA
err = esp_matter_ota_requestor_encrypted_init(s_decryption_key, s_decryption_key_len);
ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to initialized the encrypted OTA, err: %d", err));
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
#if CONFIG_ENABLE_CHIP_SHELL
esp_matter::console::diagnostics_register_commands();
esp_matter::console::wifi_register_commands();
#if CONFIG_OPENTHREAD_CLI
esp_matter::console::otcli_register_commands();
#endif
esp_matter::console::init();
#endif
}
4、待做
这里可以考虑下ble的方式,但我没有实现
我之前用这个2204的虚拟机,开启蓝牙就挂掉了(这个问题有待研究下)
看起来是这里挂了
然后我又重新装了个2004的虚拟机,终于跑起来了,参考下面这个大佬的
【Matter】使用chip tool在ESP32-C3上进行matter开发
但是最终也失败了
esp32端打印
配网的那一端打印
后面在研究下,有知道的也可以分享一下