Skip to content

Commit bb0a75d

Browse files
[Silabs] Air quality sensor app refactor (Auto-merged by platform-bot)
* [SL-ONLY] Air quality sensor app refactor (project-chip#973) * initial consolidation into AppTask * remove unused AppTask methods * some cleanup * remove correct unused button handler * implement new API for sensor value get * apply crtp * apply override for InitAirQualitySensor * rename GetAirQuality -> GetAirQualityValue to avoid confusion with common code * add readme * fix copy sources llvm build * expose WriteAirQualityToAttribute to customer as non-override * add missing header to readme file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 4164983 commit bb0a75d

11 files changed

Lines changed: 454 additions & 309 deletions

File tree

examples/air-quality-sensor-app/silabs/BUILD.gn

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ if (wifi_soc) {
6666
"${examples_plat_dir}",
6767
"${chip_root}/src/lib",
6868
"${examples_common_plat_dir}",
69+
"${examples_common_plat_dir}/customer",
6970
"${examples_common_plat_dir}/freertos_config",
7071
]
7172

@@ -90,6 +91,7 @@ if (wifi_soc) {
9091
"${examples_plat_dir}",
9192
"${chip_root}/src/lib",
9293
"${examples_common_plat_dir}",
94+
"${examples_common_plat_dir}/customer",
9395
"${examples_common_plat_dir}/freertos_config",
9496
]
9597

@@ -123,9 +125,8 @@ silabs_executable("air_quality_sensor_app") {
123125

124126
sources = [
125127
"${chip_root}/examples/air-quality-sensor-app/air-quality-sensor-common/src/air-quality-sensor-manager.cpp",
128+
"${examples_common_plat_dir}/customer/CustomerAppTask.cpp",
126129
"src/AppTask.cpp",
127-
"src/DataModelCallbacks.cpp",
128-
"src/SensorManager.cpp",
129130
]
130131

131132
#TODO: This is a placeholder for the actual implementation

examples/air-quality-sensor-app/silabs/README.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ An example showing the use of CHIP on the Silicon Labs EFR32 MG24.
66

77
- [Matter Air Quality Sensor Example](#matter-air-quality-sensor-example)
88
- [Introduction](#introduction)
9+
- [Extending Base App Implementation](#extending-base-app-implementation)
10+
- [CustomerAppTask](#customerapptask)
11+
- [How to Override APIs](#how-to-override-apis)
12+
- [DataModelCallbacks and CustomerAppTask](#datamodelcallbacks-and-customerapptask)
13+
- [Sample Implementation](#sample-implementation)
14+
- [Override API Reference](#override-api-reference)
915
- [Building](#building)
1016
- [Linux](#linux)
1117
- [Mac OS X](#mac-os-x)
@@ -55,6 +61,148 @@ The air quality sensor example is intended to serve both as a means to explore
5561
the workings of Matter as well as a template for creating real products based on
5662
the Silicon Labs platform.
5763

64+
## Extending Base App Implementation
65+
66+
### CustomerAppTask
67+
68+
To implement custom app behavior you can override any Silicon Labs implemented
69+
API in the CustomerAppTask file. This example provides
70+
[`CustomerAppTask.h`](../../platform/silabs/customer/CustomerAppTask.h) and
71+
[`CustomerAppTask.cpp`](../../platform/silabs/customer/CustomerAppTask.cpp) for
72+
that purpose. The base implementation and the full set of overridable `*Impl()`
73+
APIs live in this example's source tree under
74+
[`include/AppTaskImpl.h`](include/AppTaskImpl.h) and
75+
[`src/AppTask.cpp`](src/AppTask.cpp). Any `*Impl()` you do not override keeps
76+
the Silicon Labs default behavior. To customize behavior, copy
77+
[`CustomerAppTask.h`](../../platform/silabs/customer/CustomerAppTask.h) and
78+
[`CustomerAppTask.cpp`](../../platform/silabs/customer/CustomerAppTask.cpp) from
79+
`examples/platform/silabs/customer/` into this app's `include/` and `src/`
80+
folders, then update the corresponding paths in `BUILD.gn`.
81+
82+
### How to Override APIs
83+
84+
`CustomerAppTask` derives from the base AppTask through the Curiously Recurring
85+
Template Pattern (CRTP). You override only the `*Impl()` methods you need, the
86+
base declares one `*Impl()` per overridable API. Steps:
87+
88+
1. Find the method to override in the base API (see
89+
[Override API reference](#override-api-reference) below).
90+
2. Declare the same method signature in `CustomerAppTask` in your
91+
`CustomerAppTask.h` under `private:`. Match the base `*Impl()` signature
92+
exactly — note that `*Impl()` overrides are **non-static instance methods**
93+
even when the public dispatcher (e.g. `ButtonEventHandler`) is `static`.
94+
3. Implement the method in `CustomerAppTask.cpp`.
95+
4. Build. The CRTP layer automatically routes each call to your `*Impl()` if
96+
present, otherwise to the Silicon Labs default.
97+
98+
### DataModelCallbacks and CustomerAppTask
99+
100+
What used to live in `DataModelCallbacks.cpp` now lives in `AppTask.cpp`. The
101+
Matter SDK's `MatterPostAttributeChangeCallback` is implemented in
102+
`examples/platform/silabs/BaseApplication.cpp` and forwards to
103+
`AppTask::DMPostAttributeChangeCallback` (defined in `AppTask.cpp`), which you
104+
can customize via `DMPostAttributeChangeCallbackImpl()` in `CustomerAppTask`.
105+
106+
Forwarding into `AppTask` still goes through CRTP as in
107+
[How to Override APIs](#how-to-override-apis).
108+
109+
- **Methods that already exist in the AppTask** — Customize them by overriding
110+
the matching `*Impl()` method in `CustomerAppTask`. Do not edit the
111+
`AppTask.cpp` for app-specific behavior.
112+
113+
- **New custom data model methods** — Add them in `CustomerAppTask` directly.
114+
Do not add new application logic in autogenerated sources; those edits will
115+
not survive regeneration or project upgrades.
116+
117+
### Sample Implementation
118+
119+
The following shows a minimal example `CustomerAppTask` that overrides
120+
`AppInitImpl()` and `ButtonEventHandlerImpl()`.
121+
122+
**CustomerAppTask.h**
123+
124+
```cpp
125+
#pragma once
126+
#include "AppTaskImpl.h"
127+
128+
/** Minimal AppTaskImpl-derived class. Override only the *Impl() methods you need **/
129+
class CustomerAppTask : public AppTaskImpl<CustomerAppTask>
130+
{
131+
public:
132+
static CustomerAppTask & GetAppTask() { return sAppTask; }
133+
134+
private:
135+
friend class AppTaskImpl<CustomerAppTask>;
136+
CHIP_ERROR AppInitImpl();
137+
void ButtonEventHandlerImpl(uint8_t button, uint8_t btnAction);
138+
static CustomerAppTask sAppTask;
139+
};
140+
```
141+
142+
**CustomerAppTask.cpp**
143+
144+
```cpp
145+
#include "CustomerAppTask.h"
146+
#include "AppTask.h"
147+
#include "AppEvent.h"
148+
#include <platform/CHIPDeviceLayer.h>
149+
#include <platform/silabs/platformAbstraction/SilabsPlatform.h>
150+
151+
#define APP_FUNCTION_BUTTON 0
152+
153+
CustomerAppTask CustomerAppTask::sAppTask;
154+
155+
AppTask & AppTask::GetAppTask()
156+
{
157+
return CustomerAppTask::GetAppTask();
158+
}
159+
160+
CHIP_ERROR CustomerAppTask::AppInitImpl()
161+
{
162+
SILABS_LOG("CustomerAppTask: custom implementation (AppInitImpl)");
163+
CHIP_ERROR err = AppTask::AppInit();
164+
if (err == CHIP_NO_ERROR)
165+
{
166+
// Override the SDK default button handler registered in AppTask::AppInit().
167+
chip::DeviceLayer::Silabs::GetPlatform().SetButtonsCb(CustomerAppTask::ButtonEventHandler);
168+
}
169+
return err;
170+
}
171+
172+
void CustomerAppTask::ButtonEventHandlerImpl(uint8_t button, uint8_t btnAction)
173+
{
174+
SILABS_LOG("CustomerAppTask: custom implementation (ButtonEventHandlerImpl)");
175+
AppEvent aEvent = {};
176+
aEvent.Type = AppEvent::kEventType_Button;
177+
aEvent.ButtonEvent.Action = btnAction;
178+
179+
if (button == APP_FUNCTION_BUTTON)
180+
{
181+
aEvent.Handler = BaseApplication::ButtonHandler;
182+
AppTask::GetAppTask().PostEvent(&aEvent);
183+
}
184+
}
185+
```
186+
187+
### Override API Reference
188+
189+
`CHIP_ERROR StartAppTask()` is declared on `AppTask` only. It is not an
190+
`*Impl()` hook: platform code (for example `MatterConfig`) calls
191+
`AppTask::GetAppTask().StartAppTask()` with static type `AppTask &`, which runs
192+
the implementation in `AppTask.cpp` (creating the FreeRTOS app task via
193+
`BaseApplication::StartAppTask(...)`). To change startup behavior, edit
194+
`AppTask::StartAppTask()` or `BaseApplication::StartAppTask` in your product
195+
sources.
196+
197+
The base API and default AppTask behavior for this example are maintained under
198+
[`include/`](include/AppTaskImpl.h) and [`src/`](src/AppTask.cpp). Use them as
199+
the reference for overridable methods and app configuration.
200+
201+
| File | Purpose |
202+
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
203+
| [`include/AppTaskImpl.h`](include/AppTaskImpl.h) | Declarations of every overridable `*Impl()` method. Copy the signatures you need from here into `CustomerAppTask.h`. |
204+
| [`src/AppTask.cpp`](src/AppTask.cpp) | Silicon Labs default implementation of AppTask. This is what runs for any `*Impl()` you do not override. Use as reference when customizing behavior. |
205+
58206
## Building
59207

60208
- Download the

examples/air-quality-sensor-app/silabs/include/AirQualityConfig.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646
// <i> Default: 300
4747
#define EXTREMELY_POOR_THRESHOLD 300
4848

49+
// <o SENSOR_TIMER_PERIOD_MS> Sensor sampling period in milliseconds
50+
// <i> Default: 30000 (30 seconds)
51+
#define SENSOR_TIMER_PERIOD_MS 30000
52+
4953
// <<< end of configuration section >>>
5054

5155
#endif // SL_MATTER_AIR_QUALITY_CONFIG_H

examples/air-quality-sensor-app/silabs/include/AppTask.h

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* Includes
2424
*********************************************************/
2525

26+
#include <cstdint>
2627
#include <stdbool.h>
2728
#include <stdint.h>
2829

@@ -32,9 +33,8 @@
3233

3334
#include "AppEvent.h"
3435
#include "BaseApplication.h"
35-
#include "SensorManager.h"
36+
#include <app/ConcreteAttributePath.h>
3637
#include <ble/BLEEndPoint.h>
37-
#include <cmsis_os2.h>
3838
#include <lib/core/CHIPError.h>
3939
#include <platform/CHIPDeviceLayer.h>
4040

@@ -59,7 +59,7 @@ class AppTask : public BaseApplication
5959
public:
6060
AppTask() = default;
6161

62-
static AppTask & GetAppTask() { return sAppTask; }
62+
static AppTask & GetAppTask();
6363

6464
/**
6565
* @brief AppTask task main loop function
@@ -71,10 +71,15 @@ class AppTask : public BaseApplication
7171
CHIP_ERROR StartAppTask();
7272

7373
/**
74-
* @brief Request an update of the Air Quality Senor LCD UI
74+
* @brief Request an update of the Air Quality Sensor LCD UI
7575
*/
7676
void UpdateAirQualitySensorUI();
7777

78+
/**
79+
* @brief Update the Air Quality cluster attribute
80+
*/
81+
static void WriteAirQualityToAttribute(intptr_t context);
82+
7883
/**
7984
* @brief Event handler when a button is pressed
8085
* Function posts an event for button processing
@@ -85,24 +90,25 @@ class AppTask : public BaseApplication
8590
*/
8691
static void ButtonEventHandler(uint8_t button, uint8_t btnAction);
8792

88-
private:
89-
static AppTask sAppTask;
93+
void DMPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size,
94+
uint8_t * value);
9095

9196
/**
92-
* @brief Override of BaseApplication::AppInit() virtual method, called by BaseApplication::Init()
97+
* @brief Read the current raw air quality sensor value into @p air_quality.
9398
*
94-
* @return CHIP_ERROR
99+
* Calls `AirQualitySensor::GetAirQuality` when `USE_AIR_QUALITY_SENSOR` is set,
100+
* otherwise steps through a simulated table. On error @p air_quality is left untouched and
101+
* the caller skips scheduling a cluster update for that tick.
95102
*/
96-
CHIP_ERROR AppInit() override;
103+
CHIP_ERROR GetAirQualityValue(int32_t & air_quality);
97104

98-
/**
99-
* @brief PB0 Button event processing function
100-
* Press and hold will trigger a factory reset timer start
101-
* Press and release will restart BLEAdvertising if not commisionned
102-
*
103-
* @param aEvent button event being processed
104-
*/
105-
static void ButtonHandler(AppEvent * aEvent);
105+
// Reads new generated sensor value, stores it, and updates local Air Quality attribute
106+
static void SensorTimerEventHandler(void * arg);
107+
108+
protected:
109+
/** Override of `BaseApplication::AppInit()`. */
110+
CHIP_ERROR AppInit() override;
106111

107-
static void AirQualitySensorActionEventHandler(AppEvent * aEvent);
112+
/** Bring up the air quality sensor app: Matter manager, sensor timer, sensor driver, first reading. */
113+
CHIP_ERROR InitAirQualitySensor();
108114
};
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
*
3+
* Copyright (c) 2020-2024 Project CHIP Authors
4+
* Copyright (c) 2019-2024 Google LLC.
5+
* All rights reserved.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
#pragma once
21+
22+
#include "AppTask.h"
23+
#include "CRTPHelpers.h"
24+
25+
/**
26+
* @brief CRTP base for air quality AppTask, exposing override hooks for customizable APIs.
27+
*
28+
* Each public method dispatches to `Derived::*Impl()`. Overrides are optional: default
29+
* `*Impl()` implementations in the private section forward to `AppTask`. Override in
30+
* `CustomerAppTask` only for the behaviors you want to customize.
31+
*
32+
* @tparam Derived The derived class type (CRTP pattern)
33+
*/
34+
template <typename Derived>
35+
class AppTaskImpl : public AppTask
36+
{
37+
public:
38+
CHIP_ERROR AppInit() override { CRTP_OPTIONAL_DISPATCH(AppTaskImpl, Derived, AppInitImpl); }
39+
40+
CHIP_ERROR InitAirQualitySensor() { CRTP_OPTIONAL_DISPATCH(AppTaskImpl, Derived, InitAirQualitySensorImpl); }
41+
42+
CHIP_ERROR GetAirQualityValue(int32_t & air_quality)
43+
{
44+
CRTP_OPTIONAL_DISPATCH_ARGS(AppTaskImpl, Derived, GetAirQualityValueImpl, air_quality);
45+
}
46+
47+
static void ButtonEventHandler(uint8_t button, uint8_t btnAction)
48+
{
49+
CRTP_OPTIONAL_STATIC_DISPATCH(AppTaskImpl, Derived, ButtonEventHandlerImpl, button, btnAction);
50+
}
51+
52+
static void SensorTimerEventHandler(void * arg)
53+
{
54+
CRTP_OPTIONAL_STATIC_DISPATCH(AppTaskImpl, Derived, SensorTimerEventHandlerImpl, arg);
55+
}
56+
57+
void DMPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size,
58+
uint8_t * value)
59+
{
60+
CRTP_OPTIONAL_VOID_DISPATCH(AppTaskImpl, Derived, DMPostAttributeChangeCallbackImpl, attributePath, type, size, value);
61+
}
62+
63+
private:
64+
friend Derived;
65+
66+
CHIP_ERROR AppInitImpl() { return AppTask::AppInit(); }
67+
68+
CHIP_ERROR InitAirQualitySensorImpl() { return AppTask::InitAirQualitySensor(); }
69+
70+
CHIP_ERROR GetAirQualityValueImpl(int32_t & air_quality) { return AppTask::GetAirQualityValue(air_quality); }
71+
72+
void ButtonEventHandlerImpl(uint8_t button, uint8_t btnAction) { AppTask::ButtonEventHandler(button, btnAction); }
73+
74+
void SensorTimerEventHandlerImpl(void * arg) { AppTask::SensorTimerEventHandler(arg); }
75+
76+
void DMPostAttributeChangeCallbackImpl(const chip::app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size,
77+
uint8_t * value)
78+
{
79+
AppTask::DMPostAttributeChangeCallback(attributePath, type, size, value);
80+
}
81+
};

examples/air-quality-sensor-app/silabs/include/CHIPProjectConfig.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,6 @@
108108
*
109109
*/
110110
#define CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL (2000_ms32)
111+
112+
// Temporary setting to use CustomerAppTask for apps which are not upgraded to new architecture
113+
#define CHIP_SILABS_APP_USE_CUSTOMER_APP_TASK

0 commit comments

Comments
 (0)