Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 119 additions & 42 deletions sld250-matter-references/custom-matter-device.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,130 @@ Build a customizable lighting app using the Matter protocol.
This guide covers the basics of building a customizable lighting application
using Matter.

## Application Customization Models
## Extending Base App Implementation

Matter Extension 2.9.0 migrates a subset of sample apps to the Curiously Recurring Template Pattern (CRTP) based architecture, which removes app manager and DataModelCallbacks files. All other sample apps keep the previous architecture until the patch release.
### CustomerAppTask

Check your project in Project Explorer:
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

| If you see… | Architecture | Where to add custom logic |
|---|---|---|
| `src/CustomerAppTask.cpp` and `autogen/AppTask.cpp` | **New** | Override `*Impl()` hooks in `CustomerAppTask`, do not edit `autogen/AppTask.cpp` |
| `src/DataModelCallbacks.cpp` and editable `src/AppTask.cpp` | **Legacy** | Callbacks in `DataModelCallbacks.cpp`, init and app logic in `src/AppTask.cpp` |
`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:

**Sample apps on the new architecture in 2.9.0:**
1. Find the method to override in the base API. For more information, see [Override API reference](#override-api-reference).
2. Declare the same method signature in `CustomerAppTask.h` under the `private:` 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.
3. Implement the method in `CustomerAppTask.cpp`.
4. Build the project. If you implement the corresponding `*Impl()` method in `CustomerAppTask`, 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.

- Lighting
- Zigbee Matter Light
- On/Off Plug
- Thermostat
- Lock
- Light Switch
- Rangehood
- Platform Template
- Air Quality Sensor
### DataModelCallbacks and CustomerAppTask

All other Silicon Labs Matter sample apps in this release use the legacy model. Related guides label steps as **New architecture** or **Legacy architecture** where they differ.
What used to live in `DataModelCallbacks.cpp` before Matter Extension 2.9.0 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](#how-to-override-apis).

- **Methods that already exist in the AppTask** — Customize them by overriding
the matching `*Impl()` method in `CustomerAppTask`. Do not edit the
`AppTask.cpp` for app-specific behavior.

- **New custom data model methods** — Add them in `CustomerAppTask` directly.
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**

```cpp
#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**

```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 |
|------|--------|
| `autogen/AppTaskImpl.h` | Declarations of every overridable `*Impl()` method. Copy the signatures you need from here into `CustomerAppTask.h`. |
| `autogen/AppTask.cpp` | Silicon Labs provides the default `AppTask` implementation. Any `*Impl()` methods that you don't override use this implementation. Use it as a reference when customizing app behavior. |

## Using Matter with Clusters

Expand Down Expand Up @@ -86,7 +186,7 @@ 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.

Depending on your sample application, edit the files as described in [Application Customization Models](#application-customization-models). New architecture apps route attribute changes through `CustomerAppTask` and `DMPostAttributeChangeCallbackImpl()`. Legacy architecture apps implement `MatterPostAttributeChangeCallback()` directly in `src/DataModelCallbacks.cpp`.
Attribute changes route through `CustomerAppTask` and `DMPostAttributeChangeCallbackImpl()`. For more information, see [Extending Base App Implementation](#extending-base-app-implementation).

## Adding a Cluster to a ZAP Configuration

Expand All @@ -108,8 +208,6 @@ the current zap configuration, and run the generate.py script above.

## React to Level Control Cluster Commands

### New Architecture

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.

```cpp
Expand All @@ -125,27 +223,6 @@ In a custom implementation of `DMPostAttributeChangeCallbackImpl()` in `src/Cust
}
```

### Legacy Architecture

In the MatterPostAttributeCallback function in ZclCallbacks, add the following
line of code or a similar line. This will give the application the ability to react to
MoveToLevel commands. You can define platform-specific behavior for a
MoveToLevel action.
```cpp
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)
{
action_type = LightingManager::MOVE_TO_LEVEL;
}

LightMgr().InitiateActionLight(AppEvent::kEventType_Light, action_type, endpoint, *value);
}
```

## Send a MoveToLevel Command and Read the CurrentLevel Attribute

Rebuild the application and load the new executable on your EFR32 device. Send
Expand Down
36 changes: 2 additions & 34 deletions sld295-matter-api-reference/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ Attributes represent the current state of a device. For instance if the device i

## Attribute Changes

Depending on your sample app, instructions apply. For more information, refer to [Application customization models](/matter/{build-docspace-version}/matter-references/custom-matter-device/#application-customization-models).

### New Architecture

When a ZCL attribute is updated in the data model, the framework invokes the post-attribute-change path. The Silicon Labs Matter stack routes this as follows: `MatterPostAttributeChangeCallback` in `BaseApplication.cpp` → `AppTask::DMPostAttributeChangeCallback` in `autogen/AppTask.cpp` → your optional `DMPostAttributeChangeCallbackImpl()` override in `CustomerAppTask`.

For more information, see [Extending Base App Implementation](/matter/{build-docspace-version}/matter-references/custom-matter-device/#extending-base-app-implementation).

If this callback is implemented by the device, it is informed of the attribute change. The device may react to the attribute change. For example, in `DMPostAttributeChangeCallback` function the in `AppTask.cpp` file ([onoff-plug-app/src](https://github.com/SiliconLabsSoftware/matter_sdk/blob/main/examples/onoff-plug-app/silabs/src/AppTask.cpp)), if you want to add a custom handler code to control an RGB LED when on/off attribute in the `On-Off` Cluster changes, implement the following in `DMPostAttributeChangeCallbackImpl` in `src/CustomerAppTask.cpp`:

```cpp
Expand Down Expand Up @@ -39,36 +37,6 @@ void DMPostAttributeChangeCallbackImpl(const chip::app::ConcreteAttributePath &
}
```

### Legacy Architecture

When a ZCL attribute is updated in the data model, the framework calls `MatterPostAttributeChangeCallback()` in `src/DataModelCallbacks.cpp`. If this callback is implemented by the device it will be informed of the attribute change. For example, to control an RGB LED when the On/Off attribute changes:

```cpp
void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath,
uint8_t type,
uint16_t size,
uint8_t * value)
{
ClusterId clusterId = attributePath.mClusterId;
AttributeId attributeId = attributePath.mAttributeId;
ChipLogProgress(Zcl, "Cluster callback: " ChipLogFormatMEI, ChipLogValueMEI(clusterId));

if (clusterId == OnOff::Id && attributeId == OnOff::Attributes::OnOff::Id)
{
if (*value)
{ // turn on LED
sl_led_turn_on((sl_led_t *)&sl_simple_rgb_pwm_led_rgb_led0);
}
else
{// turn off LED
sl_led_turn_off((sl_led_t *)&sl_simple_rgb_pwm_led_rgb_led0);
}
}

//...
}
```

## Header File

This file contains the high level namespaces and constant definitions for Attributes. In Simplicity Studio, this will be generated in the autogen/zap-generated/ folder of the matter project.
Expand Down
2 changes: 1 addition & 1 deletion sld295-matter-api-reference/event.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Events are records of past state transitions such as a light device's on-off att

The autogenerated file [```include/AppEvent.h```](https://github.com/SiliconLabs/matter_extension/blob/main/third_party/matter_sdk/examples/template/silabs/include/AppEvent.h) contains the definition of the event object used by the application. It includes the event types, the structures for each event, and event handler.

**New architecture:** Custom event posting and handlers are overridden in the `CustomerAppTask` file, not in `autogen/AppTask.cpp`.
Custom event posting and handlers are overridden in the `CustomerAppTask` file, not in `autogen/AppTask.cpp`.

## Header File

Expand Down
6 changes: 2 additions & 4 deletions sld295-matter-api-reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@ This section covers the various Application Programming Interfaces (APIs) that a

## Application APIs

For guidance on new vs legacy sample app architecture and where to add custom logic, see [Application Customization Models](/matter/{build-docspace-version}/matter-references/custom-matter-device/#application-customization-models).
For information about customizing app behavior with `CustomerAppTask`, see [Extending Base App Implementation](/matter/{build-docspace-version}/matter-references/custom-matter-device/#extending-base-app-implementation).

### Initialization

**New architecture:** Default initialization is included in `autogen/AppTask.cpp`. Override `AppInitImpl()` and other `*Impl()` hooks in `src/CustomerAppTask.cpp` to customize behavior.

**Legacy architecture:** The application Init sequence is included in `src/AppTask.cpp` and is called at the beginning of the application to ensure that all components are properly initialized and ready to operate. It sets up necessary callbacks, initializes hardware components, and handles any errors that may occur during the process.
Default initialization is included in `autogen/AppTask.cpp`. Override `AppInitImpl()` and other `*Impl()` hooks in `src/CustomerAppTask.cpp` to customize behavior.

```cpp
CHIP_ERROR AppTask::Init()
Expand Down
2 changes: 1 addition & 1 deletion sld57-matter-landing-page/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Want to get a Matter application up and running quickly? Here's a high-level ove

4. **[Optional] Customize Matter App Behavior and Logic**
- Use Project Configurator and other Studio tools to customize your app logic: [Developing with Project Configurator](https://docs.silabs.com/ssv6ug/latest/ssv6-configure-project/)
- Customize app behavior using the model your sample app provides — see [Application Customization Models](/matter/{build-docspace-version}/matter-references/custom-matter-device/#application-customization-models). Refactored apps use `CustomerAppTask` and `autogen/AppTask.cpp`, all other apps use `DataModelCallbacks.cpp` and `src/AppTask.cpp`.
- Customize app behavior in `CustomerAppTask` and `autogen/AppTask.cpp`. For more information, see [Extending Base App Implementation](/matter/{build-docspace-version}/matter-references/custom-matter-device/#extending-base-app-implementation).
- Follow documentation to develop a custom matter device with ZAP and corresponding callbacks: [Custom Matter Device Development](/matter/{build-docspace-version}/matter-references/custom-matter-device)

5. **Build and Flash**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,15 @@ Now that the On/Off cluster has been successfully added to the Sample Door Lock

- Attributes, commands, and events for the cluster are added to your application’s data model.
- Code is generated for attribute storage, command handling, and event notification.
- **New architecture**: implement application-specific behavior in `src/CustomerAppTask.cpp` by overriding `*Impl()` hooks (for example `DMPostAttributeChangeCallbackImpl()`), not by editing `autogen/AppTask.cpp`.
- **Legacy architecture**: Callback stubs are generated for you to implement application-specific behavior. You interact with the cluster by filling in these stubs and using the generated data structures.
- Implement application-specific behavior in `src/CustomerAppTask.cpp` by overriding `*Impl()` hooks, such as `DMPostAttributeChangeCallbackImpl()`, instead of editing `autogen/AppTask.cpp`.

Additionally, a corresponding component is automatically added to your project. This occurs because enabling a cluster in ZAP updates your project configuration to include the necessary software components and libraries required to support that cluster’s functionality. For clusters, this functionality is implemented in the `<matter_extension>/third_party/matter_sdk/src/app/clusters` directory. For the On/Off cluster, the server command handlers and related logic can be found in the `/on-off-server/on-off-server.cpp` file.

## Step 4: Add Application Logic

This guide uses the Lock sample app, which is on the **new architecture** in 2.9.0. See [Application Customization Models](/matter/{build-docspace-version}/matter-references/custom-matter-device/#application-customization-models) to confirm which model your project uses.
For information about customizing app behavior, see [Extending Base App Implementation](/matter/{build-docspace-version}/matter-references/custom-matter-device/#extending-base-app-implementation).

### New Architecture

Application logic centers on AppTask, but refactored Matter projects split responsibilities:
Application logic centers on AppTask:

- `autogen/AppTask.cpp`: default implementation, regenerated on project upgrade (do not edit for application logic, use as reference only).
- `src/CustomerAppTask.cpp` and `include/CustomerAppTask.h`: your custom logic, add custom code and override `*Impl()` hooks here.
Expand Down Expand Up @@ -131,41 +128,6 @@ Finally, add a call to `OnOffTmrStart()` at the end of your `AppInitImpl()` over

In the flowchart above, `OnOffAttributeWriteStartTimer()` calls `OnOffTmrStart()` to restart the timer.

### Legacy Architecture

Locate your project's src/AppTask.cpp file. This file acts as the central hub for application-specific logic, initialization, and event processing in a Matter application on Silicon Labs platforms. Start by adding two helper functions: a one-shot timer to expire in 10 seconds and the OnOffTmrExpiryHandler handler function.

Include `app-common/zap-generated/attributes/Accessors.h` in your `AppTask.cpp` file, so that you can access cluster attributes.

Add the timer start function to `AppTask.cpp` and declare it in `AppTask.h`:

```C++
void AppTask::OnOffAttributeWriteStartTimer()
{
OnOffTmrStart();
}
```

Now, locate the MatterPostAttributeChangeCallback() function in the src/DataModelCallbacks.cpp file. This function is invoked by the application framework after an attribute value has been changed. Because you are modifying the OnOff attribute in the OnOffTmrExpiryHandler() function, use this callback to re-initiate the timer so that the attribute continues to toggle. To achieve this, call AppTask::OnOffAttributeWriteStartTimer(), which is part of the AppTask context.

```C++
void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size,
uint8_t * value)
{
ClusterId clusterId = attributePath.mClusterId;
AttributeId attributeId = attributePath.mAttributeId;

if (clusterId == OnOff::Id && attributeId == OnOff::Attributes::OnOff::Id){
AppTask::GetAppTask().OnOffAttributeWriteStartTimer();
}

}
```

Make sure to #include "AppTask.h" at the top of the DataModelCallbacks.cpp file to call the AppTask::GetAppTask() function. For more information on the AppTask, refer to AppTask.h.

Finally, add a call to OnOffTmrStart() at the end of the AppTask::AppInit() function to start the attribute write sequence.

## Step 5: Interact with the On/Off Cluster

After building your project, flash the compiled firmware onto your target board. Once the device is running, you should observe log messages approximately every 10 seconds indicating that the OnOff cluster's OnOff attribute is being written to. This confirms that the cluster is active and functioning as expected.
Expand Down
Loading