Adding clusters to Matter application

As part of this guide, you will modify the Matter template sample by adding new application clusters in order to create a Matter sensor device that measures temperature and can be turned on and off. The sensor will periodically generate the simulated temperature sensor value and update the corresponding cluster attributes. This application will form a Matter device within a Matter network.

Note

Make sure you are familiar with Matter in the nRF Connect SDK and you have tested some of the available Matter samples before you work with this user guide.

Overview

The Matter device is a basic node of the Matter network. The device is formed by the development kit and the application that is running the Matter stack, which is programmed on the development kit.

Each Matter application consists of the following layers:

  • Matter stack that provides the Matter core components.

  • Data Model layer in the form of clusters, which contains commands and attributes that are to be accessible over the Matter network. This layer can be further broken down into the following groups:

    • Utility clusters - These clusters represent management and diagnostic features of a Matter node. They are common for all Matter nodes.

    • Application clusters - These clusters represent functionalities specific to a given application.

  • Application logic, such as turning on and off a light bulb in response to certain commands.

Creating a Matter device consists of adding new application clusters to the Matter template sample. By default, the template sample includes only mandatory Matter clusters, necessary to commission the device into a Matter network.

Creating Matter device

Creating Matter device

Cluster is a Data Model building block in Matter. It is a representation of a single functionality within a Matter device, such as turning a device on and off. Each cluster contains attributes, commands, and events, which can be mandatory or optional. Attributes are stored in the device’s memory, while commands can be used to modify or read the state of the device, including the cluster attributes.

Clusters appropriate for a single device type such as a sensor or a light bulb are organized into an addressable container that is called an endpoint. Most utility clusters are required to be on the endpoint with ID 0. Application clusters are usually assigned to endpoints with IDs 1 and higher.

An application can implement appropriate callback functions to be informed about specific cluster state changes. These functions can be used to alter the device’s behavior when the state of a cluster is changing as a result of some external event.

For more information about the Data Model layer, see Data Model section on the Matter architecture documentation page.

Requirements

To take advantage of this guide, you need to be familiar with Matter architecture and Testing Matter in the nRF Connect SDK, and have built and tested at least one of the available Matter samples.

Copy Matter template sample

Use the Matter Template sample as the base for building a sensor device:

  1. Make sure that you meet the requirements for building the sample.

  2. Build and test the sample as described on its documentation page.

  3. Copy the contents of the samples/matter/template directory to a new directory meant for your custom application. For example, samples/matter/sensor.

Edit clusters using the ZAP tool

Adding the functionalities for an on/off switch and a sensor requires adding new clusters.

Adding new application clusters can be achieved by modifying ZAP file, which can be found as src/template.zap. This is a JSON file that contains the data model configuration of clusters, commands, and attributes that are enabled for a given application. It is not used directly by Matter applications, but it is used to generate the source files for handling given clusters.

The ZAP file can be edited using ZCL Advanced Platform (ZAP tool), a third-party tool that is a generic templating engine for applications and libraries based on Zigbee Cluster Library. This tool is provided with the Matter submodule.

To edit clusters using the ZAP tool, complete the following steps:

  1. Open your installation directory for the nRF Connect SDK in a command line.

  2. Navigate to modules/lib/matter.

  3. Open the src/template.zap for editing by running the following command, where samples/matter/sensor stands for the path where you copied the template sample in the first step of this guide.

    ./scripts/tools/zap/run_zaptool.sh ../../../nrf/samples/matter/sensor/src/template.zap
    

    The ZAP tool’s Zigbee Cluster Configurator window appears.

    Zigbee Cluster Configurator window in ZAP tool

    Zigbee Cluster Configurator window in ZAP tool

    By default, the window displays all available clusters. These can be filtered to show Only Enabled clusters. At this stage, only one endpoint is available (Endpoint 0).

  4. In the ZAP tool, click ADD NEW ENDPOINT.

  5. In the Create New Endpoint menu, create a new endpoint that represents the temperature sensor device type:

    Create New Endpoint menu in ZAP tool

    Create New Endpoint menu in ZAP tool

    The new cluster is created with both the Basic and Identify clusters enabled.

  6. Configure the On/Off cluster required for this endpoint:

    1. In the Search Clusters menu, find the On/Off cluster.

    2. Set the Server option for the On/Off cluster.

      Configuring the On/off server cluster

      Configuring the On/off server cluster

    3. In the Configure column, click the gear icon to open the cluster’s configuration.

    4. In the ATTRIBUTES tab, make sure that you have the OnOff attribute enabled.

    5. In the COMMANDS tab, make sure that you have both On and Off commands enabled:

    On/off cluster configuration

    On/off cluster configuration

  7. Configure the Temperature Measurement cluster required for this endpoint:

    1. Expand the Measurement & Sensing menu and configure the Temperature Measurement cluster by setting the Server option from the drop-down menu.

      Configuring the Temperature Measurement server cluster

      Configuring the Temperature Measurement server cluster

    2. Go to the Temperature Measurement cluster configuration and make sure that you have the MeasuredValue attribute enabled.

  8. Save the file and exit.

  9. Use the modified ZAP file to generate the C++ code that contains the selected clusters by running the following command, where samples/matter/sensor stands for the path where you copied the template sample in the first step of this guide:

    ./scripts/tools/zap/generate.py ../../../nrf/samples/matter/sensor/src/template.zap
    

At this point, new clusters have been added to the Matter device.

Edit the main loop of the application

After adding clusters, you must modify the way in which the application interacts with the newly added clusters. This is needed to properly model the sensor’s behavior.

The src/app_task.cpp file contains the main loop of the application. Complete the steps in the following subsections to modify the main loop.

Edit the event queue

The main application loop is based on an AppEventQueue, on which events are posted by ZCL callbacks and by other application components, such as Zephyr timers. In each iteration, an event is dequeued and a corresponding event handler is called.

Add new events

After you copied the template sample application, the following events are used in the application (src/app_event.h):

  • Button - For general operation of Button 1.

  • ButtonPushed - For pressing Button 1.

  • ButtonReleased - For releasing Button 1.

  • Timer - For factory reset timeout.

  • UpdateLedState - For updating of the LED 1 state.

To model the behavior of the sensor, add the following new AppEventType events:

  • SensorActivate - For sensor activation.

  • SensorDeactivate - For sensor deactivation.

  • SensorMeasure - For sensor measurement update.

For example, the edited AppEventType can look as follows:

enum class AppEventType {
     None = 0,
     Button,
     ButtonPushed,
     ButtonReleased,
     Timer,
     UpdateLedState,
     SensorActivate,
     SensorDeactivate,
     SensorMeasure
};

Add sensor timer

You need to make sure that the sensor is making measurements at the required time points. For this purpose, use a Zephyr timer to periodically post SensorMeasure events. In the template sample, such a timer is being used to count down 6 seconds when Button 1 is being pressed to initiate the factory reset.

To add a new timer for the measurement event, edit the src/app_task.cpp file as follows:

k_timer sSensorTimer;

void SensorTimerHandler(k_timer *timer)
{
        AppEvent event;
        event.Type = AppEventType::SensorMeasure;
        event.Handler = AppTask::SensorMeasureHandler;
        AppTask::Instance().PostEvent(event);
}

void StartSensorTimer(uint32_t aTimeoutMs)
{
        k_timer_start(&sSensorTimer, K_MSEC(aTimeoutMs), K_MSEC(aTimeoutMs));
}

void StopSensorTimer()
{
        k_timer_stop(&sSensorTimer);
}

int AppTask::Init()
{
        /*
        ... Original content
        */

        k_timer_init(&sSensorTimer, &SensorTimerHandler, nullptr);
        k_timer_user_data_set(&sSensorTimer, this);
        return 0;
}

The timer must be initialized in the Init() method of the AppTask class. If StartSensorTimer() is called, the SensorMeasure event is added to the event queue every aTimeoutMs milliseconds, until StopSensorTimer() is called.

Implement event handlers

When an event is dequeued, the application calls the event handler in the DispatchEvent() function. Because you have added new events, you must implement the corresponding handlers.

To add new event handlers, complete the following steps:

  1. Edit the src/app_task.cpp file as follows:

    void AppTask::SensorActivateHandler(const AppEvent &)
    {
            StartSensorTimer(500);
    }
    
    void AppTask::SensorDeactivateHandler(const AppEvent &)
    {
            StopSensorTimer();
    }
    
    void AppTask::SensorMeasureHandler(const AppEvent &)
    {
            chip::app::Clusters::TemperatureMeasurement::Attributes::MeasuredValue::Set(
                    /* endpoint ID */ 1, /* temperature in 0.01*C */ int16_t(rand() % 5000));
    }
    

    With this addition, when the sensor is active, the timer expiration event happens every half a second. This causes an invocation of SensorMeasureHandler() and triggers an update of the MeasuredValue attribute of the Temperature Measurement cluster.

    Note

    In the code fragment, the example value is updated randomly, but in a real sensor application it would be updated with the value obtained from external measurement.

  2. Declare these handler functions as static in the public scope of AppTask class in src/app_task.h to make sure the application builds properly.

Include header for cluster attribute helpers

To import helper functions for accessing cluster attributes, make sure to include the following file in the src/app_task.cpp file:

#include <app-common/zap-generated/attributes/Accessors.h>

Create a callback for sensor activation and deactivation

Handlers for the SensorActivate and SensorDeactivate events are now ready, but the events are not posted to the event queue. The sensor is supposed to be turned on and off remotely by changing the OnOff attribute of the On/off cluster, for example using the Matter controller. This means that we need to implement a callback function to post one of these events every time the OnOff attribute changes.

To implement the callback function, complete the following steps:

  1. Create a new file, for example src/zcl_callbacks.cpp.

  2. Implement the callback in this file:

    1. Open src/zap-generated/callback-stub.cpp to check the list of customizable callback functions, marked with __attribute__((weak)).

    2. Read the description of MatterPostAttributeChangeCallback().

    3. Implement MatterPostAttributeChangeCallback() in the src/zcl_callbacks.cpp file.

For example, the implementation can look as follows:

#include "app_task.h"

#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/ConcreteAttributePath.h>

using namespace ::chip;
using namespace ::chip::app::Clusters;

void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t mask, uint8_t type,
                                       uint16_t size, uint8_t * value)
{
        if (attributePath.mClusterId != OnOff::Id || attributePath.mAttributeId != OnOff::Attributes::OnOff::Id)
                return;

        AppEvent event;
        if (*value) {
            event.Type = AppEventType::SensorActivate;
            event.Handler = AppTask::SensorActivateHandler;
        }
        else {
            event.Type = AppEventType::SensorDeactivate;
            event.Handler = AppTask::SensorDeactivateHandler;
        }
        AppTask::Instance().PostEvent(event);
}

In this implementation, the if part filters out events other than those that belong to the On/Off cluster. Then, the callback posts the event for the sensor, namely SensorActivate if the current value of the attribute is not zero.

Add new source file to CMakeLists

To ensure that everything builds without errors, update the CMakeLists.txt file by adding src/zcl_callbacks.cpp source file to the app target.

Testing the new sensor application

Note

Use CHIP Tool for Android when setting up Matter development environment.

To check if the sensor device is working, complete the following steps:

  1. Connect the kit to the computer using a USB cable. The kit is assigned a COM port (Windows) or ttyACM device (Linux), which is visible in the Device Manager.

  2. Connect to the kit with a terminal emulator that supports VT100/ANSI escape characters (for example, PuTTY). See How to connect with PuTTY for the required settings.

  3. Commission the device into a Matter network by following the guides linked on the Testing Matter in the nRF Connect SDK page for the Matter controller you want to use. The guides walk you through the following steps:

    • Only if you are configuring Matter over Thread: Configure the Thread Border Router.

    • Build and install the Matter controller.

    • Commission the device.

    • Send Matter commands.

    At the end of this procedure, LED 1 of the Matter device programmed with the sample starts flashing in the Short Flash Off state. This indicates that the device is fully provisioned, but does not yet have full IPv6 network connectivity.

  4. Activate the sensor by running the following command on the On/off cluster with the correct node_ID assigned during commissioning:

    ./chip-tool onoff on node_ID 1
  5. Read the measurement several times by checking value of MeasuredValue in the Temperature Measurement cluster:

    ./chip-tool temperaturemeasurement read measured-value node_ID 1
  6. Deactivate the sensor by running the following command on the On/off cluster with the correct node_ID assigned during commissioning:

    ./chip-tool onoff off node_ID 1
  7. Read the measurement again. The measurement should not change.