Writing a Code-Driven Application#
Convert all-devices-app from a runtime multi-device simulator into a
fixed-function Matter product by retaining platform setup, RootNode, and spec
base classes (all-devices-common/device/types/) while stripping
DeviceFactory and impl/Logging* mocks.
1. Simulator vs. Product Architecture#
Layer / Component |
|
Product Application Baseline |
|---|---|---|
Platform Entrypoint & Hardware Setup |
|
Reuse. Keep platform initialization, event loop, and |
Endpoint 0 (Root Node) |
Reuse. Compose |
|
Base Device Types |
|
Reuse. Inherit from these base classes, which wire the Descriptor cluster and mandatory server clusters. |
Hardware / Cluster Delegates |
|
Replace. Subclass the base device type and implement cluster |
Device Factory & Registries |
|
Remove. Instantiate |
Build-Time Multi-Device Lists |
Remove. Compile and link only |
|
Runtime Topology & Sample Peripherals |
CLI |
Remove. Register fixed |
2. Endpoint Topology Checklist#
Map Matter Base Device Type and Device Library requirements to C++ types before coding:
Endpoint |
Role |
Code-Driven Class / Hook |
Configuration |
|---|---|---|---|
|
Root Node |
Pass |
|
|
Application Device |
Mandatory clusters wired by base class. Pass feature maps and optional attributes via constructor |
3. Product Device Subclass#
Do not instantiate impl/Logging* classes in product builds. Subclass the
device base class (device/types/<device>/<Device>.h) and implement its cluster
Delegate interfaces to drive hardware peripherals (see
device/types/README.md):
Required Delegates & Shared Capabilities: Inspect
device/types/<device>/impl/Logging*.h(e.g.,LoggingSpeaker.h) to see which clusterDelegateinterfaces a base device type requires. Some device types are thin wrappers around shared capabilities indevice/capabilities/<capability>/(such asOnOffLoad,DimmableLoad,ColorLight, orFanLoad), where theirContext,Delegates,Config, andimpl/Logging*classes are defined.Base Initialization Order: When passing
*thisas a delegate reference to the<BaseDevice>constructor, declare theprivateDelegatebases beforepublic <BaseDevice>(as inLoggingDimmableLight.h) so the delegate base classes initialize first.Cluster Type Qualification & Access: Capability and device classes define
<Cluster>Cluster()accessor methods (e.g.,IdentifyCluster()), which shadow unqualified cluster type names in derived classes; qualify cluster parameter types in delegate overrides (e.g.,Clusters::IdentifyCluster &). If a capability base class declaresRegisterandUnregisterasprotected, expose them withusing <BaseDevice>::Register; using <BaseDevice>::Unregister;.Hardware Peripheral Reference: See
PosixSpeaker.horESP32DimmableLight.hfor compiled subclasses driving platform hardware.Logic Beyond Hardware Bindings: Keep the subclass to hardware calls. Caching, session handling, or protocol state machines belong in the cluster under
src/app/clusters/, where they can be unit tested.
class MyProductSpeaker : private Clusters::LevelControlDelegate,
private Clusters::OnOffDelegate,
public Speaker
{
public:
explicit MyProductSpeaker(TimerDelegate & timerDelegate) :
Speaker(*this, *this, timerDelegate)
{}
private:
void OnLevelChanged(uint8_t value) override;
void OnOffStartup(bool on) override;
void OnOnOffChanged(bool on) override;
};
4. Endpoint Registration#
Platform entrypoints (main.cpp / AppTask.cpp) already construct
CodeDrivenDataModelProvider and register RootNode on kRootEndpointId
(0):
Wi-Fi:
WifiRootNode(RootNodeWith<WifiFeature>,WifiRootNode.h)Thread:
ThreadRootNode(RootNodeWith<ThreadFeature>,ThreadRootNode.h)With OTA:
RootNodeWith<WifiFeature, OtaFeature>orRootNodeWith<ThreadFeature, OtaFeature>
Keep RootNode registration on kRootEndpointId (0) and replace the
DeviceFactory block with direct registration of the product device.
Single-endpoint devices (inheriting from
SingleEndpoint) take
EndpointId(1) directly, whereas multi-endpoint composed devices (inheriting
from DeviceInterface, such as
Oven, Refrigerator, or RoomAirConditioner) take an
EndpointIdAllocator
(e.g.,
ConsecutiveEndpointIdAllocator):
ReturnErrorOnFailure(mRootNode->Register(kRootEndpointId, *mDataModelProvider));
ReturnErrorOnFailure(mProductDevice->Register(EndpointId(1), *mDataModelProvider));
5. Production Providers#
Replace example and test providers (such as
all-devices-common/providers/ or
Examples::GetExampleDACProvider()) with hardware-backed implementations before
starting the Matter server:
DeviceAttestationCredentialsProvider: BindSetDeviceAttestationCredentialsProviderto the platform factory data partition or secure element instead ofAllDevicesExampleDACProvider/GetExampleDACProvider().DeviceInstanceInfoProvider&DeviceInfoProvider: Register the platform factory data provider viaSetDeviceInstanceInfoProviderandSetDeviceInfoProvider.CommissionableDataProvider: BindSetCommissionableDataProviderto factory-provisioned SPAKE2+ verifier, salt, iteration count, and discriminator instead of test defaults (CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE/CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR).
6. Build Systems & Platform Guides#
GN Build System:
CMake Build System: