Skip to content

How to implement Matter door_lock endpoint #11765

@0xbacaba

Description

@0xbacaba

Board

ESP32 Dev Module

Device Description

DevKit V1

Hardware Configuration

Version

v3.3.0

Type

Question

IDE Name

neovim + arduino-cli

Operating System

Arch Linux

Flash frequency

40MHz

PSRAM enabled

no

Upload speed

921600

Description

I'm trying to setup a door_lock endpoint in a similar way as the builtin OnOffLight. The device is correctly advertised, but whenever i flip the switch, it just flips back on its own.
The lock and unlock command callbacks are called correctly, MatterDoorLock::attributeChangeCB is not called.
Any help would be greatly appreciated!

Sketch

// MatterTest.ino:
#include <Matter.h>
#include <WiFi.h>

#include "MatterDoorLock.h"

MatterDoorLock doorLock;
bool set_lock(bool state) {
  Serial.print("lock set: ");
  Serial.println(state ? "on" : "off");
  return true;
}

void setup() {
  Serial.begin(115200);

  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while(WiFi.status() != WL_CONNECTED) {
    Serial.print(WiFi.status());
    delay(500);
  }
  Serial.print("\nConnected as ");
  Serial.println(WiFi.localIP());

  doorLock.onChange(set_lock);
  doorLock.begin(true);

  Matter.begin();

  if (!Matter.isDeviceCommissioned()) {
    Serial.println("");
    Serial.println("Matter Node is not commissioned yet.");
    Serial.println("Initiate the device discovery in your Matter environment.");
    Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
    Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
    Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
  }
}

// MatterDoorLock.h
#pragma once
#include <sdkconfig.h>

#include <Matter.h>
#include <MatterEndPoint.h>
#include <app/clusters/door-lock-server/door-lock-delegate.h>
#include <core/CHIPError.h>

using EndPointCB = std::function<bool(bool)>;
class MatterDoorLock : public MatterEndPoint, public ArduinoMatter {
protected:
  bool started = false;
  bool lockedState = false;
  EndPointCB _onChangeCB = NULL;

public:
  MatterDoorLock();
  ~MatterDoorLock();

  bool begin(bool initialLocked = false);
  void end();

  bool set_lock(bool new_state);
  bool toggle();
  bool isLocked();

  void onChange(EndPointCB cb) { _onChangeCB = cb; }

  bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val);
};

// MatterDoorLock.cpp
#include <sdkconfig.h>

#include <Matter.h>
#include <app/server/Server.h>
#include <app/clusters/door-lock-server/door-lock-server.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include "MatterDoorLock.h"
#include "esp_matter.h"
#include "esp_matter_attribute_utils.h"
#include "esp_matter_command.h"
#include "esp_matter_core.h"

#define log(level, format, ...) Serial.printf(level " (%d) " format "\n", millis(), ##__VA_ARGS__)
#define log_v(format, ...) log("V", format, ##__VA_ARGS__)
#define log_d(format, ...) log("D", format, ##__VA_ARGS__)
#define log_i(format, ...) log("I", format, ##__VA_ARGS__)
#define log_e(format, ...) log("E", format, ##__VA_ARGS__)

using namespace esp_matter;
using namespace esp_matter::endpoint;
using namespace chip::app::Clusters;

MatterDoorLock::MatterDoorLock() {}

MatterDoorLock::~MatterDoorLock() {
    end();
}

bool MatterDoorLock::begin(bool initialLocked) {
    ArduinoMatter::_init();

    if (getEndPointId() != 0) {
        log_e("Matter Door Lock with Endpoint Id %d device already exists.", getEndPointId());
        return false;
    }

    lockedState = initialLocked;

    door_lock::config_t lock_config;
    lock_config.door_lock.lock_state = initialLocked;
    lock_config.door_lock.actuator_enabled = true;

    endpoint_t *endpoint = door_lock::create(node::get(), &lock_config, ENDPOINT_FLAG_NONE, (void *)this);
    if (endpoint == nullptr) {
        log_e("Failed to create Door Lock endpoint");
        return false;
    }
    setEndPointId(endpoint::get_id(endpoint));

    cluster_t *lock_cluster = cluster::create(endpoint, DoorLock::Id, CLUSTER_FLAG_NONE);
    if(lock_cluster == nullptr) {
        log_e("Failed to create Door Lock cluster");
        return false;
    }

    command_t *unlock_command = cluster::door_lock::command::create_unlock_door(lock_cluster);
    command_t *lock_command = cluster::door_lock::command::create_lock_door(lock_cluster);
    if(unlock_command == nullptr || lock_command == nullptr) {
        log_e("Failed to create Door Lock commands");
        return false;
    }
    command::set_user_callback(unlock_command, [](const ConcreteCommandPath &path, TLVReader &data, void *context) {
        auto self = (MatterDoorLock *) context;
        self->set_lock(false);
        return ESP_OK;
    });
    command::set_user_callback(lock_command, [](const ConcreteCommandPath &path, TLVReader &data, void *context) {
        auto self = (MatterDoorLock *) context;
        self->set_lock(true);
        return ESP_OK;
    });

    log_i("Door Lock created with endpoint_id %d", getEndPointId());

    started = true;
    return true;
}

void MatterDoorLock::end() {
    started = false;
}

bool MatterDoorLock::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) {
    log_d("attributeChangeCB: %d, %d, %d", endpoint_id, cluster_id, attribute_id);
    if (!started) {
        log_e("Matter Door Lock device not started");
        return false;
    }

    if (endpoint_id == getEndPointId() && cluster_id == DoorLock::Id && attribute_id == DoorLock::Attributes::LockState::Id) {
        log_d("Door Lock state changed to %d", val->val.b);
        if (_onChangeCB) _onChangeCB(val->val.b);
        lockedState = val->val.b;
    }

    return true;
}

bool MatterDoorLock::set_lock(bool new_state) {
    log_i("set_lock %s", new_state ? "true" : "false");
    if (!started) return false;
    if (lockedState == new_state) return true; // no change

    lockedState = new_state;
    esp_matter_attr_val_t val = esp_matter_bool(new_state);
    updateAttributeVal(DoorLock::Id, DoorLock::Attributes::LockState::Id, &val);
    return true;
}

bool MatterDoorLock::toggle() {
    return set_lock(!isLocked());
}

bool MatterDoorLock::isLocked() {
    return lockedState;
}

// Makefile
PORT := /dev/ttyUSB0
BAUDRATE := 115200

all: compile upload monitor

compile:
	arduino-cli compile --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080

upload:
	arduino-cli upload -p $(PORT)

monitor:
	arduino-cli monitor -c $(BAUDRATE) -p $(PORT)

Debug Message

I (1670) Door Lock created with endpoint_id  1
E (1685) chip[DMG]: Endpoint 0, Cluster 0x0000_0031 not found in IncreaseClusterDataVersion!
E (1686) chip[DMG]: Endpoint 0, Cluster 0x0000_0031 not found in IncreaseClusterDataVersion!
E (6476) mdns: mdns_service_remove_for_host(6677): Service doesn't exist
I (39167) set_lock false
E (45197) chip[DMG]: Endpoint=2 Cluster=0x0000_0101 Command=0x0000_0001 status 0x01 (no additional context)
I (41099) set_lock false
E (47131) chip[DMG]: Endpoint=2 Cluster=0x0000_0101 Command=0x0000_0001 status 0x01 (no additional context)

Other Steps to Reproduce

I've tried the same structure to replicate an OnOffLight endpoint and that has worked flawlessly. Didn't even create a cluster etc.

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.

Metadata

Metadata

Assignees

Labels

Area: MatterIssues and Feature Request about Matter ProtocolType: Feature requestFeature request for Arduino ESP32Type: QuestionOnly question

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions