Unable Set Measured value for CarbonDioxideConcentration cluster

We are trying to add CarbonDioxideConcentration cluster in matter. we are able to add temperature and humidity clusters and values which are updated are getting read in chip tool properly.

From the post (https://devzone.nordicsemi.com/f/nordic-q-a/111148/matter-setting-carbondioxideconcentrationmeasurement-value) got to know that to set value for CarboDiaxide it needs to follow the sample example https://github.com/nrfconnect/sdk-connectedhomeip/blob/ca3672fbb939180da8e43e6994e01c89fa9dcd02/examples/air-quality-sensor-app/air-quality-sensor-common/src/air-quality-sensor-manager.cpp#L20

we implemented similar way but when try to read values in chip tool for CarbonDioxideConcentration it still fails.
Command have Used  : chip-tool carbondioxideconcentrationmeasurement read measured-value 1110111100001110 2
Error : ( [TOO] Run command failure: IM Error 0x00000501: General error: 0x01 (FAILURE) )

Any Hints or support will be appreciated to get it working


Have attached the code to add Temperature, Humidity and CarbonDioxideConcentration clusters in Light Bulb sample. The changes which are done for getting CarbonDioxideConcentration
values are as below 
app_task.h

app_task.cpp




app_task.h file

app_task.h

/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 * 
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */
#include "board/board.h"

#include "pwm/pwm_device.h"
#include <app/clusters/concentration-measurement-server/concentration-measurement-server.h>

#include <platform/CHIPDeviceLayer.h>
#include <app-common/zap-generated/attributes/Accessors.h>
#include <app-common/zap-generated/cluster-objects.h>


#pragma once


struct k_timer;
struct Identify;

enum class LightingActor : uint8_t { Remote, Button };

struct LightingEvent {
	uint8_t Action;
	LightingActor Actor;
};



class AppTask {
public:
	static AppTask &Instance()
	{
		static AppTask sAppTask;
		return sAppTask;
	};

	CHIP_ERROR StartApp();

	void UpdateClusterState();
	void InitPWMDDevice();
	Nrf::PWMDevice &GetPWMDevice() { return mPWMDevice; }

	static void IdentifyStartHandler(Identify *);
	static void IdentifyStopHandler(Identify *);
	static void TriggerIdentifyEffectHandler(Identify *);
	static void TriggerEffectTimerTimeoutCallback(k_timer *timer);

// pj
	// void StartSensorTimer();
	void StartSensorTimer(int time);
	static void StopSensorTimer();
	static void SensorMeasureHandler();
	static void SensorActivateHandler();
	static void SensorDeactivateHandler();
	    // Constructor declaration
		AppTask();
		
private:
	CHIP_ERROR Init();
	
	static void LightingActionEventHandler(const LightingEvent &event);
	static void ButtonEventHandler(Nrf::ButtonState state, Nrf::ButtonMask hasChanged);

	static void ActionInitiated(Nrf::PWMDevice::Action_t action, int32_t actor);
	static void ActionCompleted(Nrf::PWMDevice::Action_t action, int32_t actor);
	
	chip::app::Clusters::ConcentrationMeasurement::Instance<true, true, true, true, true, true> co2_cluster;
	bool mInitSuccess;
#ifdef CONFIG_AWS_IOT_INTEGRATION
	static bool AWSIntegrationCallback(struct aws_iot_integration_cb_data *data);
#endif

	Nrf::PWMDevice mPWMDevice;
};

app_task.cpp

app_task.cpp

/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 * 
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include "app_task.h"
#include <cstdlib>
#include <cstdio>
#include<stdio.h>
#include <optional>
#ifdef CONFIG_AWS_IOT_INTEGRATION
#include "aws_iot_integration.h"
#endif

#include "app/matter_init.h"
#include "app/task_executor.h"

#if defined(CONFIG_PWM)
#include "pwm/pwm_device.h"
#endif

#ifdef CONFIG_CHIP_OTA_REQUESTOR
#include "dfu/ota/ota_util.h"
#endif

#include <app-common/zap-generated/attributes/Accessors.h>
#include <app-common/zap-generated/cluster-objects.h>
#include <app/DeferredAttributePersistenceProvider.h>
#include <app/clusters/identify-server/identify-server.h>
#include <app/server/OnboardingCodesUtil.h>
#include <app/server/Server.h>

#include <zephyr/logging/log.h>
k_timer sSensorTimer;
LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL);

using namespace ::chip;
using namespace ::chip::app;
using namespace ::chip::DeviceLayer;

namespace
{
constexpr EndpointId kLightEndpointId = 1;
constexpr EndpointId kAllSenEndpointId=2;
constexpr EndpointId kSensorEndpointId=3;
constexpr uint8_t kDefaultMinLevel = 0;
constexpr uint8_t kDefaultMaxLevel = 254;
constexpr uint16_t kTriggerEffectTimeout = 5000;
constexpr uint16_t kTriggerEffectFinishTimeout = 1000;

k_timer sTriggerEffectTimer;

Identify sIdentify = { kLightEndpointId, AppTask::IdentifyStartHandler, AppTask::IdentifyStopHandler,
		       Clusters::Identify::IdentifyTypeEnum::kVisibleIndicator, AppTask::TriggerIdentifyEffectHandler };

bool sIsTriggerEffectActive = false;

#if defined(CONFIG_PWM)
const struct pwm_dt_spec sLightPwmDevice = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led1));
#endif

// Define a custom attribute persister which makes actual write of the CurrentLevel attribute value
// to the non-volatile storage only when it has remained constant for 5 seconds. This is to reduce
// the flash wearout when the attribute changes frequently as a result of MoveToLevel command.
// DeferredAttribute object describes a deferred attribute, but also holds a buffer with a value to
// be written, so it must live so long as the DeferredAttributePersistenceProvider object.
DeferredAttribute gCurrentLevelPersister(ConcreteAttributePath(kLightEndpointId, Clusters::LevelControl::Id,
							       Clusters::LevelControl::Attributes::CurrentLevel::Id));
DeferredAttributePersistenceProvider gDeferredAttributePersister(Server::GetInstance().GetDefaultAttributePersister(),
								 Span<DeferredAttribute>(&gCurrentLevelPersister, 1),
								 System::Clock::Milliseconds32(5000));

#define APPLICATION_BUTTON_MASK DK_BTN2_MSK
} /* namespace */

void AppTask::IdentifyStartHandler(Identify *)
{
	Nrf::PostTask(
		[] { Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Blink(Nrf::LedConsts::kIdentifyBlinkRate_ms); });
}

void AppTask::IdentifyStopHandler(Identify *)
{
	Nrf::PostTask([] {
		Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Set(false);

#if defined(CONFIG_PWM)
		Instance().mPWMDevice.ApplyLevel();
#endif
	});
}

void AppTask::TriggerEffectTimerTimeoutCallback(k_timer *timer)
{
	LOG_INF("Identify effect completed");

	sIsTriggerEffectActive = false;

	Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Set(false);

#if defined(CONFIG_PWM)
	Instance().mPWMDevice.ApplyLevel();
#endif
}

void AppTask::TriggerIdentifyEffectHandler(Identify *identify)
{
	switch (identify->mCurrentEffectIdentifier) {
	/* Just handle all effects in the same way. */
	case Clusters::Identify::EffectIdentifierEnum::kBlink:
	case Clusters::Identify::EffectIdentifierEnum::kBreathe:
	case Clusters::Identify::EffectIdentifierEnum::kOkay:
	case Clusters::Identify::EffectIdentifierEnum::kChannelChange:
		LOG_INF("Identify effect identifier changed to %d",
			static_cast<uint8_t>(identify->mCurrentEffectIdentifier));

		sIsTriggerEffectActive = false;

		k_timer_stop(&sTriggerEffectTimer);
		k_timer_start(&sTriggerEffectTimer, K_MSEC(kTriggerEffectTimeout), K_NO_WAIT);

#if defined(CONFIG_PWM)
		Instance().mPWMDevice.SuppressOutput();
#endif
		Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Blink(Nrf::LedConsts::kIdentifyBlinkRate_ms);

		break;
	case Clusters::Identify::EffectIdentifierEnum::kFinishEffect:
		LOG_INF("Identify effect finish triggered");
		k_timer_stop(&sTriggerEffectTimer);
		k_timer_start(&sTriggerEffectTimer, K_MSEC(kTriggerEffectFinishTimeout), K_NO_WAIT);
		break;
	case Clusters::Identify::EffectIdentifierEnum::kStopEffect:
		if (sIsTriggerEffectActive) {
			sIsTriggerEffectActive = false;

			k_timer_stop(&sTriggerEffectTimer);

			Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Set(false);

#if defined(CONFIG_PWM)
			Instance().mPWMDevice.ApplyLevel();
#endif
		}
		break;
	default:
		LOG_ERR("Received invalid effect identifier.");
		break;
	}
}

void AppTask::LightingActionEventHandler(const LightingEvent &event)
{
#if defined(CONFIG_PWM)
	Nrf::PWMDevice::Action_t action = Nrf::PWMDevice::INVALID_ACTION;
	int32_t actor = 0;
	if (event.Actor == LightingActor::Button) {
		action = Instance().mPWMDevice.IsTurnedOn() ? Nrf::PWMDevice::OFF_ACTION : Nrf::PWMDevice::ON_ACTION;
		actor = static_cast<int32_t>(event.Actor);
	}

	if (action == Nrf::PWMDevice::INVALID_ACTION || !Instance().mPWMDevice.InitiateAction(action, actor, NULL)) {
		LOG_INF("An action could not be initiated.");
	}
#else
	Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Set(!Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).GetState());
#endif
}

void AppTask::ButtonEventHandler(Nrf::ButtonState state, Nrf::ButtonMask hasChanged)
{
	if ((APPLICATION_BUTTON_MASK & hasChanged) & state) {
		Nrf::PostTask([] {
			LightingEvent event;
			event.Actor = LightingActor::Button;
			SensorActivateHandler();
			LightingActionEventHandler(event);
		});
	}
}

#ifdef CONFIG_AWS_IOT_INTEGRATION
bool AppTask::AWSIntegrationCallback(struct aws_iot_integration_cb_data *data)
{
	LOG_INF("Attribute change requested from AWS IoT: %d", data->value);

	Protocols::InteractionModel::Status status;

	VerifyOrDie(data->error == 0);

	if (data->attribute_id == ATTRIBUTE_ID_ONOFF) {
		/* write the new on/off value */
		status = Clusters::OnOff::Attributes::OnOff::Set(kLightEndpointId, data->value);
		if (status != Protocols::InteractionModel::Status::Success) {
			LOG_ERR("Updating on/off cluster failed: %x", to_underlying(status));
			return false;
		}
	} else if (data->attribute_id == ATTRIBUTE_ID_LEVEL_CONTROL) {
		/* write the current level */
		status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kLightEndpointId, data->value);

		if (status != Protocols::InteractionModel::Status::Success) {
			LOG_ERR("Updating level cluster failed: %x", to_underlying(status));
			return false;
		}
	}

	return true;
}
#endif /* CONFIG_AWS_IOT_INTEGRATION */

#if defined(CONFIG_PWM)
void AppTask::ActionInitiated(Nrf::PWMDevice::Action_t action, int32_t actor)
{
	if (action == Nrf::PWMDevice::ON_ACTION) {
		LOG_INF("Turn On Action has been initiated");
	} else if (action == Nrf::PWMDevice::OFF_ACTION) {
		LOG_INF("Turn Off Action has been initiated");
	} else if (action == Nrf::PWMDevice::LEVEL_ACTION) {
		LOG_INF("Level Action has been initiated");
	}
}

void AppTask::ActionCompleted(Nrf::PWMDevice::Action_t action, int32_t actor)
{
	if (action == Nrf::PWMDevice::ON_ACTION) {
		LOG_INF("Turn On Action has been completed");
	} else if (action == Nrf::PWMDevice::OFF_ACTION) {
		LOG_INF("Turn Off Action has been completed");
	} else if (action == Nrf::PWMDevice::LEVEL_ACTION) {
		LOG_INF("Level Action has been completed");
	}

	if (actor == static_cast<int32_t>(LightingActor::Button)) {
		Instance().UpdateClusterState();
	}
}
#endif /* CONFIG_PWM */

void AppTask::UpdateClusterState()
{
	SystemLayer().ScheduleLambda([this] {
#if defined(CONFIG_PWM)
		/* write the new on/off value */
		Protocols::InteractionModel::Status status =
			Clusters::OnOff::Attributes::OnOff::Set(kLightEndpointId, mPWMDevice.IsTurnedOn());
#else
		Protocols::InteractionModel::Status status = Clusters::OnOff::Attributes::OnOff::Set(
			kLightEndpointId, Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).GetState());
#endif
		if (status != Protocols::InteractionModel::Status::Success) {
			LOG_ERR("Updating on/off cluster failed: %x", to_underlying(status));
		}

#if defined(CONFIG_PWM)
		/* write the current level */
		status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kLightEndpointId, mPWMDevice.GetLevel());
#else
		/* write the current level */
		if (Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).GetState()) {
			status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kLightEndpointId, 100);
		} else {
			status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kLightEndpointId, 0);
		}
#endif

		if (status != Protocols::InteractionModel::Status::Success) {
			LOG_ERR("Updating level cluster failed: %x", to_underlying(status));
		}
	});
}

void AppTask::InitPWMDDevice()
{
#if defined(CONFIG_PWM)
	/* Initialize lighting device (PWM) */
	uint8_t minLightLevel = kDefaultMinLevel;
	Clusters::LevelControl::Attributes::MinLevel::Get(kLightEndpointId, &minLightLevel);

	uint8_t maxLightLevel = kDefaultMaxLevel;
	Clusters::LevelControl::Attributes::MaxLevel::Get(kLightEndpointId, &maxLightLevel);

	Clusters::LevelControl::Attributes::CurrentLevel::TypeInfo::Type currentLevel;
	Clusters::LevelControl::Attributes::CurrentLevel::Get(kLightEndpointId, currentLevel);

	int ret =
		mPWMDevice.Init(&sLightPwmDevice, minLightLevel, maxLightLevel, currentLevel.ValueOr(kDefaultMaxLevel));
	if (ret != 0) {
		LOG_ERR("Failed to initialize PWD device.");
	}

	mPWMDevice.SetCallbacks(ActionInitiated, ActionCompleted);
#endif
}

//pj
void AppTask::SensorActivateHandler()
{
	AppTask::Instance().StartSensorTimer(5000);//StartSensorTimer(500);
}

void AppTask::SensorDeactivateHandler()
{
        StopSensorTimer();
}

AppTask::AppTask():co2_cluster(kAllSenEndpointId, 
	chip::app::Clusters::CarbonDioxideConcentrationMeasurement::Id, 
	chip::app::Clusters::ConcentrationMeasurement::MeasurementMediumEnum::kAir,
	chip::app::Clusters::ConcentrationMeasurement::MeasurementUnitEnum::kPpm)
{
    // Initialize the CO2 cluster
    // co2_cluster.Init();
	CHIP_ERROR initResult = co2_cluster.Init();

    // Check if initialization not successful
    if (initResult != CHIP_NO_ERROR) {
        // Log the error
        LOG_ERR("Failed to initialize CO2 cluster, error code: ");
        // Set the initialization success flag to false
        mInitSuccess = false;
    }
}


void AppTask::SensorMeasureHandler()
{
	printf("************ Temp value ************\n");
//kAllSenEndpointIdk
	int16_t temperature = int16_t(rand()%5000);
	printf("Sample Sensor Temperature : %d\n",temperature);
        chip::app::Clusters::TemperatureMeasurement::Attributes::MeasuredValue::Set(
                /* endpoint ID 2*/ kAllSenEndpointId, /* temperature in 0.01*C  int16_t(rand() % 5000)*/temperature);
				
	int16_t humidity = int16_t(rand()%100);
	printf("Sample Sensor Humidity : %d\n",humidity);
	chip::app::Clusters::RelativeHumidityMeasurement::Attributes::MeasuredValue::Set(
				/* endpoint ID 3 */ kAllSenEndpointId, /* temperature in 0.01*C  int16_t(rand() % 5000)*/humidity);

	float value = float(rand() % 5000);
	AppTask& appTask = AppTask::Instance();
	appTask.co2_cluster.SetMeasuredValue(chip::app::DataModel::MakeNullable(2.0f));  //(chip::app::DataModel::Nullable<float>(value));//(value);
	LOG_INF("Sample Sensor CO2 : %f\n",value);
	// appTask.co2_cluster.SetMeasuredValue(DataModel::Nullable<float>(value));
}

void SensorTimerHandler(k_timer *timer)
{
        Nrf::PostTask([] { AppTask::SensorMeasureHandler(); });
}

void AppTask::StartSensorTimer(int time)
{
        k_timer_start(&sSensorTimer, K_MSEC(time), K_MSEC(time));
}

void AppTask::StopSensorTimer()
{
        k_timer_stop(&sSensorTimer);
}


CHIP_ERROR AppTask::Init()
{
	/* Initialize Matter stack */
	ReturnErrorOnFailure(Nrf::Matter::PrepareServer(Nrf::Matter::InitData{ .mPostServerInitClbk = [] {
		app::SetAttributePersistenceProvider(&gDeferredAttributePersister);
		return CHIP_NO_ERROR;
	} }));

	if (!Nrf::GetBoard().Init(ButtonEventHandler)) {
		LOG_ERR("User interface initialization failed.");
		return CHIP_ERROR_INCORRECT_STATE;
	}

	// co2_cluster();
	/* Register Matter event handler that controls the connectivity status LED based on the captured Matter network
	 * state. */
	ReturnErrorOnFailure(Nrf::Matter::RegisterEventHandler(Nrf::Board::DefaultMatterEventHandler, 0));

#ifdef CONFIG_AWS_IOT_INTEGRATION
	int retAws = aws_iot_integration_register_callback(AWSIntegrationCallback);
	if (retAws) {
		LOG_ERR("aws_iot_integration_register_callback() failed");
		return chip::System::MapErrorZephyr(retAws);
	}
#endif
//pj
		k_timer_init(&sSensorTimer, &SensorTimerHandler, nullptr);
		k_timer_user_data_set(&sSensorTimer, this);
		return Nrf::Matter::StartServer();
}

CHIP_ERROR AppTask::StartApp()
{
	ReturnErrorOnFailure(Init());

	while (true) {
		Nrf::DispatchNextTask();
	}

	return CHIP_NO_ERROR;
}

  

using zap tool i add cluster 

Parents Reply
  • Hi again,

    Maria Gilje said:
    I got a response to my internal ticket earlier this week, but it was about changing the storage option to RAM. Since you are having some issues with changing the storage option as well I requested some information on why the storage option is reset to External after changing it to RAM in ZAP-tool.

    The storage option reverting back to External after changing it to RAM in ZAP tool could be an issue with ZAP tool when the app is opened and closed several times. Is the reverting consistently taking place?

    Dhaval Dalvadi said:
    We found this post Matter setting CarbonDioxideConcentrationMeasurement value. which mentions to read measured value for carbon dioxide some extra steps needed to be performed compare to temperature re humidity cluster.

    We tried those steps as well but we might be missing something as complete solution is still not mentioned in that post 

    Can you check if there is any document available on it?
    Or if you can provide some steps or guidance that will be helpful for us to resolve this issue

    I have not found other documentation on this for now, but I may get some more details from my internal ticket.

    Best regards,

    Maria

Children
  • Maria Gilje said:
    The storage option reverting back to External after changing it to RAM in ZAP tool could be an issue with ZAP tool when the app is opened and closed several times.

    Our developers has reproduced this issue now and are investigating it. The .zap file reflects the change, but the storage option is still external, so our developers are investigating what the source of the revert is.

    Thank you for your patience while we are investigating this.

    Best regards,

    Maria

  •  Thanks for the update
    Is there any timeline possible to target or we should follow up?
    its already more then a month and not able to understand what is the root cause of this problem.

    We are stuck from implementing matter core clusters which we used to hope should be implemented in SDK or to have some documentation to implement if not available in SDK.

  • Hello,

    Dhaval Dalvadi said:
    Is there any timeline possible to target or we should follow up?
    its already more then a month and not able to understand what is the root cause of this problem.

    I'm not able to provide a proper timeline for you. If your project timeline is critical and you are in need of expedited support, your Regional Sales Manager can evaluate if we are able to provide that for you. Let me know if you don't know who your RSM is.

    Dhaval Dalvadi said:
    We are stuck from implementing matter core clusters which we used to hope should be implemented in SDK or to have some documentation to implement if not available in SDK.

    I understand that being stuck from this is frustrating. You uncovered some behaviour that we have not seen before, so we do need to take the proper time to investigate and find a proper solution.

    Best regards,

    Maria

Related