Custom Matter Device Development#
Build a customizable lighting app using the Matter protocol.
Overview#
This guide covers the basics of building a customizable lighting application using Matter.
Extending Base App Implementation#
CustomerAppTask#
To customize app behavior, override any Silicon Labs implemented API in CustomerAppTask. This example provides CustomerAppTask.h and CustomerAppTask.cpp for that purpose. The build system generates the base implementation and the complete set of overridable *Impl() APIs in autogen/AppTask.cpp and autogen/AppTaskImpl.h. Any *Impl() methods that you do not override use the Silicon Labs default implementation.
How to Override APIs#
CustomerAppTask extends the base AppTask by using the Curiously Recurring Template Pattern (CRTP). The base class declares one *Impl() method for each overridable API. Override only the *Impl() methods that you need. To override a *Impl() method:
Find the method to override in the base API. For more information, see Override API reference.
Declare the same method signature in
CustomerAppTask.hunder theprivate:section. Match the base*Impl()signature exactly.NOTE:
*Impl()overrides are non-static instance methods, even when the corresponding public dispatcher (for example,ButtonEventHandler) is static.Implement the method in
CustomerAppTask.cpp.Build the project. If you implement the corresponding
*Impl()method inCustomerAppTask, your implementation is used. Otherwise, the Silicon Labs default implementation is used. You only need to implement the methods that you want to customize. All other methods automatically use the default implementation.
DataModelCallbacks and CustomerAppTask#
What used to live in DataModelCallbacks.cpp before Matter Extension 2.9.1 now lives in AppTask.cpp. The
Matter SDK's MatterPostAttributeChangeCallback is implemented in
examples/platform/silabs/BaseApplication.cpp and forwards to
AppTask::DMPostAttributeChangeCallback (defined in AppTask.cpp), which you
can customize via DMPostAttributeChangeCallbackImpl() in CustomerAppTask.
Forwarding into AppTask still goes through CRTP as in
How to Override APIs.
Methods that already exist in the AppTask — Customize them by overriding the matching
*Impl()method inCustomerAppTask. Do not edit theAppTask.cppfor app-specific behavior.New custom data model methods — Add them in
CustomerAppTaskdirectly. Do not add new application logic in autogenerated sources; those edits will not survive regeneration or project upgrades.
Sample Implementation#
The following shows a minimal example CustomerAppTask that overrides AppInitImpl() and ButtonEventHandlerImpl() from
the lighting app implementation.
CustomerAppTask.h
#pragma once
#include "AppTaskImpl.h"
/**
* Minimal AppTaskImpl-derived class. Override only the *Impl() methods you need;
* add AppInitImpl(), GetAppTask(), and sAppTask as required by the CRTP base.
*/
class CustomerAppTask : public AppTaskImpl<CustomerAppTask>
{
public:
static CustomerAppTask & GetAppTask() { return sAppTask; }
private:
friend class AppTaskImpl<CustomerAppTask>;
CHIP_ERROR AppInitImpl();
void ButtonEventHandlerImpl(uint8_t button, uint8_t btnAction);
static CustomerAppTask sAppTask;
};CustomerAppTask.cpp
#include "CustomerAppTask.h"
#include "AppTask.h"
#include "AppConfig.h"
#include "AppEvent.h"
#include <platform/CHIPDeviceLayer.h>
#include <platform/silabs/platformAbstraction/SilabsPlatform.h>
using namespace ::chip::DeviceLayer::Silabs;
#define APP_FUNCTION_BUTTON 0
#define APP_LIGHT_SWITCH 1
CustomerAppTask CustomerAppTask::sAppTask;
AppTask & AppTask::GetAppTask()
{
return CustomerAppTask::GetAppTask();
}
CHIP_ERROR CustomerAppTask::AppInitImpl()
{
SILABS_LOG("CustomerAppTask: custom implementation (AppInitImpl)");
CHIP_ERROR err = this->AppTask::AppInit();
if (err == CHIP_NO_ERROR)
{
// Override the SDK default button handler registered in AppTask::AppInit().
chip::DeviceLayer::Silabs::GetPlatform().SetButtonsCb(CustomerAppTask::ButtonEventHandler);
}
return err;
}
void CustomerAppTask::ButtonEventHandlerImpl(uint8_t button, uint8_t btnAction)
{
SILABS_LOG("CustomerAppTask: custom implementation (ButtonEventHandlerImpl)");
AppEvent button_event = {};
button_event.Type = AppEvent::kEventType_Button;
button_event.ButtonEvent.Action = btnAction;
if (button == APP_LIGHT_SWITCH && btnAction == static_cast<uint8_t>(SilabsPlatform::ButtonAction::ButtonPressed))
{
button_event.Handler = LightActionEventHandler;
AppTask::GetAppTask().PostEvent(&button_event);
}
else if (button == APP_FUNCTION_BUTTON)
{
button_event.Handler = BaseApplication::ButtonHandler;
AppTask::GetAppTask().PostEvent(&button_event);
}
}Override API Reference#
The base API and implementation are generated into the autogen/ directory. These files are regenerated whenever you upgrade the project and match the installed SDK version. Use them as a reference for overridable methods and app configuration.
File | Purpose |
|---|---|
| Declarations of every overridable |
| Silicon Labs provides the default |
Using Matter with Clusters#
In Matter, commands can be issued by using a cluster. A cluster is a set of attributes and commands which are grouped together under a relevant theme.
Attributes store values (think of them as variables). Commands are used to modify the value of attributes.
For example, the "On/Off" cluster has an attribute named "OnOff" of type boolean. The value of this attribute can be set to "1" by sending an "On" command or it can be set to "0" by sending an "Off" command.
The C++ implementation of these clusters is located in the clusters directory. Note that you can also create your own custom cluster.
ZAP Configuration#
In Studio, navigate to the ZAP UI in your project by double clicking the zap file
located at config/zap/lighting-app.zap.
On the left side of the application, there is a tab for Endpoint 0 and Endpoint 1. Endpoint 0 is known as the root node. This endpoint is akin to a "read me first" endpoint that describes itself and the other endpoints that make up the node. Endpoint 1 represents a lighting application device type. There are a number of required ZCL clusters enabled in Endpoint 1. Some clusters are common across most device types, such as identify and group clusters. Others, such as the On/Off, Level Control and Color Control clusters are required for a lighting application device type.
Clicking on the blue settings icon on the right side of the application brings you to the zap configuration settings for that cluster. Each cluster has some required attributes that may cause compile-time errors if they are not selected in the zap configuration. Other attributes are optional and are allowed to be disabled. Clusters also have a list of client-side commands, again some are mandatory and others are optional depending on the cluster. ZCL offers an extensive list of optional attributes and commands that allow you to customize your application to the full power of the Matter SDK.
For example, if a lighting application only includes single color LEDs instead of RGB LEDs, it might make sense to disable the Color Control cluster in the ZAP configuration. Similarly, if a lighting application does not take advantage of the Level Control cluster, which allows you to customize current flow to an LED, it might make sense to disable the Level Control cluster.
Receiving Matter Commands#
All Matter commands reach the application through the intermediate function
MatterPostAttributeChangeCallback(). When a request is made by a Matter client,
the information contained in the request is forwarded to a Matter application
through this function. The command can then be dissected using conditional logic
to call the proper application functions based on the most recent command
received.
Attribute changes route through CustomerAppTask and DMPostAttributeChangeCallbackImpl(). For more information, see Extending Base App Implementation.
Adding a Cluster to a ZAP Configuration#
In the ZAP UI, navigate to the Level Control cluster. Make sure this cluster is enabled as a server in the drop-down menu in the "Enable" column. Then click on the blue settings wheel in the "Configure" column. This cluster can be used to gather power source configuration settings from a Matter device. It contains a few required attributes, and a number of optional attributes.
Adding a New Attribute#
In the Level Control cluster configurations, ensure the CurrentLevel attribute is set to enabled. Set the default value of this attribute as 1.
Adding a New Command#
Navigate to the commands tab in zap and enable the MoveToLevel command. Now save the current zap configuration, and run the generate.py script above.
React to Level Control Cluster Commands#
In a custom implementation of DMPostAttributeChangeCallbackImpl() in src/CustomerAppTask.cpp, add the following or similar code. This enables the application to react to the MoveToLevel commands.
else if (clusterId == LevelControl::Id)
{
ChipLogProgress(Zcl, "Level Control attribute ID: " ChipLogFormatMEI " Type: %u Value: %u, length %u",
ChipLogValueMEI(attributeId), type, *value, size);
if (attributeId == LevelControl::Attributes::CurrentLevel::Id)
{
sLightLED.SetLevel(*value);
}
}Send a MoveToLevel Command and Read the CurrentLevel Attribute#
Rebuild the application and load the new executable on your EFR32 device. Send the following mattertool commands and verify that the current-level default attribute was updated as was configured. Replace {desired_level} with 10, and node_ID with the node ID assigned to the device upon commissioning.
$ mattertool levelcontrol read current-level 1 1 // Returns 1$ mattertool levelcontrol move-to-level {desired_level} 0 1 1 {node_ID} 1$ mattertool levelcontrol read current-level 1 1 // Returns 10Defining a Custom Cluster#
In order to use a custom cluster in an application, follow these steps:
Create an XML file with custom cluster definitions. For an example, see Sample MEI Cluster.
In ZAP, click Extensions and add the XML file. The newly defined cluster can then be enabled in any endpoint under the domain for which it was defined (for example General). Its Commands and Attributes can be managed like those of any other cluster. Click Ctrl+S to save the changes.
Manually edit the project's .zap file (located in config/zap/ in the project directory) to add the following block to the "keyValuePairs" array.
{
"key": "generateStaticTemplates",
"value": "true"
} For an example, see Sample ZAP file
Install the Custom ZAP generation component under Silicon Labs Matter -> Stack in the project's Component Editor.