Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Air Purifier Example App (#29861)
Browse files Browse the repository at this point in the history
* Adding Air Purifier example framework and basic app, WIP

* wip, started adding class structures

* A messy yet stable version of the air-purifier, doesn't build as need to merge bug fix from upstream, WIP

* Added TODO and tested following merge

* The Air Quality Cluster API changed so updated constructor to take hard-coded features for now

* Added common device callback interface and changed the MatterPostAttributeChangeCallback to call that

* Changed the way we initialise the singleton Air Purifier Manager

* Add air-purifier example for Ameba platform

* Fixed wrong cluster revision in zap for concentraitons

* Add air-purifier example for CC32xx platform

* Add LastChangedTime and ReplaceableProductList To zap For HEPAFREMON And ACFREMON

* Updates to zap file for missing attributes and commands

* Adding PICS for the example

* Add PostchangeCallback To CC32XX Example

needed for writing the percentSetting to percentCurrent

* Fix Zap File for RH

- set Min/max/ MeasuredValue
- enable tolerance attribute

* Fix Zap File For Temp Measurement

* Update ClusterRev For Thermostat From 5 To 6

* Update FeatureMap For Fan Control

* Regenerate Matter File From Previous Zap Changes

* Add Button Handler For CC32XX

As a workaround save the KVS to NV when long pressing the right button.
This can be used to save the state after e.g. a unpair.

* Use Common filter-delegates.cpp For CC32XX Example

* Switched thermostat pics to be AbsLimits on the setpoint

* Updated the zapfile for thermostat

* Adding thermostat manager

* Changed all temperatures to be *100 and added callthrough from top level manager to thermostat when handling attribute changes

* Added thermostat endpoint into init call

* Changed name of attribute write handlers

* Changed name of attribute write handlers

* Add Thermostat-manager To CC32XX example

* Add helper script for executing Air-Purifier tests

* Remove duplicate line in air-purifier test runner script

* Adding CHIP header comment to files

* Removing Thermostat as the tests are broken for our use case

* Removing TSTAT tests

* Adding PICS folder in common with XML and simple PICS

* Add Correct Device Type And Name

* Removing Thermostat as the tests are broken for our use case

* Add A Way To Reset The Matter Part Of The Device For CC32XX Air Purifier Example App

this just deletes the kvs.cfg file on long pressing SW3

* Update README

* Remove Commented Code

* Update Copyright For CC32xx To 2023

* Update Copyright to 2023 For Ameba

* Regenned after merging upstream

* Added clearer Fan Mode support and some comments

* Added comment re: TODO for thermostat endpoint

* Restyled by whitespace

* Restyled by clang-format

* Restyled by gn

* Restyled by prettier-markdown

* Restyled by shellharden

* Removed missing reference in cc32xx readme

* Adding README for linux and adding all README's to the docs toctree

* Attempt 2 at setting toctree for new readmes

* Fix whitespace in script

* Restyled by shfmt

* Fixed spelling mistake in Readme

* Update include paths

* Add Ameba air-purifier example readme file

* Add words to wordlist

* Removed PICS and script, changed cc32xx headers to use pragma once

* Restyled by clang-format

* Restyled by prettier-markdown

* Removed TODO's from common code as they were not relevant

* Revert "Add words to wordlist"

This reverts commit 894a129.

* Replace indentations with fenced code blocks and minor tidy up within air purifier example readme file

* Removed old debug comments from gn file

* Added linux air-purifier-app to the helper script for building examples

* Added air-purifier example for linux to CI

* Restyled by prettier-markdown

---------

Co-authored-by: Chris Mapp <[email protected]>
Co-authored-by: Graf Tobias <[email protected]>
Co-authored-by: Restyled.io <[email protected]>
4 people authored and pull[bot] committed Nov 28, 2023

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
1 parent 9e18935 commit 5210149
Showing 58 changed files with 23,329 additions and 3 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/examples-linux-standalone.yaml
Original file line number Diff line number Diff line change
@@ -186,6 +186,16 @@ jobs:
"./scripts/build/build_examples.py \
--target linux-x64-contact-sensor-no-ble-with-ui \
build"
- name: Build example Air Purifier
run: |
./scripts/run_in_build_env.sh \
"./scripts/build/build_examples.py \
--target linux-x64-air-purifier \
build"
.environment/pigweed-venv/bin/python3 scripts/tools/memory/gh_sizes.py \
linux debug air-purifier-app \
out/linux-x64-air-purifier/chip-air-purifier-app \
/tmp/bloat_reports/
- name: Uploading Size Reports
uses: ./.github/actions/upload-size-reports
if: ${{ !env.ACT }}
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -115,7 +115,9 @@
"condition_variable": "cpp",
"numeric": "cpp",
"random": "cpp",
"thread": "cpp"
"thread": "cpp",
"variant": "cpp",
"any": "cpp"
},
// Configure paths or glob patterns to exclude from file watching.
"files.watcherExclude": {
11 changes: 10 additions & 1 deletion docs/examples/index.md
Original file line number Diff line number Diff line change
@@ -303,11 +303,20 @@ window-app/**/README
resource-monitoring-app/**/README
```

## Air Quality Sensor example
## RVC example

```{toctree}
:glob:
:maxdepth: 1
rvc-app/README
```

## Air Purifier Example

```{toctree}
:glob:
:maxdepth: 1
air-purifier-app/**/README
```
24 changes: 24 additions & 0 deletions examples/air-purifier-app/air-purifier-common/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright (c) 2020 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import("//build_overrides/chip.gni")
import("${chip_root}/src/app/chip_data_model.gni")

chip_data_model("air-purifier-common") {
zap_file = "air-purifier-app.zap"

zap_pregenerated_dir =
"${chip_root}/zzz_generated/air-purifier-app/zap-generated"
is_server = true
}
2,348 changes: 2,348 additions & 0 deletions examples/air-purifier-app/air-purifier-common/air-purifier-app.matter

Large diffs are not rendered by default.

16,606 changes: 16,606 additions & 0 deletions examples/air-purifier-app/air-purifier-common/air-purifier-app.zap

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <app/util/af-types.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPError.h>

namespace chip {
namespace DeviceManager {

/**
* @brief
* This class provides a skeleton for all the callback functions. The functions will be
* called by other objects within the CHIP stack for specific events.
* Applications interested in receiving specific callbacks can specialize this class and handle
* these events in their implementation of this class.
*/
class DeviceManagerCallbacks
{
public:
virtual void PostAttributeChangeCallback(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, uint8_t type,
uint16_t size, uint8_t * value){};

Protocols::InteractionModel::Status PreAttributeChangeCallback(EndpointId endpoint, ClusterId clusterId,
AttributeId attributeId, uint8_t type, uint16_t size,
uint8_t * value)
{
return Protocols::InteractionModel::Status::Success;
};

virtual ~DeviceManagerCallbacks(){};
};

} // namespace DeviceManager
} // namespace chip
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <DeviceManager.h>
#include <air-quality-sensor-manager.h>
#include <app/clusters/fan-control-server/fan-control-server.h>
#include <filter-delegates.h>
#include <relative-humidity-sensor-manager.h>
#include <temperature-sensor-manager.h>

#pragma once

namespace chip {
namespace app {
namespace Clusters {

constexpr std::bitset<4> gHepaFilterFeatureMap{ static_cast<uint32_t>(ResourceMonitoring::Feature::kCondition) |
static_cast<uint32_t>(ResourceMonitoring::Feature::kWarning) |
static_cast<uint32_t>(ResourceMonitoring::Feature::kReplacementProductList) };

constexpr std::bitset<4> gActivatedCarbonFeatureMap{ static_cast<uint32_t>(ResourceMonitoring::Feature::kCondition) |
static_cast<uint32_t>(ResourceMonitoring::Feature::kWarning) |
static_cast<uint32_t>(ResourceMonitoring::Feature::kReplacementProductList) };

/**
* @brief
* This class provides a singleton AIr Purifier Manager which also implements the FanControl::Delegate and the Device Callbacks.
* The class is responsible for all Air Purifier Elements on the Air Purifier Endpoint and it has members that are other Managers
* for the devices in its composition tree.
*/
class AirPurifierManager : public FanControl::Delegate, public DeviceManager::DeviceManagerCallbacks
{
public:
// Delete copy constructor and assignment operator.
AirPurifierManager(const AirPurifierManager &) = delete;
AirPurifierManager(const AirPurifierManager &&) = delete;
AirPurifierManager & operator=(const AirPurifierManager &) = delete;

static void InitInstance(EndpointId aEndpointId = 1, EndpointId aAirQualitySensorEndpointId = 2,
EndpointId aTemperatureSensorEndpointId = 3, EndpointId aHumiditySensorEndpointId = 4)
{
if (mInstance == nullptr)
{
mInstance = new AirPurifierManager(aEndpointId, aAirQualitySensorEndpointId, aTemperatureSensorEndpointId,
aHumiditySensorEndpointId);
mInstance->Init();
}
};

/**
* @brief Get an Air Purifier Manager object - this class acts as a singleton device manager for the air purifier
* @param[in] aEndpointId Endpoint that the air purifier is on
* @param[in] aAirQualitySensorEndpointId Endpoint that the air quality sensor is on
* @param[in] aTemperatureSensorEndpointId Endpoint that the temperature sensor is on
* @param[in] aHumiditySensorEndpointId Endpoint that the humidity sensor is on
* @return mInstance The AirPurifierManager instance, note this this could be nullptr if InitInstance has not been called
*/
static AirPurifierManager * GetInstance() { return mInstance; };

/**
* @brief Initialize the Air Purifier Manager and call init on underlying composed members.
*/
void Init();

/**
* @brief Top level handler for all attribute changes in the device. This function will call the appropriate attribute change
* handler based on the cluster id.
*/
void PostAttributeChangeCallback(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, uint8_t type, uint16_t size,
uint8_t * value) override;

/**
* @brief Handle the step command from the Fan Control Cluster
*/
Protocols::InteractionModel::Status HandleStep(FanControl::StepDirectionEnum aDirection, bool aWrap, bool aLowestOff) override;

private:
inline static AirPurifierManager * mInstance;

EndpointId mEndpointId;
EndpointId mAirQualitySensorEndpointId;
EndpointId mTemperatureSensorEndpointId;
EndpointId mHumiditySensorEndpointId;

uint8_t percentCurrent;
uint8_t speedCurrent;

// Set up for Activated Carbon Filter Monitoring
ActivatedCarbonFilterMonitoringDelegate activatedCarbonFilterDelegate;
ResourceMonitoring::Instance activatedCarbonFilterInstance;

// Set up for Hepa Filter Monitoring
HepaFilterMonitoringDelegate hepaFilterDelegate;
ResourceMonitoring::Instance hepaFilterInstance;

// Set up other members of composed device
AirQualitySensorManager mAirQualitySensorManager;
TemperatureSensorManager mTemperatureSensorManager;
RelativeHumiditySensorManager mHumiditySensorManager;

// Fan Mode Limits
static constexpr int FAN_MODE_LOW_LOWER_BOUND = 1;
static constexpr int FAN_MODE_LOW_UPPER_BOUND = 3;
static constexpr int FAN_MODE_MEDIUM_LOWER_BOUND = 4;
static constexpr int FAN_MODE_MEDIUM_UPPER_BOUND = 7;
static constexpr int FAN_MODE_HIGH_LOWER_BOUND = 8;
static constexpr int FAN_MODE_HIGH_UPPER_BOUND = 10;

/**
* @brief Construct a new Air Purifier Manager object - this class acts as a singleton device manager for the air purifier
* @param[in] aEndpointId Endpoint that the air purifier is on
* @param[in] aAirQualitySensorEndpointId Endpoint that the air quality sensor is on
* @param[in] aTemperatureSensorEndpointId Endpoint that the temperature sensor is on
* @param[in] aHumiditySensorEndpointId Endpoint that the humidity sensor is on
*/
AirPurifierManager(EndpointId aEndpointId, EndpointId aAirQualitySensorEndpointId, EndpointId aTemperatureSensorEndpointId,
EndpointId aHumiditySensorEndpointId) :
FanControl::Delegate(aEndpointId),
mEndpointId(aEndpointId),
activatedCarbonFilterInstance(&activatedCarbonFilterDelegate, mEndpointId, ActivatedCarbonFilterMonitoring::Id,
static_cast<uint32_t>(gActivatedCarbonFeatureMap.to_ulong()),
ResourceMonitoring::DegradationDirectionEnum::kDown, true),
hepaFilterInstance(&hepaFilterDelegate, mEndpointId, HepaFilterMonitoring::Id,
static_cast<uint32_t>(gHepaFilterFeatureMap.to_ulong()),
ResourceMonitoring::DegradationDirectionEnum::kDown, true),
mAirQualitySensorManager(aAirQualitySensorEndpointId), mTemperatureSensorManager(aTemperatureSensorEndpointId),
mHumiditySensorManager(aHumiditySensorEndpointId){};

/**
* @brief Handle attribute changes for the Fan Control Cluster
* @param[in] attributeId Cluster attribute id that changed
* @param[in] type Cluster attribute type
* @param[in] size Size of the attribute
* @param[in] value Pointer to the new value
*/
void HandleFanControlAttributeChange(AttributeId attributeId, uint8_t type, uint16_t size, uint8_t * value);

void PercentSettingWriteCallback(uint8_t aNewPercentSetting);
void SpeedSettingWriteCallback(uint8_t aNewSpeedSetting);
void FanModeWriteCallback(FanControl::FanModeEnum aNewFanMode);

void SetSpeedSetting(DataModel::Nullable<uint8_t> aNewSpeedSetting);
};

} // namespace Clusters
} // namespace app
} // namespace chip
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/clusters/air-quality-server/air-quality-server.h>
#include <app/clusters/concentration-measurement-server/concentration-measurement-server.h>

#pragma once

namespace chip {
namespace app {
namespace Clusters {

class AirQualitySensorManager
{
public:
AirQualitySensorManager(EndpointId aEndpointId) :
mEndpointId(aEndpointId),
airQualityInstance(mEndpointId,
BitMask<AirQuality::Feature, uint32_t>(AirQuality::Feature::kModerate, AirQuality::Feature::kFair,
AirQuality::Feature::kVeryPoor,
AirQuality::Feature::kExtremelyPoor)),
carbonDioxideConcentrationMeasurementInstance(mEndpointId, CarbonDioxideConcentrationMeasurement::Id,
ConcentrationMeasurement::MeasurementMediumEnum::kAir,
ConcentrationMeasurement::MeasurementUnitEnum::kPpm),
carbonMonoxideConcentrationMeasurementInstance(mEndpointId, CarbonMonoxideConcentrationMeasurement::Id,
ConcentrationMeasurement::MeasurementMediumEnum::kAir,
ConcentrationMeasurement::MeasurementUnitEnum::kPpm),
nitrogenDioxideConcentrationMeasurementInstance(mEndpointId, NitrogenDioxideConcentrationMeasurement::Id,
ConcentrationMeasurement::MeasurementMediumEnum::kAir,
ConcentrationMeasurement::MeasurementUnitEnum::kPpm),
pm1ConcentrationMeasurementInstance(mEndpointId, Pm1ConcentrationMeasurement::Id,
ConcentrationMeasurement::MeasurementMediumEnum::kAir,
ConcentrationMeasurement::MeasurementUnitEnum::kPpm),
pm10ConcentrationMeasurementInstance(mEndpointId, Pm10ConcentrationMeasurement::Id,
ConcentrationMeasurement::MeasurementMediumEnum::kAir,
ConcentrationMeasurement::MeasurementUnitEnum::kPpm),
pm25ConcentrationMeasurementInstance(mEndpointId, Pm25ConcentrationMeasurement::Id,
ConcentrationMeasurement::MeasurementMediumEnum::kAir,
ConcentrationMeasurement::MeasurementUnitEnum::kPpm),
radonConcentrationMeasurementInstance(mEndpointId, RadonConcentrationMeasurement::Id,
ConcentrationMeasurement::MeasurementMediumEnum::kAir,
ConcentrationMeasurement::MeasurementUnitEnum::kPpm),
totalVolatileOrganicCompoundsConcentrationMeasurementInstance(
mEndpointId, TotalVolatileOrganicCompoundsConcentrationMeasurement::Id,
ConcentrationMeasurement::MeasurementMediumEnum::kAir, ConcentrationMeasurement::MeasurementUnitEnum::kPpm),
ozoneConcentrationMeasurementInstance(mEndpointId, OzoneConcentrationMeasurement::Id,
ConcentrationMeasurement::MeasurementMediumEnum::kAir,
ConcentrationMeasurement::MeasurementUnitEnum::kPpm),
formaldehydeConcentrationMeasurementInstance(mEndpointId, FormaldehydeConcentrationMeasurement::Id,
ConcentrationMeasurement::MeasurementMediumEnum::kAir,
ConcentrationMeasurement::MeasurementUnitEnum::kPpm){};

void Init();

private:
EndpointId mEndpointId;
AirQuality::Instance airQualityInstance;
ConcentrationMeasurement::Instance<true, true, true, true, true, true> carbonDioxideConcentrationMeasurementInstance;
ConcentrationMeasurement::Instance<true, true, true, true, true, true> carbonMonoxideConcentrationMeasurementInstance;
ConcentrationMeasurement::Instance<true, true, true, true, true, true> nitrogenDioxideConcentrationMeasurementInstance;
ConcentrationMeasurement::Instance<true, true, true, true, true, true> pm1ConcentrationMeasurementInstance;
ConcentrationMeasurement::Instance<true, true, true, true, true, true> pm10ConcentrationMeasurementInstance;
ConcentrationMeasurement::Instance<true, true, true, true, true, true> pm25ConcentrationMeasurementInstance;
ConcentrationMeasurement::Instance<true, true, true, true, true, true> radonConcentrationMeasurementInstance;
ConcentrationMeasurement::Instance<true, true, true, true, true, true>
totalVolatileOrganicCompoundsConcentrationMeasurementInstance;
ConcentrationMeasurement::Instance<true, true, true, true, true, true> ozoneConcentrationMeasurementInstance;
ConcentrationMeasurement::Instance<true, true, true, true, true, true> formaldehydeConcentrationMeasurementInstance;
};

} // namespace Clusters
} // namespace app
} // namespace chip
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/clusters/resource-monitoring-server/resource-monitoring-cluster-objects.h>
#include <app/clusters/resource-monitoring-server/resource-monitoring-server.h>

#pragma once

namespace chip {
namespace app {
namespace Clusters {

class ActivatedCarbonFilterMonitoringDelegate : public ResourceMonitoring::Delegate
{
private:
CHIP_ERROR Init() override;
Protocols::InteractionModel::Status PreResetCondition() override;
Protocols::InteractionModel::Status PostResetCondition() override;

public:
~ActivatedCarbonFilterMonitoringDelegate() override = default;
};

class HepaFilterMonitoringDelegate : public ResourceMonitoring::Delegate
{
private:
CHIP_ERROR Init() override;
Protocols::InteractionModel::Status PreResetCondition() override;
Protocols::InteractionModel::Status PostResetCondition() override;

public:
~HepaFilterMonitoringDelegate() override = default;
};

class ImmutableReplacementProductListManager : public ResourceMonitoring::ReplacementProductListManager
{
public:
CHIP_ERROR
Next(ResourceMonitoring::ReplacementProductStruct & item) override;
};

} // namespace Clusters
} // namespace app
} // namespace chip
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

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

namespace chip {
namespace app {
namespace Clusters {

class RelativeHumiditySensorManager
{
public:
RelativeHumiditySensorManager(EndpointId aEndpointId) : mEndpointId(aEndpointId){};

void Init()
{
EmberAfStatus status = RelativeHumidityMeasurement::Attributes::MinMeasuredValue::Set(mEndpointId, 0);
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status,
ChipLogError(NotSpecified, "Failed to set RelativeHumidityMeasurement MinMeasuredValue attribute"));

status = RelativeHumidityMeasurement::Attributes::MaxMeasuredValue::Set(mEndpointId, 10000);
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status,
ChipLogError(NotSpecified, "Failed to set RelativeHumidityMeasurement MaxMeasuredValue attribute"));
};

void OnHumidityChangeHandler(uint16_t newValue)
{
EmberAfStatus status = RelativeHumidityMeasurement::Attributes::MeasuredValue::Set(mEndpointId, newValue);
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status,
ChipLogError(NotSpecified, "Failed to set RelativeHumidityMeasurement MeasuredValue attribute"));
ChipLogDetail(NotSpecified, "The new RelativeHumidityMeasurement value: %d", newValue);
}

private:
EndpointId mEndpointId;
};

} // namespace Clusters
} // namespace app
} // namespace chip
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

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

namespace chip {
namespace app {
namespace Clusters {

class TemperatureSensorManager
{
public:
TemperatureSensorManager(EndpointId aEndpointId) : mEndpointId(aEndpointId){};

void Init()
{
EmberAfStatus status = TemperatureMeasurement::Attributes::MinMeasuredValue::Set(mEndpointId, -500);
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status,
ChipLogError(NotSpecified, "Failed to set TemperatureMeasurement MinMeasuredValue attribute"));

status = TemperatureMeasurement::Attributes::MaxMeasuredValue::Set(mEndpointId, 6000);
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status,
ChipLogError(NotSpecified, "Failed to set TemperatureMeasurement MaxMeasuredValue attribute"));
}

void OnTemperatureChangeHandler(int16_t newValue)
{
EmberAfStatus status = TemperatureMeasurement::Attributes::MeasuredValue::Set(mEndpointId, newValue);
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status,
ChipLogError(NotSpecified, "Failed to set TemperatureMeasurement MeasuredValue attribute"));
ChipLogDetail(NotSpecified, "The new TemperatureMeasurement value: %d", newValue);
}

private:
EndpointId mEndpointId;
};

} // namespace Clusters
} // namespace app
} // namespace chip
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <air-purifier-manager.h>
#include <app/util/error-mapping.h>

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using Protocols::InteractionModel::Status;

void AirPurifierManager::Init()
{
FanControl::SetDefaultDelegate(mEndpointId, this);

activatedCarbonFilterInstance.Init();
hepaFilterInstance.Init();
mAirQualitySensorManager.Init();
mTemperatureSensorManager.Init();
mHumiditySensorManager.Init();

DataModel::Nullable<Percent> percentSetting;
EmberAfStatus status = FanControl::Attributes::PercentSetting::Get(mEndpointId, percentSetting);
if (EMBER_ZCL_STATUS_SUCCESS == status)
{
if (percentSetting.IsNull())
{
PercentSettingWriteCallback(0);
}
else
{
PercentSettingWriteCallback(percentSetting.Value());
}
}

DataModel::Nullable<uint8_t> speedSetting;
status = FanControl::Attributes::SpeedSetting::Get(mEndpointId, speedSetting);
if (EMBER_ZCL_STATUS_SUCCESS == status)
{
if (speedSetting.IsNull())
{
SpeedSettingWriteCallback(0);
}
else
{
SpeedSettingWriteCallback(speedSetting.Value());
}
}

// Set up some sane initial values for temperature and humidity - note these are fixed values for testing purposes only
mTemperatureSensorManager.OnTemperatureChangeHandler(2000);
mHumiditySensorManager.OnHumidityChangeHandler(5000);
}

void AirPurifierManager::PostAttributeChangeCallback(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId,
uint8_t type, uint16_t size, uint8_t * value)
{
switch (clusterId)
{
case FanControl::Id: {
HandleFanControlAttributeChange(attributeId, type, size, value);
break;
}

default:
break;
}
}

Status AirPurifierManager::HandleStep(FanControl::StepDirectionEnum aDirection, bool aWrap, bool aLowestOff)
{
ChipLogProgress(NotSpecified, "AirPurifierManager::HandleStep aDirection %d, aWrap %d, aLowestOff %d",
to_underlying(aDirection), aWrap, aLowestOff);

VerifyOrReturnError(aDirection != FanControl::StepDirectionEnum::kUnknownEnumValue, Status::InvalidCommand);

uint8_t speedMax;
FanControl::Attributes::SpeedMax::Get(mEndpointId, &speedMax);

DataModel::Nullable<uint8_t> speedSetting;
FanControl::Attributes::SpeedSetting::Get(mEndpointId, speedSetting);

uint8_t newSpeedSetting = speedSetting.IsNull() ? 0 : speedSetting.Value();

if (aDirection == FanControl::StepDirectionEnum::kIncrease)
{
if (speedSetting.IsNull())
{
newSpeedSetting = 1;
}
else if (speedSetting.Value() < speedMax)
{
newSpeedSetting = static_cast<uint8_t>(speedSetting.Value() + 1);
}
else if (speedSetting.Value() == speedMax)
{
if (aWrap)
{
newSpeedSetting = aLowestOff ? 0 : 1;
}
}
}
else if (aDirection == FanControl::StepDirectionEnum::kDecrease)
{
if (speedSetting.IsNull())
{
newSpeedSetting = aLowestOff ? 0 : 1;
}
else if ((speedSetting.Value() > 1) && (speedSetting.Value() <= speedMax))
{
newSpeedSetting = static_cast<uint8_t>(speedSetting.Value() - 1);
}
else if (speedSetting.Value() == 1)
{
if (aLowestOff)
{
newSpeedSetting = static_cast<uint8_t>(speedSetting.Value() - 1);
}
else if (aWrap)
{
newSpeedSetting = speedMax;
}
}
else if (speedSetting.Value() == 0)
{
if (aWrap)
{
newSpeedSetting = speedMax;
}
else if (!aLowestOff)
{
newSpeedSetting = 1;
}
}
}

return ToInteractionModelStatus(FanControl::Attributes::SpeedSetting::Set(mEndpointId, newSpeedSetting));
}

void AirPurifierManager::HandleFanControlAttributeChange(AttributeId attributeId, uint8_t type, uint16_t size, uint8_t * value)
{
switch (attributeId)
{
case FanControl::Attributes::PercentSetting::Id: {
DataModel::Nullable<Percent> percentSetting = static_cast<DataModel::Nullable<uint8_t>>(*value);
if (percentSetting.IsNull())
{
PercentSettingWriteCallback(0);
}
else
{
PercentSettingWriteCallback(percentSetting.Value());
}
break;
}

case FanControl::Attributes::SpeedSetting::Id: {
DataModel::Nullable<uint8_t> speedSetting = static_cast<DataModel::Nullable<uint8_t>>(*value);
if (speedSetting.IsNull())
{
SpeedSettingWriteCallback(0);
}
else
{
SpeedSettingWriteCallback(speedSetting.Value());
}
break;
}

case FanControl::Attributes::FanMode::Id: {
FanControl::FanModeEnum fanMode = static_cast<FanControl::FanModeEnum>(*value);
FanModeWriteCallback(fanMode);
break;
}

default: {
break;
}
}
}

void AirPurifierManager::PercentSettingWriteCallback(uint8_t aNewPercentSetting)
{
if (aNewPercentSetting != percentCurrent)
{
ChipLogDetail(NotSpecified, "AirPurifierManager::PercentSettingWriteCallback: %d", aNewPercentSetting);
percentCurrent = aNewPercentSetting;
EmberAfStatus status = FanControl::Attributes::PercentCurrent::Set(mEndpointId, percentCurrent);
if (status != EMBER_ZCL_STATUS_SUCCESS)
{
ChipLogError(NotSpecified,
"AirPurifierManager::PercentSettingWriteCallback: failed to set PercentCurrent attribute: %d", status);
}
}
}

void AirPurifierManager::SpeedSettingWriteCallback(uint8_t aNewSpeedSetting)
{
if (aNewSpeedSetting != speedCurrent)
{
ChipLogDetail(NotSpecified, "AirPurifierManager::SpeedSettingWriteCallback: %d", aNewSpeedSetting);
speedCurrent = aNewSpeedSetting;
EmberAfStatus status = FanControl::Attributes::SpeedCurrent::Set(mEndpointId, speedCurrent);
if (status != EMBER_ZCL_STATUS_SUCCESS)
{
ChipLogError(NotSpecified, "AirPurifierManager::SpeedSettingWriteCallback: failed to set SpeedCurrent attribute: %d",
status);
}

// Determine if the speed change should also change the fan mode
if (speedCurrent == 0)
{
FanControl::Attributes::FanMode::Set(mEndpointId, FanControl::FanModeEnum::kOff);
}
else if (speedCurrent <= FAN_MODE_LOW_UPPER_BOUND)
{
FanControl::Attributes::FanMode::Set(mEndpointId, FanControl::FanModeEnum::kLow);
}
else if (speedCurrent <= FAN_MODE_MEDIUM_UPPER_BOUND)
{
FanControl::Attributes::FanMode::Set(mEndpointId, FanControl::FanModeEnum::kMedium);
}
else if (speedCurrent <= FAN_MODE_HIGH_UPPER_BOUND)
{
FanControl::Attributes::FanMode::Set(mEndpointId, FanControl::FanModeEnum::kHigh);
}
}
}

void AirPurifierManager::FanModeWriteCallback(FanControl::FanModeEnum aNewFanMode)
{
ChipLogDetail(NotSpecified, "AirPurifierManager::FanModeWriteCallback: %d", (uint8_t) aNewFanMode);
switch (aNewFanMode)
{
case FanControl::FanModeEnum::kOff: {
if (speedCurrent != 0)
{
DataModel::Nullable<uint8_t> speedSetting(0);
SetSpeedSetting(speedSetting);
}
break;
}
case FanControl::FanModeEnum::kLow: {
if (speedCurrent < FAN_MODE_LOW_LOWER_BOUND || speedCurrent > FAN_MODE_LOW_UPPER_BOUND)
{
DataModel::Nullable<uint8_t> speedSetting(FAN_MODE_LOW_LOWER_BOUND);
SetSpeedSetting(speedSetting);
}
break;
}
case FanControl::FanModeEnum::kMedium: {
if (speedCurrent < FAN_MODE_MEDIUM_LOWER_BOUND || speedCurrent > FAN_MODE_MEDIUM_UPPER_BOUND)
{
DataModel::Nullable<uint8_t> speedSetting(FAN_MODE_MEDIUM_LOWER_BOUND);
SetSpeedSetting(speedSetting);
}
break;
}
case FanControl::FanModeEnum::kOn:
case FanControl::FanModeEnum::kHigh: {
if (speedCurrent < FAN_MODE_HIGH_LOWER_BOUND || speedCurrent > FAN_MODE_HIGH_UPPER_BOUND)
{
DataModel::Nullable<uint8_t> speedSetting(FAN_MODE_HIGH_LOWER_BOUND);
SetSpeedSetting(speedSetting);
}
break;
}
case FanControl::FanModeEnum::kSmart:
case FanControl::FanModeEnum::kAuto: {
ChipLogProgress(NotSpecified, "AirPurifierManager::FanModeWriteCallback: Auto");
break;
}
case FanControl::FanModeEnum::kUnknownEnumValue: {
ChipLogProgress(NotSpecified, "AirPurifierManager::FanModeWriteCallback: Unknown");
break;
}
}
}

void AirPurifierManager::SetSpeedSetting(DataModel::Nullable<uint8_t> aNewSpeedSetting)
{
if (aNewSpeedSetting.IsNull())
{
ChipLogError(NotSpecified, "AirPurifierManager::SetSpeedSetting: invalid value");
return;
}

if (aNewSpeedSetting.Value() != speedCurrent)
{
EmberAfStatus status = FanControl::Attributes::SpeedSetting::Set(mEndpointId, aNewSpeedSetting);
if (status != EMBER_ZCL_STATUS_SUCCESS)
{
ChipLogError(NotSpecified, "AirPurifierManager::SetSpeedSetting: failed to set SpeedSetting attribute: %d", status);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <air-quality-sensor-manager.h>

using namespace chip;
using namespace chip::app;
using namespace chip::app::DataModel;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::ConcentrationMeasurement;
using namespace chip::app::Clusters::AirQuality;

void AirQualitySensorManager::Init()
{
/*
* Note these values are for testing purposes only and are not actual values for the air quality sensor.
* They are also fixed.
*/

// Air Quality
airQualityInstance.Init();
airQualityInstance.UpdateAirQuality(AirQualityEnum::kGood);

// CO2
carbonDioxideConcentrationMeasurementInstance.Init();
carbonDioxideConcentrationMeasurementInstance.SetMinMeasuredValue(MakeNullable(0.0f));
carbonDioxideConcentrationMeasurementInstance.SetMaxMeasuredValue(MakeNullable(1000.0f));
carbonDioxideConcentrationMeasurementInstance.SetMeasuredValue(MakeNullable(2.0f));
carbonDioxideConcentrationMeasurementInstance.SetPeakMeasuredValue(MakeNullable(1.0f));
carbonDioxideConcentrationMeasurementInstance.SetPeakMeasuredValueWindow(320);
carbonDioxideConcentrationMeasurementInstance.SetAverageMeasuredValue(MakeNullable(1.0f));
carbonDioxideConcentrationMeasurementInstance.SetAverageMeasuredValueWindow(320);
carbonDioxideConcentrationMeasurementInstance.SetUncertainty(0.0f);
carbonDioxideConcentrationMeasurementInstance.SetLevelValue(LevelValueEnum::kLow);

// CO
carbonMonoxideConcentrationMeasurementInstance.Init();
carbonMonoxideConcentrationMeasurementInstance.SetMinMeasuredValue(MakeNullable(0.0f));
carbonMonoxideConcentrationMeasurementInstance.SetMaxMeasuredValue(MakeNullable(1000.0f));
carbonMonoxideConcentrationMeasurementInstance.SetMeasuredValue(MakeNullable(2.0f));
carbonMonoxideConcentrationMeasurementInstance.SetPeakMeasuredValue(MakeNullable(1.0f));
carbonMonoxideConcentrationMeasurementInstance.SetPeakMeasuredValueWindow(320);
carbonMonoxideConcentrationMeasurementInstance.SetAverageMeasuredValue(MakeNullable(1.0f));
carbonMonoxideConcentrationMeasurementInstance.SetAverageMeasuredValueWindow(320);
carbonMonoxideConcentrationMeasurementInstance.SetUncertainty(0.0f);
carbonMonoxideConcentrationMeasurementInstance.SetLevelValue(LevelValueEnum::kLow);

// NO2
nitrogenDioxideConcentrationMeasurementInstance.Init();
nitrogenDioxideConcentrationMeasurementInstance.SetMinMeasuredValue(MakeNullable(0.0f));
nitrogenDioxideConcentrationMeasurementInstance.SetMaxMeasuredValue(MakeNullable(1000.0f));
nitrogenDioxideConcentrationMeasurementInstance.SetMeasuredValue(MakeNullable(2.0f));
nitrogenDioxideConcentrationMeasurementInstance.SetPeakMeasuredValue(MakeNullable(1.0f));
nitrogenDioxideConcentrationMeasurementInstance.SetPeakMeasuredValueWindow(320);
nitrogenDioxideConcentrationMeasurementInstance.SetAverageMeasuredValue(MakeNullable(1.0f));
nitrogenDioxideConcentrationMeasurementInstance.SetAverageMeasuredValueWindow(320);
nitrogenDioxideConcentrationMeasurementInstance.SetUncertainty(0.0f);
nitrogenDioxideConcentrationMeasurementInstance.SetLevelValue(LevelValueEnum::kLow);

// PM1
pm1ConcentrationMeasurementInstance.Init();
pm1ConcentrationMeasurementInstance.SetMinMeasuredValue(MakeNullable(0.0f));
pm1ConcentrationMeasurementInstance.SetMaxMeasuredValue(MakeNullable(1000.0f));
pm1ConcentrationMeasurementInstance.SetMeasuredValue(MakeNullable(2.0f));
pm1ConcentrationMeasurementInstance.SetPeakMeasuredValue(MakeNullable(1.0f));
pm1ConcentrationMeasurementInstance.SetPeakMeasuredValueWindow(320);
pm1ConcentrationMeasurementInstance.SetAverageMeasuredValue(MakeNullable(1.0f));
pm1ConcentrationMeasurementInstance.SetAverageMeasuredValueWindow(320);
pm1ConcentrationMeasurementInstance.SetUncertainty(0.0f);
pm1ConcentrationMeasurementInstance.SetLevelValue(LevelValueEnum::kLow);

// PM10
pm10ConcentrationMeasurementInstance.Init();
pm10ConcentrationMeasurementInstance.SetMinMeasuredValue(MakeNullable(0.0f));
pm10ConcentrationMeasurementInstance.SetMaxMeasuredValue(MakeNullable(1000.0f));
pm10ConcentrationMeasurementInstance.SetMeasuredValue(MakeNullable(2.0f));
pm10ConcentrationMeasurementInstance.SetPeakMeasuredValue(MakeNullable(1.0f));
pm10ConcentrationMeasurementInstance.SetPeakMeasuredValueWindow(320);
pm10ConcentrationMeasurementInstance.SetAverageMeasuredValue(MakeNullable(1.0f));
pm10ConcentrationMeasurementInstance.SetAverageMeasuredValueWindow(320);
pm10ConcentrationMeasurementInstance.SetUncertainty(0.0f);
pm10ConcentrationMeasurementInstance.SetLevelValue(LevelValueEnum::kLow);

// PM2.5
pm25ConcentrationMeasurementInstance.Init();
pm25ConcentrationMeasurementInstance.SetMinMeasuredValue(MakeNullable(0.0f));
pm25ConcentrationMeasurementInstance.SetMaxMeasuredValue(MakeNullable(1000.0f));
pm25ConcentrationMeasurementInstance.SetMeasuredValue(MakeNullable(2.0f));
pm25ConcentrationMeasurementInstance.SetPeakMeasuredValue(MakeNullable(1.0f));
pm25ConcentrationMeasurementInstance.SetPeakMeasuredValueWindow(320);
pm25ConcentrationMeasurementInstance.SetAverageMeasuredValue(MakeNullable(1.0f));
pm25ConcentrationMeasurementInstance.SetAverageMeasuredValueWindow(320);
pm25ConcentrationMeasurementInstance.SetUncertainty(0.0f);
pm25ConcentrationMeasurementInstance.SetLevelValue(LevelValueEnum::kLow);

// Radon
radonConcentrationMeasurementInstance.Init();
radonConcentrationMeasurementInstance.SetMinMeasuredValue(MakeNullable(0.0f));
radonConcentrationMeasurementInstance.SetMaxMeasuredValue(MakeNullable(1000.0f));
radonConcentrationMeasurementInstance.SetMeasuredValue(MakeNullable(2.0f));
radonConcentrationMeasurementInstance.SetPeakMeasuredValue(MakeNullable(1.0f));
radonConcentrationMeasurementInstance.SetPeakMeasuredValueWindow(320);
radonConcentrationMeasurementInstance.SetAverageMeasuredValue(MakeNullable(1.0f));
radonConcentrationMeasurementInstance.SetAverageMeasuredValueWindow(320);
radonConcentrationMeasurementInstance.SetUncertainty(0.0f);
radonConcentrationMeasurementInstance.SetLevelValue(LevelValueEnum::kLow);

// TVOC
totalVolatileOrganicCompoundsConcentrationMeasurementInstance.Init();
totalVolatileOrganicCompoundsConcentrationMeasurementInstance.SetMinMeasuredValue(MakeNullable(0.0f));
totalVolatileOrganicCompoundsConcentrationMeasurementInstance.SetMaxMeasuredValue(MakeNullable(1000.0f));
totalVolatileOrganicCompoundsConcentrationMeasurementInstance.SetMeasuredValue(MakeNullable(2.0f));
totalVolatileOrganicCompoundsConcentrationMeasurementInstance.SetPeakMeasuredValue(MakeNullable(1.0f));
totalVolatileOrganicCompoundsConcentrationMeasurementInstance.SetPeakMeasuredValueWindow(320);
totalVolatileOrganicCompoundsConcentrationMeasurementInstance.SetAverageMeasuredValue(MakeNullable(1.0f));
totalVolatileOrganicCompoundsConcentrationMeasurementInstance.SetAverageMeasuredValueWindow(320);
totalVolatileOrganicCompoundsConcentrationMeasurementInstance.SetUncertainty(0.0f);
totalVolatileOrganicCompoundsConcentrationMeasurementInstance.SetLevelValue(LevelValueEnum::kLow);

// Ozone
ozoneConcentrationMeasurementInstance.Init();
ozoneConcentrationMeasurementInstance.SetMinMeasuredValue(MakeNullable(0.0f));
ozoneConcentrationMeasurementInstance.SetMaxMeasuredValue(MakeNullable(1000.0f));
ozoneConcentrationMeasurementInstance.SetMeasuredValue(MakeNullable(2.0f));
ozoneConcentrationMeasurementInstance.SetPeakMeasuredValue(MakeNullable(1.0f));
ozoneConcentrationMeasurementInstance.SetPeakMeasuredValueWindow(320);
ozoneConcentrationMeasurementInstance.SetAverageMeasuredValue(MakeNullable(1.0f));
ozoneConcentrationMeasurementInstance.SetAverageMeasuredValueWindow(320);
ozoneConcentrationMeasurementInstance.SetUncertainty(0.0f);
ozoneConcentrationMeasurementInstance.SetLevelValue(LevelValueEnum::kLow);

// Formaldehyde
formaldehydeConcentrationMeasurementInstance.Init();
formaldehydeConcentrationMeasurementInstance.SetMinMeasuredValue(MakeNullable(0.0f));
formaldehydeConcentrationMeasurementInstance.SetMaxMeasuredValue(MakeNullable(1000.0f));
formaldehydeConcentrationMeasurementInstance.SetMeasuredValue(MakeNullable(2.0f));
formaldehydeConcentrationMeasurementInstance.SetPeakMeasuredValue(MakeNullable(1.0f));
formaldehydeConcentrationMeasurementInstance.SetPeakMeasuredValueWindow(320);
formaldehydeConcentrationMeasurementInstance.SetAverageMeasuredValue(MakeNullable(1.0f));
formaldehydeConcentrationMeasurementInstance.SetAverageMeasuredValueWindow(320);
formaldehydeConcentrationMeasurementInstance.SetUncertainty(0.0f);
formaldehydeConcentrationMeasurementInstance.SetLevelValue(LevelValueEnum::kLow);
}
108 changes: 108 additions & 0 deletions examples/air-purifier-app/air-purifier-common/src/filter-delegates.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::ResourceMonitoring;
using namespace chip::app::Clusters::ActivatedCarbonFilterMonitoring;
using namespace chip::app::Clusters::HepaFilterMonitoring;
using chip::Protocols::InteractionModel::Status;

static ImmutableReplacementProductListManager sReplacementProductListManager;

//-- Activated Carbon Filter Monitoring delegate methods
CHIP_ERROR ActivatedCarbonFilterMonitoringDelegate::Init()
{
ChipLogDetail(Zcl, "ActivatedCarbonFilterMonitoringDelegate::Init()");
GetInstance()->SetReplacementProductListManagerInstance(&sReplacementProductListManager);
return CHIP_NO_ERROR;
}

Status ActivatedCarbonFilterMonitoringDelegate::PreResetCondition()
{
ChipLogDetail(Zcl, "ActivatedCarbonFilterMonitoringDelegate::PreResetCondition()");
return Status::Success;
}

Status ActivatedCarbonFilterMonitoringDelegate::PostResetCondition()
{
ChipLogDetail(Zcl, "ActivatedCarbonFilterMonitoringDelegate::PostResetCondition()");
return Status::Success;
}

//-- Hepa Filter Monitoring delegate methods
CHIP_ERROR HepaFilterMonitoringDelegate::Init()
{
ChipLogDetail(Zcl, "HepaFilterMonitoringDelegate::Init()");
GetInstance()->SetReplacementProductListManagerInstance(&sReplacementProductListManager);
return CHIP_NO_ERROR;
}

Status HepaFilterMonitoringDelegate::PreResetCondition()
{
ChipLogDetail(Zcl, "HepaFilterMonitoringDelegate::PreResetCondition()");
return Status::Success;
}

Status HepaFilterMonitoringDelegate::PostResetCondition()
{
ChipLogDetail(Zcl, "HepaFilterMonitoringDelegate::PostResetCondition()");
return Status::Success;
}

CHIP_ERROR ImmutableReplacementProductListManager::Next(ReplacementProductStruct & item)
{
if (mIndex >= kReplacementProductListMaxSize)
{
return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED;
}

switch (mIndex)
{
case 0: {
item.SetProductIdentifierType(ResourceMonitoring::ProductIdentifierTypeEnum::kUpc);
item.SetProductIdentifierValue(CharSpan::fromCharString("111112222233"));
break;
case 1:
item.SetProductIdentifierType(ResourceMonitoring::ProductIdentifierTypeEnum::kGtin8);
item.SetProductIdentifierValue(CharSpan::fromCharString("gtin8xxx"));
break;
case 2:
item.SetProductIdentifierType(ResourceMonitoring::ProductIdentifierTypeEnum::kEan);
item.SetProductIdentifierValue(CharSpan::fromCharString("4444455555666"));
break;
case 3:
item.SetProductIdentifierType(ResourceMonitoring::ProductIdentifierTypeEnum::kGtin14);
item.SetProductIdentifierValue(CharSpan::fromCharString("gtin14xxxxxxxx"));
break;
case 4:
item.SetProductIdentifierType(ResourceMonitoring::ProductIdentifierTypeEnum::kOem);
item.SetProductIdentifierValue(CharSpan::fromCharString("oem20xxxxxxxxxxxxxxx"));
break;
default:
return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED;
break;
}
}
mIndex++;
return CHIP_NO_ERROR;
}
5 changes: 5 additions & 0 deletions examples/air-purifier-app/ameba/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.vscode

/build/
/sdkconfig
/sdkconfig.old
117 changes: 117 additions & 0 deletions examples/air-purifier-app/ameba/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# CHIP Ameba Air Purifier Example

This example demonstrates the Matter air purifier application on Ameba platform.

---

- [CHIP Ameba Air Purifier Example](#chip-ameba-air-purifier-example)
- [Supported Device](#supported-device)
- [Building the Example Application](#building-the-example-application)
- [Commissioning](#commissioning)
- [BLE mode](#ble-mode)
- [IP mode](#ip-mode)
- [Cluster control](#cluster-control)

---

## Supported Device

The CHIP demo application is supported on
[Ameba RTL8722DM Board](https://www.amebaiot.com/en/amebad).

## Building the Example Application

- Check out the Ameba repository in the same folder/directory as the Matter
SDK repository:

```
git clone https://github.com/ambiot/ambd_matter.git
```

- Setup build environment:

```
$ cd connectedhomeip
$ source scripts/bootstrap.sh
```

- To build the demo application:

```
$ cd ambd_matter/project/realtek_amebaD_va0_example/GCC-RELEASE/project_lp/
$ make all
$ cd ambd_matter/project/realtek_amebaD_va0_example/GCC-RELEASE/project_hp/
$ make -C asdk air_purifier
From the same directory:
$ make all
```

- Combine the three output images for flashing using the **Ameba Image Tool**:

```
$ cd ambd_matter/tools/AmebaD/Image_Tool_Linux
$ sudo ./AmebaD_ImageTool -combine \
../../../project/realtek_amebaD_va0_example/GCC-RELEASE/project_lp/asdk/image/km0_boot_all.bin 0x0000 \
../../../project/realtek_amebaD_va0_example/GCC-RELEASE/project_hp/asdk/image/km4_boot_all.bin 0x4000 \
../../../project/realtek_amebaD_va0_example/GCC-RELEASE/project_hp/asdk/image/km0_km4_image2.bin 0x6000
```

- This will produce a combined Image_All.bin file alongside the image tool
that can be flashed using the **Ameba Image Tool**:

1. Connect your device via USB
2. Edit the `ambd_matter/tools/AmebaD/mpp.ini` file with the correct port
setting (the rest of the settings should be correct)
3. Click **Download** button and the **Reset** button to get the board into
serial download mode
4. Flash on the image:

```
$ cd ambd_matter/tools/AmebaD/Image_Tool_Linux
$ ./AmebaD_ImageTool -download
```

## Commissioning

There are two commissioning modes supported by Ameba platform:

### BLE mode

1. Build and Flash
2. The example will run automatically after booting the Ameba board.
3. Test with
[Chip-Tool](https://github.com/project-chip/connectedhomeip/tree/master/examples/chip-tool)

### IP mode

1. Build and Flash
2. The example will run automatically after booting the Ameba board.
3. Connect to AP using `ATW0, ATW1, ATWC` commands
4. Test with
[Chip-Tool](https://github.com/project-chip/connectedhomeip/tree/master/examples/chip-tool)

## Cluster Control

After successful commissioning, the air purifier clusters can be read and
controlled using
[Chip-Tool](https://github.com/project-chip/connectedhomeip/tree/master/examples/chip-tool#using-the-client-to-send-matter-commands).

The Air Purifier is a composed device. The example has endpoints configured as
follows:

- Air purifier on endpoint 1
- Air quality sensor on endpoint 2
- Temperature sensor on endpoint 3
- Relative humidity sensor on endpoint 4

Example commands using the chip tool:

```
$ ./chip-tool fancontrol write speed-setting 10 ${NODE_ID_TO_ASSIGN} 1
$ ./chip-tool formaldehydeconcentrationmeasurement read level-value ${NODE_ID_TO_ASSIGN} 2
$ ./chip-tool temperaturemeasurement read measured-value ${NODE_ID_TO_ASSIGN} 3
$ ./chip-tool relativehumiditymeasurement read measured-value ${NODE_ID_TO_ASSIGN} 4
```
284 changes: 284 additions & 0 deletions examples/air-purifier-app/ameba/chip_main.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
cmake_minimum_required(VERSION 3.6)

project(chip_main)

set(chip_dir "${ameba_matter_root}")
set(chip_dir_output "${matter_output_path}/chip")
set(dir "${sdk_root}/component/common/api")
set(chip_main chip_main)
set(list_chip_main_sources chip_main_sources)

if (matter_enable_rpc)
set(pigweed_dir "${chip_dir}/third_party/pigweed/repo")

include(${pigweed_dir}/pw_build/pigweed.cmake)
include(${pigweed_dir}/pw_protobuf_compiler/proto.cmake)
include(${pigweed_dir}/pw_assert/backend.cmake)
include(${pigweed_dir}/pw_log/backend.cmake)
include(${pigweed_dir}/pw_sys_io/backend.cmake)
include(${pigweed_dir}/pw_trace/backend.cmake)

set(dir_pw_third_party_nanopb "${chip_dir}/third_party/nanopb/repo" CACHE STRING "" FORCE)

pw_set_module_config(pw_rpc_CONFIG pw_rpc.disable_global_mutex_config)
pw_set_backend(pw_log pw_log_basic)
pw_set_backend(pw_assert.check pw_assert_log.check_backend)
pw_set_backend(pw_assert.assert pw_assert.assert_compatibility_backend)
pw_set_backend(pw_sys_io pw_sys_io.ameba)
pw_set_backend(pw_trace pw_trace_tokenized)

add_subdirectory(${chip_dir}/third_party/pigweed/repo ${chip_dir}/examples/air-purifier-app/ameba/out/pigweed)
add_subdirectory(${chip_dir}/third_party/nanopb/repo ${chip_dir}/examples/air-purifier-app/ameba/out/nanopb)
add_subdirectory(${chip_dir}/examples/platform/ameba/pw_sys_io ${chip_dir}/examples/air-purifier-app/ameba/out/pw_sys_io)

pw_proto_library(attributes_service
SOURCES
${chip_dir}/examples/common/pigweed/protos/attributes_service.proto
INPUTS
${chip_dir}/examples/common/pigweed/protos/attributes_service.options
PREFIX
attributes_service
STRIP_PREFIX
${chip_dir}/examples/common/pigweed/protos
DEPS
pw_protobuf.common_proto
)

pw_proto_library(button_service
SOURCES
${chip_dir}/examples/common/pigweed/protos/button_service.proto
PREFIX
button_service
STRIP_PREFIX
${chip_dir}/examples/common/pigweed/protos
DEPS
pw_protobuf.common_proto
)

pw_proto_library(descriptor_service
SOURCES
${chip_dir}/examples/common/pigweed/protos/descriptor_service.proto
PREFIX
descriptor_service
STRIP_PREFIX
${chip_dir}/examples/common/pigweed/protos
DEPS
pw_protobuf.common_proto
)

pw_proto_library(device_service
SOURCES
${chip_dir}/examples/common/pigweed/protos/device_service.proto
INPUTS
${chip_dir}/examples/common/pigweed/protos/device_service.options
PREFIX
device_service
STRIP_PREFIX
${chip_dir}/examples/common/pigweed/protos
DEPS
pw_protobuf.common_proto
)

pw_proto_library(lighting_service
SOURCES
${chip_dir}/examples/common/pigweed/protos/lighting_service.proto
PREFIX
lighting_service
STRIP_PREFIX
${chip_dir}/examples/common/pigweed/protos
DEPS
pw_protobuf.common_proto
)

pw_proto_library(locking_service
SOURCES
${chip_dir}/examples/common/pigweed/protos/locking_service.proto
PREFIX
locking_service
STRIP_PREFIX
${chip_dir}/examples/common/pigweed/protos
DEPS
pw_protobuf.common_proto
)

pw_proto_library(wifi_service
SOURCES
${chip_dir}/examples/common/pigweed/protos/wifi_service.proto
INPUTS
${chip_dir}/examples/common/pigweed/protos/wifi_service.options
PREFIX
wifi_service
DEPS
pw_protobuf.common_proto
STRIP_PREFIX
${chip_dir}/examples/common/pigweed/protos
)

endif(matter_enable_rpc)

include(${prj_root}/GCC-RELEASE/project_hp/asdk/includepath.cmake)

if (matter_enable_rpc)
list(
APPEND ${list_chip_main_sources}
#rpc
${chip_dir}/examples/platform/ameba/PigweedLogger.cpp
${chip_dir}/examples/platform/ameba/Rpc.cpp
${chip_dir}/examples/common/pigweed/RpcService.cpp
${chip_dir}/examples/common/pigweed/ameba/PigweedLoggerMutex.cpp
)
endif (matter_enable_rpc)

if (matter_enable_ota_requestor)
list(
APPEND ${list_chip_main_sources}
#OTARequestor
${chip_dir}/src/app/clusters/ota-requestor/BDXDownloader.cpp
${chip_dir}/src/app/clusters/ota-requestor/DefaultOTARequestor.cpp
${chip_dir}/src/app/clusters/ota-requestor/DefaultOTARequestorDriver.cpp
${chip_dir}/src/app/clusters/ota-requestor/DefaultOTARequestorStorage.cpp
${chip_dir}/src/app/clusters/ota-requestor/ota-requestor-server.cpp
${chip_dir}/examples/platform/ameba/ota/OTAInitializer.cpp
)
endif (matter_enable_ota_requestor)

list(
APPEND ${list_chip_main_sources}

${chip_dir}/examples/air-purifier-app/ameba/main/chipinterface.cpp
${chip_dir}/examples/air-purifier-app/ameba/main/DeviceCallbacks.cpp
${chip_dir}/examples/air-purifier-app/ameba/main/CHIPDeviceManager.cpp

${chip_dir}/examples/platform/ameba/route_hook/ameba_route_hook.c
${chip_dir}/examples/platform/ameba/route_hook/ameba_route_table.c

${chip_dir}/examples/providers/DeviceInfoProviderImpl.cpp
)

add_library(
${chip_main}
STATIC
${chip_main_sources}
)

chip_configure_data_model(chip_main
INCLUDE_SERVER
ZAP_FILE ${matter_example_path}/../air-purifier-common/air-purifier-app.zap
)

if (matter_enable_rpc)
target_include_directories(
${chip_main}
PUBLIC
#rpc
${chip_dir}/examples/platform/ameba
${chip_dir}/examples/platform/ameba/pw_sys_io/public
${chip_dir}/examples/common
${chip_dir}/examples/common/pigweed
${chip_dir}/examples/common/pigweed/ameba
${chip_dir}/src
${chip_dir}/src/lib/support
${pigweed_dir}/pw_rpc/nanopb/public
)
endif (matter_enable_rpc)

target_include_directories(
${chip_main}
PUBLIC
${inc_path}
${chip_dir}/zzz_generated/air-purifier-app
${chip_dir}/zzz_generated/air-purifier-app/zap-generated
${chip_dir}/zzz_generated/app-common
${chip_dir}/examples/air-purifier-app/air-purifier-common
${chip_dir}/examples/air-purifier-app/air-purifier-common/include
${chip_dir}/examples/air-purifier-app/ameba/main/include
${chip_dir}/examples/platform/ameba
${chip_dir}/examples/providers
${chip_dir_output}/gen/include
${chip_dir}/src/include/
${chip_dir}/src/lib/
${chip_dir}/src/
${chip_dir}/third_party/nlassert/repo/include/
${chip_dir}/src/app/
${chip_dir}/src/app/util/
${chip_dir}/src/app/server/
${chip_dir}/src/controller/data_model
${chip_dir}/third_party/nlio/repo/include/
${chip_dir}/third_party/nlunit-test/repo/src
)

if (matter_enable_rpc)
target_link_libraries(${chip_main} PUBLIC
attributes_service.nanopb_rpc
button_service.nanopb_rpc
descriptor_service.nanopb_rpc
device_service.nanopb_rpc
lighting_service.nanopb_rpc
locking_service.nanopb_rpc
wifi_service.nanopb_rpc
pw_checksum
pw_hdlc
pw_log
pw_rpc.server
pw_sys_io
pw_trace_tokenized
pw_trace_tokenized.trace_buffer
pw_trace_tokenized.rpc_service
pw_trace_tokenized.protos.nanopb_rpc
PwRpc
)

link_directories(
${chip_dir_output}/lib
)
endif (matter_enable_rpc)

list(
APPEND chip_main_flags

-DINET_CONFIG_ENABLE_IPV4=0
-DCHIP_PROJECT=1
-DCHIP_DEVICE_LAYER_TARGET=Ameba
-DUSE_ZAP_CONFIG
-DCHIP_HAVE_CONFIG_H
-DMBEDTLS_CONFIG_FILE=<mbedtls_config.h>
)

if (matter_enable_persistentstorage_audit)
list(
APPEND chip_main_flags

-DCHIP_SUPPORT_ENABLE_STORAGE_API_AUDIT
)
endif (matter_enable_persistentstorage_audit)

if (matter_enable_rpc)
list(
APPEND chip_main_flags

-DPW_RPC_ATTRIBUTE_SERVICE=1
-DPW_RPC_BUTTON_SERVICE=1
-DPW_RPC_DESCRIPTOR_SERVICE=1
-DPW_RPC_DEVICE_SERVICE=1
-DPW_RPC_LIGHTING_SERVICE=1
-DPW_RPC_LOCKING_SERVICE=1
-DCONFIG_ENABLE_PW_RPC=1
)
endif (matter_enable_rpc)

list(
APPEND chip_main_cpp_flags

-Wno-unused-parameter
-std=c++17
-fno-rtti
)
target_compile_definitions(${chip_main} PRIVATE ${chip_main_flags} )
target_compile_options(${chip_main} PRIVATE ${chip_main_cpp_flags})

# move static library post build command
add_custom_command(
TARGET ${chip_main}
POST_BUILD
COMMAND cp lib${chip_main}.a ${CMAKE_CURRENT_SOURCE_DIR}/lib/application
)
112 changes: 112 additions & 0 deletions examples/air-purifier-app/ameba/main/CHIPDeviceManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @file
* This file implements the CHIP Device Interface that is used by
* applications to interact with the CHIP stack
*
*/

#include <stdlib.h>

#include "CHIPDeviceManager.h"
#include <air-purifier-manager.h>
#include <app/util/basic-types.h>
#include <core/ErrorStr.h>
#include <credentials/DeviceAttestationCredsProvider.h>
#include <credentials/examples/DeviceAttestationCredsExample.h>
#include <platform/Ameba/FactoryDataProvider.h>
#include <support/CHIPMem.h>
#include <support/CodeUtils.h>

#include <app-common/zap-generated/attributes/Accessors.h>
#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/util/af-types.h>
#include <app/util/attribute-storage.h>
#include <app/util/util.h>

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

namespace chip {

namespace DeviceManager {

using namespace ::chip::DeviceLayer;

chip::DeviceLayer::FactoryDataProvider mFactoryDataProvider;

void CHIPDeviceManager::CommonDeviceEventHandler(const ChipDeviceEvent * event, intptr_t arg)
{
CHIPDeviceManagerCallbacks * cb = reinterpret_cast<CHIPDeviceManagerCallbacks *>(arg);
if (cb != nullptr)
{
cb->DeviceEventCallback(event, reinterpret_cast<intptr_t>(cb));
}
}

CHIP_ERROR CHIPDeviceManager::Init(CHIPDeviceManagerCallbacks * cb)
{
CHIP_ERROR err;
mCB = cb;

err = Platform::MemoryInit();
SuccessOrExit(err);

err = PlatformMgr().InitChipStack();
SuccessOrExit(err);

err = mFactoryDataProvider.Init();
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "Error initializing FactoryData!");
ChipLogError(DeviceLayer, "Check if you have flashed it correctly!");
}
SetCommissionableDataProvider(&mFactoryDataProvider);
SetDeviceAttestationCredentialsProvider(&mFactoryDataProvider);
SetDeviceInstanceInfoProvider(&mFactoryDataProvider);

if (CONFIG_NETWORK_LAYER_BLE)
{
ConnectivityMgr().SetBLEAdvertisingEnabled(true);
}

PlatformMgr().AddEventHandler(CHIPDeviceManager::CommonDeviceEventHandler, reinterpret_cast<intptr_t>(cb));

// // Start a task to run the CHIP Device event loop.
err = PlatformMgr().StartEventLoopTask();
SuccessOrExit(err);

exit:
return err;
}
} // namespace DeviceManager
} // namespace chip

void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size,
uint8_t * value)
{
if (AirPurifierManager::GetInstance() != nullptr)
{
AirPurifierManager::GetInstance()->PostAttributeChangeCallback(attributePath.mEndpointId, attributePath.mClusterId,
attributePath.mAttributeId, type, size, value);
}
}
182 changes: 182 additions & 0 deletions examples/air-purifier-app/ameba/main/DeviceCallbacks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @file DeviceCallbacks.cpp
*
* Implements all the callbacks to the application from the CHIP Stack
*
**/
#include "DeviceCallbacks.h"

#include "CHIPDeviceManager.h"
#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/CommandHandler.h>
#include <app/server/Dnssd.h>
#include <app/util/af.h>
#include <app/util/basic-types.h>
#include <app/util/util.h>
#include <lib/dnssd/Advertiser.h>
#include <platform/Ameba/AmebaUtils.h>
#include <route_hook/ameba_route_hook.h>
#include <support/CodeUtils.h>
#include <support/logging/CHIPLogging.h>
#include <support/logging/Constants.h>

#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR
#include <ota/OTAInitializer.h>
#endif

static const char * TAG = "app-devicecallbacks";

using namespace ::chip;
using namespace ::chip::Inet;
using namespace ::chip::System;
using namespace ::chip::DeviceLayer;
using namespace ::chip::DeviceManager;
using namespace ::chip::Logging;

uint32_t identifyTimerCount;
constexpr uint32_t kIdentifyTimerDelayMS = 250;
constexpr uint32_t kInitOTARequestorDelaySec = 3;

#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR
void InitOTARequestorHandler(System::Layer * systemLayer, void * appState)
{
OTAInitializer::Instance().InitOTARequestor();
}
#endif

void DeviceCallbacks::DeviceEventCallback(const ChipDeviceEvent * event, intptr_t arg)
{
switch (event->Type)
{
case DeviceEventType::kInternetConnectivityChange:
OnInternetConnectivityChange(event);
break;

case DeviceEventType::kCHIPoBLEConnectionEstablished:
ChipLogProgress(DeviceLayer, "CHIPoBLE Connection Established");
break;

case DeviceEventType::kCHIPoBLEConnectionClosed:
ChipLogProgress(DeviceLayer, "CHIPoBLE Connection Closed");
break;

case DeviceEventType::kCHIPoBLEAdvertisingChange:
ChipLogProgress(DeviceLayer, "CHIPoBLE advertising has changed");
break;

case DeviceEventType::kInterfaceIpAddressChanged:
if ((event->InterfaceIpAddressChanged.Type == InterfaceIpChangeType::kIpV4_Assigned) ||
(event->InterfaceIpAddressChanged.Type == InterfaceIpChangeType::kIpV6_Assigned))
{
// MDNS server restart on any ip assignment: if link local ipv6 is configured, that
// will not trigger a 'internet connectivity change' as there is no internet
// connectivity. MDNS still wants to refresh its listening interfaces to include the
// newly selected address.
chip::app::DnssdServer::Instance().StartServer();
}
if (event->InterfaceIpAddressChanged.Type == InterfaceIpChangeType::kIpV6_Assigned)
{
ChipLogProgress(DeviceLayer, "Initializing route hook...");
ameba_route_hook_init();
}
break;

case DeviceEventType::kCommissioningComplete:
ChipLogProgress(DeviceLayer, "Commissioning Complete");
chip::DeviceLayer::Internal::AmebaUtils::SetCurrentProvisionedNetwork();
break;
}
}

void DeviceCallbacks::OnInternetConnectivityChange(const ChipDeviceEvent * event)
{
if (event->InternetConnectivityChange.IPv4 == kConnectivity_Established)
{
printf("IPv4 Server ready...");
chip::app::DnssdServer::Instance().StartServer();
}
else if (event->InternetConnectivityChange.IPv4 == kConnectivity_Lost)
{
printf("Lost IPv4 connectivity...");
}
if (event->InternetConnectivityChange.IPv6 == kConnectivity_Established)
{
printf("IPv6 Server ready...");
chip::app::DnssdServer::Instance().StartServer();
#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR
// Init OTA requestor only when we have gotten IPv6 address
if (!OTAInitializer::Instance().CheckInit())
{
ChipLogProgress(DeviceLayer, "Initializing OTA");
chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds32(kInitOTARequestorDelaySec),
InitOTARequestorHandler, nullptr);
}
#endif
}
else if (event->InternetConnectivityChange.IPv6 == kConnectivity_Lost)
{
printf("Lost IPv6 connectivity...");
}
}

void DeviceCallbacks::PostAttributeChangeCallback(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, uint8_t type,
uint16_t size, uint8_t * value)
{
switch (clusterId)
{
case app::Clusters::Identify::Id:
OnIdentifyPostAttributeChangeCallback(endpointId, attributeId, value);
break;

default:
ChipLogProgress(Zcl, "Unknown cluster ID: " ChipLogFormatMEI, ChipLogValueMEI(clusterId));
break;
}
}

void IdentifyTimerHandler(Layer * systemLayer, void * appState)
{
if (identifyTimerCount)
{
systemLayer->StartTimer(Clock::Milliseconds32(kIdentifyTimerDelayMS), IdentifyTimerHandler, appState);
// Decrement the timer count.
identifyTimerCount--;
}
}

void DeviceCallbacks::OnIdentifyPostAttributeChangeCallback(EndpointId endpointId, AttributeId attributeId, uint8_t * value)
{
VerifyOrExit(attributeId == app::Clusters::Identify::Attributes::IdentifyTime::Id,
ChipLogError(DeviceLayer, "[%s] Unhandled Attribute ID: '0x%04x", TAG, attributeId));
VerifyOrExit(endpointId == 1, ChipLogError(DeviceLayer, "[%s] Unexpected EndPoint ID: `0x%02x'", TAG, endpointId));

// timerCount represents the number of callback executions before we stop the timer.
// value is expressed in seconds and the timer is fired every 250ms, so just multiply value by 4.
// Also, we want timerCount to be odd number, so the ligth state ends in the same state it starts.
identifyTimerCount = (*value) * 4;

DeviceLayer::SystemLayer().CancelTimer(IdentifyTimerHandler, this);
DeviceLayer::SystemLayer().StartTimer(Clock::Milliseconds32(kIdentifyTimerDelayMS), IdentifyTimerHandler, this);

exit:
return;
}
175 changes: 175 additions & 0 deletions examples/air-purifier-app/ameba/main/chipinterface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <platform_stdlib.h>

#include "CHIPDeviceManager.h"
#include "DeviceCallbacks.h"
#include "Server.h"
#include <DeviceInfoProviderImpl.h>

#include "chip_porting.h"
#include <platform/CHIPDeviceLayer.h>
#include <support/CHIPMem.h>

#include <air-purifier-manager.h>
#include <app/clusters/identify-server/identify-server.h>
#include <app/clusters/network-commissioning/network-commissioning.h>
#include <app/server/OnboardingCodesUtil.h>
#include <app/util/af.h>
#include <lib/core/ErrorStr.h>
#include <platform/Ameba/AmebaConfig.h>
#include <platform/Ameba/NetworkCommissioningDriver.h>
#include <platform/CHIPDeviceLayer.h>
#include <setup_payload/ManualSetupPayloadGenerator.h>
#include <setup_payload/QRCodeSetupPayloadGenerator.h>

#include <lwip_netconf.h>

#if CONFIG_ENABLE_PW_RPC
#include "Rpc.h"
#endif

#define AIR_PURIFIER_ENDPOINT 1
#define AIR_QUALITY_SENSOR_ENDPOINT 2
#define TEMPERATURE_SENSOR_ENDPOINT 3
#define RELATIVE_HUMIDITY_SENSOR_ENDPOINT 4

using namespace ::chip;
using namespace ::chip::app;
using namespace ::chip::DeviceManager;
using namespace ::chip::DeviceLayer;
using namespace ::chip::System;

namespace { // Network Commissioning
constexpr EndpointId kNetworkCommissioningEndpointMain = 0;
constexpr EndpointId kNetworkCommissioningEndpointSecondary = 0xFFFE;

app::Clusters::NetworkCommissioning::Instance
sWiFiNetworkCommissioningInstance(kNetworkCommissioningEndpointMain /* Endpoint Id */,
&(NetworkCommissioning::AmebaWiFiDriver::GetInstance()));
} // namespace

void NetWorkCommissioningInstInit()
{
sWiFiNetworkCommissioningInstance.Init();

// We only have network commissioning on endpoint 0.
emberAfEndpointEnableDisable(kNetworkCommissioningEndpointSecondary, false);
}

static DeviceCallbacks EchoCallbacks;
chip::DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider;

void OnIdentifyStart(Identify *)
{
ChipLogProgress(Zcl, "OnIdentifyStart");
}

void OnIdentifyStop(Identify *)
{
ChipLogProgress(Zcl, "OnIdentifyStop");
}

void OnTriggerEffect(Identify * identify)
{
switch (identify->mCurrentEffectIdentifier)
{
case Clusters::Identify::EffectIdentifierEnum::kBlink:
ChipLogProgress(Zcl, "Clusters::Identify::EffectIdentifierEnum::kBlink");
break;
case Clusters::Identify::EffectIdentifierEnum::kBreathe:
ChipLogProgress(Zcl, "Clusters::Identify::EffectIdentifierEnum::kBreathe");
break;
case Clusters::Identify::EffectIdentifierEnum::kOkay:
ChipLogProgress(Zcl, "Clusters::Identify::EffectIdentifierEnum::kOkay");
break;
case Clusters::Identify::EffectIdentifierEnum::kChannelChange:
ChipLogProgress(Zcl, "Clusters::Identify::EffectIdentifierEnum::kChannelChange");
break;
default:
ChipLogProgress(Zcl, "No identifier effect");
return;
}
}

static Identify gIdentify1 = {
chip::EndpointId{ 1 }, OnIdentifyStart, OnIdentifyStop, Clusters::Identify::IdentifyTypeEnum::kVisibleIndicator,
OnTriggerEffect,
};

static void InitAirPurifierManager(void)
{
Clusters::AirPurifierManager::InitInstance(EndpointId(AIR_PURIFIER_ENDPOINT), EndpointId(AIR_QUALITY_SENSOR_ENDPOINT),
EndpointId(TEMPERATURE_SENSOR_ENDPOINT),
EndpointId(RELATIVE_HUMIDITY_SENSOR_ENDPOINT));

SetParentEndpointForEndpoint(AIR_QUALITY_SENSOR_ENDPOINT, AIR_PURIFIER_ENDPOINT);
SetParentEndpointForEndpoint(TEMPERATURE_SENSOR_ENDPOINT, AIR_PURIFIER_ENDPOINT);
SetParentEndpointForEndpoint(RELATIVE_HUMIDITY_SENSOR_ENDPOINT, AIR_PURIFIER_ENDPOINT);
}

static void InitServer(intptr_t context)
{
// Init ZCL Data Model and CHIP App Server
static chip::CommonCaseDeviceServerInitParams initParams;
(void) initParams.InitializeStaticResourcesBeforeServerInit();
chip::Server::GetInstance().Init(initParams);
gExampleDeviceInfoProvider.SetStorageDelegate(&Server::GetInstance().GetPersistentStorage());
chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider);

NetWorkCommissioningInstInit();

if (RTW_SUCCESS != wifi_is_connected_to_ap())
{
// QR code will be used with CHIP Tool
PrintOnboardingCodes(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE));
}

InitAirPurifierManager();
}

extern "C" void ChipTest(void)
{
ChipLogProgress(DeviceLayer, "Air purifier App Demo!");
CHIP_ERROR err = CHIP_NO_ERROR;

#if CONFIG_ENABLE_PW_RPC
chip::rpc::Init();
#endif

initPref();

CHIPDeviceManager & deviceMgr = CHIPDeviceManager::GetInstance();

err = deviceMgr.Init(&EchoCallbacks);
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "DeviceManagerInit() - ERROR!\r\n");
}
else
{
ChipLogProgress(DeviceLayer, "DeviceManagerInit() - OK\r\n");
}

chip::DeviceLayer::PlatformMgr().ScheduleWork(InitServer, 0);
}

bool lowPowerClusterSleep()
{
return true;
}
123 changes: 123 additions & 0 deletions examples/air-purifier-app/ameba/main/include/CHIPDeviceManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @file
* This file contains definitions for the CHIP DeviceManager Interface
*
* This object will co-ordinate multiple activities such as
* initialisation, rendezvous, session mgmt and other such
* activities within the CHIP stack. This is a singleton object.
*/

#pragma once

#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPError.h>
#include <platform/CHIPDeviceLayer.h>

#include <lib/support/DLLUtil.h>

#include <stdarg.h>
#include <stdlib.h>

#include <app/util/af-types.h>

namespace chip {
namespace DeviceManager {

/**
* @brief
* This class provides a skeleton for all the callback functions. The functions will be
* called by other objects within the CHIP stack for specific events.
* Applications interested in receiving specific callbacks can specialize this class and handle
* these events in their implementation of this class.
*/
class CHIPDeviceManagerCallbacks
{
public:
/**
* @brief
* Called when CHIP Device events (PublicEventTypes) are triggered.
*
* @param event ChipDeviceEvent that occurred
* @param arg arguments specific to the event, if any
*/
virtual void DeviceEventCallback(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg) {}

/**
* @brief
* Called after an attribute has been changed
*
* @param endpoint endpoint id
* @param clusterID cluster id
* @param attributeId attribute id that was changed
* @param manufacturerCode manufacturer code
* @param type attribute type
* @param size size of the attribute
* @param value pointer to the new value
*/
virtual void PostAttributeChangeCallback(chip::EndpointId endpoint, chip::ClusterId clusterId, chip::AttributeId attributeId,
uint8_t type, uint16_t size, uint8_t * value)
{}
virtual ~CHIPDeviceManagerCallbacks() {}
};

/**
* @brief
* A common class that drives other components of the CHIP stack
*/
class DLL_EXPORT CHIPDeviceManager
{
public:
CHIPDeviceManager(const CHIPDeviceManager &) = delete;
CHIPDeviceManager(const CHIPDeviceManager &&) = delete;
CHIPDeviceManager & operator=(const CHIPDeviceManager &) = delete;

static CHIPDeviceManager & GetInstance()
{
static CHIPDeviceManager instance;
return instance;
}

/**
* @brief
* Initialise CHIPDeviceManager
*
* @param cb Application's instance of the CHIPDeviceManagerCallbacks for consuming events
*/
CHIP_ERROR Init(CHIPDeviceManagerCallbacks * cb);

/**
* @brief
* Fetch a pointer to the registered CHIPDeviceManagerCallbacks object.
*
*/
CHIPDeviceManagerCallbacks * GetCHIPDeviceManagerCallbacks() { return mCB; }

/**
* Use internally for registration of the ChipDeviceEvents
*/
static void CommonDeviceEventHandler(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg);

private:
CHIPDeviceManagerCallbacks * mCB = nullptr;
CHIPDeviceManager() {}
};

} // namespace DeviceManager
} // namespace chip
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

// include the CHIPProjectConfig from config/standalone
#include <CHIPProjectConfig.h>

#define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY 0

#define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT 1

#define CHIP_DEVICE_CONFIG_ENABLE_EXTENDED_DISCOVERY 1

#define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONABLE_DEVICE_TYPE 1

#define CHIP_DEVICE_CONFIG_DEVICE_TYPE 0x002D // Air Purifier

#define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONABLE_DEVICE_NAME 1

#define CHIP_DEVICE_ENABLE_PORT_PARAMS 1

#define CHIP_DEVICE_CONFIG_DEVICE_NAME "Air Purifier"
43 changes: 43 additions & 0 deletions examples/air-purifier-app/ameba/main/include/DeviceCallbacks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @file DeviceCallbacks.h
*
* Implementations for the DeviceManager callbacks for this application
*
**/

#pragma once

#include "CHIPDeviceManager.h"
#include <app/util/af-types.h>
#include <app/util/basic-types.h>
#include <platform/CHIPDeviceLayer.h>

class DeviceCallbacks : public chip::DeviceManager::CHIPDeviceManagerCallbacks
{
public:
virtual void DeviceEventCallback(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg);
void PostAttributeChangeCallback(chip::EndpointId endpointId, chip::ClusterId clusterId, chip::AttributeId attributeId,
uint8_t type, uint16_t size, uint8_t * value) override;

private:
void OnInternetConnectivityChange(const chip::DeviceLayer::ChipDeviceEvent * event);
void OnIdentifyPostAttributeChangeCallback(chip::EndpointId endpointId, chip::AttributeId attributeId, uint8_t * value);
};
28 changes: 28 additions & 0 deletions examples/air-purifier-app/cc32xx/.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright (c) 2020 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import("//build_overrides/build.gni")

# The location of the build configuration file.
buildconfig = "${build_root}/config/BUILDCONFIG.gn"

# CHIP uses angle bracket includes.
check_system_includes = true

default_args = {
target_cpu = "arm"
target_os = "freertos"

import("//args.gni")
}
126 changes: 126 additions & 0 deletions examples/air-purifier-app/cc32xx/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Copyright (c) 2020 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import("//build_overrides/build.gni")
import("//build_overrides/chip.gni")
import("//build_overrides/ti_simplelink_sdk.gni")
import("${build_root}/config/defaults.gni")
import("${chip_root}/src/platform/device.gni")
import("${ti_simplelink_sdk_build_root}/ti_simplelink_executable.gni")
import("${ti_simplelink_sdk_build_root}/ti_simplelink_sdk.gni")

assert(current_os == "freertos")

project_dir = "${chip_root}/examples/air-purifier-app/cc32xx"

ti_simplelink_sdk("sdk") {
include_dirs = [ "${project_dir}/main/include" ]

defines = []
if (is_debug) {
defines += [ "BUILD_RELEASE=0" ]
} else {
defines += [ "BUILD_RELEASE=1" ]
}
}

ti_sysconfig("sysconfig") {
sources = [ "${project_dir}/chip.syscfg" ]
outputs = [
"ti_drivers_net_wifi_config.c",
"ti_net_config.c",
"ti_drivers_config.c",
"ti_drivers_config.h",
]
}

source_set("air-purifier_app_sdk") {
defines = []

configs -= [ "${build_root}/config/compiler:std_default" ]
configs += [ ":sdk_posix_config" ]

sources = [
"${chip_root}/src/platform/cc32xx/Logging.cpp",
"${project_dir}/main/cc32xxWifiInit.c",
"${project_dir}/main/main.cpp",
"${ti_simplelink_sdk_root}/examples/rtos/common/ifmod/lwip_if.c",
"${ti_simplelink_sdk_root}/examples/rtos/common/ifmod/utils_if.c",
"${ti_simplelink_sdk_root}/examples/rtos/common/ifmod/wifi_if.c",
]

include_dirs = [
"${project_dir}/include",
"${project_dir}/main",
"${project_dir}/main/ifmod/",
"${chip_root}/src/platform/cc32xx",
"${chip_root}/examples/platform/cc32xx",
]

deps = [
":sdk",
":sysconfig",
"${chip_root}/examples/air-purifier-app/air-purifier-common",
"${chip_root}/src/lib",
"${chip_root}/src/setup_payload",
]
}

ti_simplelink_executable("air-purifier_app") {
defines = []
output_name = "chip-${ti_simplelink_board}-air-purifier-example.out"

sources = [
"${chip_root}/examples/air-purifier-app/air-purifier-common/src/air-purifier-manager.cpp",
"${chip_root}/examples/air-purifier-app/air-purifier-common/src/air-quality-sensor-manager.cpp",
"${chip_root}/examples/air-purifier-app/air-purifier-common/src/filter-delegates.cpp",
"${project_dir}/main/AppTask.cpp",
"${project_dir}/main/CHIPDeviceManager.cpp",
"${project_dir}/main/CXXExceptionStubs.cpp",
"${project_dir}/main/DeviceCallbacks.cpp",
"${project_dir}/main/ZclCallbacks.cpp",
]

deps = [
":air-purifier_app_sdk",
":sdk",
":sysconfig",
"${chip_root}/examples/air-purifier-app/air-purifier-common",
"${chip_root}/examples/platform/cc32xx:cc32xx-attestation-credentials",
"${chip_root}/src/lib",
"${chip_root}/src/setup_payload",
]

include_dirs = [
"${project_dir}",
"${project_dir}/main",
"${chip_root}/examples/air-purifier-app/air-purifier-common/include",
]

cflags = [
"-Wno-implicit-fallthrough",
"-Wno-sign-compare",
"-Wconversion",
]

output_dir = root_out_dir
}

group("cc32xx") {
deps = [ ":air-purifier_app" ]
}

group("default") {
deps = [ ":cc32xx" ]
}
163 changes: 163 additions & 0 deletions examples/air-purifier-app/cc32xx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Matter `CC32XXSF` Air Purifier Example Application

An example application showing the use of [Matter][matter] on the Texas
Instruments CC32XX family of Wireless MCUs.

---

- [Matter `CC32XXSF` Air Purifier Example Application](#matter-cc32xxsf-air-purifier-example-application)
- [Introduction](#introduction)
- [Device UI](#device-ui)
- [Building](#building)
- [Preparation](#preparation)
- [Compilation](#compilation)
- [Adding DAC Certificates](#adding-dac-certificates)
- [Programming](#programming)
- [Code Composer Studio](#code-composer-studio)
- [Viewing Logging Output](#viewing-logging-output)
- [Running the Example](#running-the-example)
- [Provisioning](#provisioning)
- [Bluetooth LE Provisioning](#bluetooth-le-provisioning)

---

## Introduction

The CC32XX air purifier example application provides a working demonstration of
a connected air purifier device. This uses the open-source CHIP implementation
and the Texas Instruments SimpleLink™ Wi-Fi® CC32xx software development kit.

By default this example targets the [CC3235SF_LAUNCHXL][cc3235sf_launchxl]
LaunchPad, but the example application is enabled to build on the whole `CC32XX`
family of MCUs.

The air purifier example is intended to serve both as a means to explore the
workings of CHIP, as well as a template for creating real products based on the
Texas Instruments devices.

## Device UI

The left button (`BTN-1`) is used to enable provisioning (provisioning is
enabled as "oneshot" by default). The right button (`BTN-2`) long press is used
to reset the device.

## Building

### Preparation

Some initial setup is necessary for preparing the build environment. This
section will need to be done when migrating to new versions of the SDK. This
guide assumes that the environment is linux based, and recommends Ubuntu 20.04.

- Download and install [SysConfig][sysconfig] ([recommended
version][sysconfig_recommended]). This can be done simply with the following
commands.

```
$ cd ~
$ wget https://software-dl.ti.com/ccs/esd/sysconfig/sysconfig-1.13.0_2553-setup.run
$ chmod +x sysconfig-1.13.0_2553-setup.run
$ ./sysconfig-1.13.0_2553-setup.run
```
- Run the bootstrap script to setup the build environment.
```
$ cd ~/connectedhomeip
$ source ./scripts/bootstrap.sh
```
### Compilation
It is necessary to activate the environment in every new shell. Then run GN and
Ninja to build the executable.
- Activate the build environment with the repository activate script.
```
$ cd ~/connectedhomeip
$ source ./scripts/activate.sh
```
- Run the build to produce a default executable. By default on Linux the
Sysconfig is located in a `ti` folder in the user's home directory, and you
must provide the absolute path for it. For example
`/home/username/ti/sysconfig_1.13.0`. On Windows the default directory is
`C:\ti`. Take note of this install path, as it will be used in the next
step.
```
$ cd ~/connectedhomeip/examples/air-purifier-app/cc32xx
$ gn gen out/debug --args="ti_sysconfig_root=\"$HOME/ti/sysconfig_1.13.0\""
$ ninja -C out/debug
```
## Adding DAC Certificates
To add custom DAC Certificates, the `CC32XXDeviceAttestationCreds.cpp` file in
`examples/platform/cc32xx` can be modified. The private key, public key, DAC
cert and PAI cert arrays all need to be replaced.
## Programming
Loading the built image onto a LaunchPad is supported through Code Composer
Studio (CCS). Code Composer Studio can be used to load the image and debug the
source code. UniFlash programming (bin) image is not generated currently.
### Code Composer Studio
Programming with CCS will allow for a full debug environment within the IDE.
This is accomplished by creating a target connection to the XDS110 debugger and
starting a project-less debug session. The CCS IDE will attempt to find the
source files on the local machine based on the debug information embedded within
the ELF. CCS may prompt you to find the source code if the image was built on
another machine or the source code is located in a different location than is
recorded within the ELF.
Download and install [Code Composer Studio][ccs].
First open CCS and create a new workspace.
Create a target connection (sometimes called the CCXML) for your target SoC and
debugger as described in the [Manual Method][ccs_manual_method] section of the
CCS User's Guide.
Next initiate a project-less debug session as described in the [Manual
Launch][ccs_manual_launch] section of the CCS User's Guide.
CCS should switch to the debug view described in the [After
Launch][ccs_after_launch] section of the User's Guide. The SoC core will likely
be disconnected and symbols will not be loaded. Connect to the core as described
in the [Debug View][ccs_debug_view] section of the User's Guide. Once the core
is connected, use the `Load` button on the toolbar to load the ELF image.
Note that the default configuration of the CCXML uses 2-wire cJTAG instead of
the full 4-wire JTAG connection to match the default jumper configuration of the
LaunchPad.
## Viewing Logging Output
By default the log output will be sent to the Application/User UART. Open a
terminal emulator to that port to see the output with the following options:
| Parameter | Value |
| ------------ | -------- |
| Speed (baud) | `115200` |
| Data bits | `8` |
| Stop bits | `1` |
| Parity | `None` |
| Flow control | `None` |
## Running the Example
### Provisioning
The first step to bring the Matter device onto the network is to provision it.
The example accomplishes this through the proprietary SimpleLink provisioning
method (AP or Smart Config) using the SimpleLink Starter Pro mobile app. Once
the device is connected to the local AP, commissioning can be triggered using
"OnNetwork" configuration.
#### Bluetooth LE Provisioning
BLE provisioning is not supported currently.
44 changes: 44 additions & 0 deletions examples/air-purifier-app/cc32xx/args.gni
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright (c) 2020 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import("//build_overrides/chip.gni")
import("${chip_root}/config/standalone/args.gni")
import("${chip_root}/examples/platform/cc32xx/args.gni")

ti_simplelink_sdk_target = get_label_info(":sdk", "label_no_toolchain")
ti_simplelink_sysconfig_target =
get_label_info(":sysconfig", "label_no_toolchain")

ti_simplelink_board = "CC3235SF_LAUNCHXL"

# use -Os instead of -Og
#is_debug = false

# disable BLE for now
chip_config_network_layer_ble = false
chip_bypass_rendezvous = false

#enable logging
chip_progress_logging = true
chip_detail_logging = true
chip_automation_logging = true

## Disable lock tracking, since our FreeRTOS configuration does not set
# INCLUDE_xSemaphoreGetMutexHolder
chip_stack_lock_tracking = "none"

matter_device_vid = "0xFFF1"
matter_device_pid = "0x8006"
matter_software_ver = "0x0001"
matter_software_ver_str = "1.0d1"
1 change: 1 addition & 0 deletions examples/air-purifier-app/cc32xx/build_overrides
74 changes: 74 additions & 0 deletions examples/air-purifier-app/cc32xx/chip.syscfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Import the modules used in this configuration.
*/
const Display = scripting.addModule("/ti/display/Display");
const Display1 = Display.addInstance();
const SPI = scripting.addModule("/ti/drivers/SPI");
const Button = scripting.addModule("/ti/drivers/apps/Button");
const Button1 = Button.addInstance();
const Button2 = Button.addInstance();
const LED = scripting.addModule("/ti/drivers/apps/LED");
const LED1 = LED.addInstance();
const LED2 = LED.addInstance();
const LED3 = LED.addInstance();
const SimpleLinkWifi = scripting.addModule("/ti/drivers/net/wifi/SimpleLinkWifi");
const net_utils = scripting.addModule("/ti/drivers/net/wifi/net_utils", {}, false);
const net_utils1 = net_utils.addInstance();
const HTTPClient = scripting.addModule("/ti/net/HTTPClient", {}, false);
const HTTPClient1 = HTTPClient.addInstance();
const MQTT = scripting.addModule("/ti/net/MQTT", {}, false);
const MQTT1 = MQTT.addInstance();
const SNTP = scripting.addModule("/ti/net/SNTP");
const SlNet = scripting.addModule("/ti/net/SlNet", {}, false);
const SlNet1 = SlNet.addInstance();

/**
* Write custom configuration values to the imported modules.
*/
Display1.$name = "CONFIG_Display_0";
Display1.$hardware = system.deviceData.board.components.XDS110UART;
Display1.uart.$name = "CONFIG_UART2_0";

const Power = scripting.addModule("/ti/drivers/Power", {}, false);
Power.parkPins.$name = "ti_drivers_power_PowerCC32XXPins0";

Button1.$hardware = system.deviceData.board.components.SW2;
Button1.$name = "CONFIG_BTN_LEFT";

Button2.$hardware = system.deviceData.board.components.SW3;
Button2.$name = "CONFIG_BTN_RIGHT";

LED1.$hardware = system.deviceData.board.components.LED_BLUE;
LED1.$name = "CONFIG_LED_BLUE";

LED2.$hardware = system.deviceData.board.components.LED_GREEN;
LED2.$name = "CONFIG_LED_GREEN";

LED3.$hardware = system.deviceData.board.components.LED_RED;
LED3.dimmable = true;
LED3.$name = "CONFIG_LED_RED";

net_utils1.$name = "CONFIG_NET_UTILS_0";

HTTPClient1.$name = "CONFIG_HTTPCLIENT_0";

MQTT1.$name = "CONFIG_MQTT_0";

SlNet1.$name = "CONFIG_SLNET_0";

/**
* Pinmux solution for unlocked pins/peripherals. This ensures that minor changes to the automatic solver in a future
* version of the tool will not impact the pinmux you originally saw. These lines can be completely deleted in order to
* re-solve from scratch.
*/
Display1.uart.uart.$suggestSolution = "UART1";
Display1.uart.uart.txPin.$suggestSolution = "ball.55";
Display1.uart.uart.txDmaChannel.$suggestSolution = "UDMA_CH11";
Display1.uart.uart.rxPin.$suggestSolution = "ball.57";
Display1.uart.uart.rxDmaChannel.$suggestSolution = "UDMA_CH10";
Button1.button.$suggestSolution = "boosterpack.3";
Button2.button.$suggestSolution = "boosterpack.11";
LED1.ledPin.$suggestSolution = "boosterpack.29";
LED2.ledPin.$suggestSolution = "boosterpack.10";
LED3.pwmPin.timer.$suggestSolution = "Timer3";
LED3.pwmPin.timer.pwmPin.$suggestSolution = "boosterpack.9";
31 changes: 31 additions & 0 deletions examples/air-purifier-app/cc32xx/main/AppConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

// Logging
#ifdef __cplusplus
extern "C" {
#endif

int cc32xxLogInit(void);
void cc32xxLog(const char * aFormat, ...);
#define PLAT_LOG(...) cc32xxLog(__VA_ARGS__);

#ifdef __cplusplus
}
#endif
57 changes: 57 additions & 0 deletions examples/air-purifier-app/cc32xx/main/AppEvent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

struct AppEvent;
typedef void (*EventHandler)(AppEvent *);

struct AppEvent
{
enum AppEventType
{
kEventType_None = 0,
kEventType_ButtonLeft,
kEventType_ButtonRight,
kEventType_AppEvent,
};

enum AppEventButtonType
{
kAppEventButtonType_None = 0,
kAppEventButtonType_Clicked,
kAppEventButtonType_LongClicked,
};

enum AppEventType Type;

union
{
struct
{
enum AppEventButtonType Type;
} ButtonEvent;

struct
{
void * Context;
} BoltLockEvent;
};

EventHandler Handler;
};
Loading

0 comments on commit 5210149

Please sign in to comment.