Hello, Nordic Team!
I am developing another emergency project. And we using the ncs v3.1.1 and NRF54L15. But there is a compile problem about the custom cluster.
Here is my xml file.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configurator>
<struct name="ValveFreezeStateStruct">
<cluster code="0xFFF1FC01"/>
<item type="boolean" name="freezed"/>
<item type="int64u" name="timestamp"/>
</struct>
<struct name="ValveTemperatureStateStruct">
<cluster code="0xFFF1FC01"/>
<item type="int16u" name="temperature"/>
<item type="int64u" name="timestamp"/>
</struct>
<struct name="ValveSessionLogStruct">
<cluster code="0xFFF1FC01"/>
<item type="int64u" name="Begin"/>
<item type="int64u" name="End"/>
<item type="int16u" name="Volume"/>
</struct>
<struct name="ValveSessionStruct">
<cluster code="0xFFF1FC01"/>
<item name="Weekdays" type="int8u"/>
<item name="Hour" type="int8u"/>
<item name="Minute" type="int8u"/>
<item name="Volume" type="int16u"/>
<item name="Duration" type="int32u"/>
</struct>
<enum name="ValveSessionOperationEnum" type="int8u">
<cluster code="0xFFF1FC01"/>
<!-- create a new valve session with status-->
<item name="Disable" value="0"/>
<item name="Enable" value="1"/>
<item name="Remove" value="2"/>
</enum>
<enum name="ValveSessionStateEnum" type="int8u">
<cluster code="0xFFF1FC01"/>
<item name="Disable" value="0"/>
<item name="Enable" value="1"/>
<item name="Running" value="2"/>
</enum>
<struct name="ValveSessionStateStruct">
<cluster code="0xFFF1FC01"/>
<item name="Session" type="ValveSessionStruct"/>
<item name="State" type="ValveSessionStateEnum"/>
</struct>
<enum name="WaterStatsEnum" type="int8u">
<cluster code="0xFFF1FC01"/>
<item name="Hour" value="0"/>
<item name="Day" value="1"/>
<item name="Week" value="2"/>
<item name="PastWeek" value="3"/>
<item name="Month" value="4"/>
<item name="Year" value="5"/>
</enum>
<struct name="WaterUsageStruct">
<cluster code="0xFFF1FC01"/>
<item name="Hour" type="int32u"/>
<item name="Today" type="int32u"/>
<item name="Yesterday" type="int32u"/>
<item name="CurrentWeek" type="int32u"/>
<item name="LastWeek" type="int32u"/>
<item name="CurrentMonth" type="int32u"/>
<item name="LastMonth" type="int32u"/>
<item name="CurrentYear" type="int32u"/>
<item name="LastYear" type="int32u"/>
</struct>
<cluster>
<domain>General</domain>
<name>WaterValveData</name>
<code>0xFFF1FC01</code>
<define>WATER_VALVE_DATA_CLUSTER</define>
<description>cluster for controlling and receiving data of water valves</description>
<attribute side="server" code="0xFFF10000" define="VOLUME" type="int16u" writable="true" optional="false" default="0x00">Volume</attribute>
<attribute side="server" code="0xFFF10001" define="VOLUME_REMAINING" type="int16u" writable="true" optional="false" default="0x00">VolumeRemaining</attribute>
<attribute side="server" code="0xFFF10002" define="AQUAVALUE" type="int16u" writable="true" optional="false" default="0x00">AquaValue</attribute>
<attribute side="server" code="0xFFF10003" define="CHILD_LOCK" type="boolean" writable="true" optional="false" default="false">ChildLock</attribute>
<attribute side="server" code="0xFFF10004" define="TEMPERATURE_ADJUST" type="int8s" writable="true" optional="false" default="0x00">TemperatureAdjust</attribute>
<attribute side="server" code="0xFFF10005" define="FREEZE_THRESHOLD" type="int8s" writable="true" optional="false" default="0x00">FreezeThreshold</attribute>
<command source="client" code="0xFFF10000" name="GetValveSessionLogs" response="GetValveSessionLogsResponse">
<description>get the valve session logs</description>
<!-- One page contains 50 entries -->
<arg name="page" type="int8u" array="false"/>
</command>
<command source="server" code="0xFFF10001" name="GetValveSessionLogsResponse">
<description>response with valve session logs</description>
<arg name="total" type="int16u" array="false"/>
<arg name="logs" type="ValveSessionLogStruct" array="true"/>
</command>
<command source="client" code="0xFFF10002" name="SetValveSession" response="SetValveSessionResponse">
<description>set the valve timer session</description>
<arg name="session" type="ValveSessionStruct" array="false"/>
<arg name="operation" type="ValveSessionOperationEnum" array="false"/>
</command>
<command source="server" code="0xFFF10003" name="SetValveSessionResponse">
<description>return all the timer sessions</description>
<arg name="total" type="int8u" array="false"/>
<arg name="sessions" type="ValveSessionStateStruct" array="true"/>
</command>
<command source="client" code="0xFFF10004" name="GetAllValveSession" response="GetAllValveSessionResponse">
<description>get the valve session logs</description>
</command>
<command source="server" code="0xFFF10005" name="GetAllValveSessionResponse">
<description>response with all valve session logs</description>
<arg name="total" type="int8u" array="false"/>
<arg name="sessions" type="ValveSessionStateStruct" array="true"/>
</command>
<command source="client" code="0xFFF10006" name="GetWaterUsage" response="GetWaterUsageResponse">
<description>get the water usage</description>
</command>
<command source="server" code="0xFFF10007" name="GetWaterUsageResponse">
<description>response with water usage response</description>
<arg name="usage" type="WaterUsageStruct" array="false"/>
</command>
<command source="client" code="0xFFF10008" name="GetWaterStats" response="GetWaterStatsResponse">
<description>get the water stats</description>
<arg name="last" type="boolean" array="false"/>
<arg name="stats" type="WaterStatsEnum" array="false"/>
</command>
<command source="server" code="0xFFF10009" name="GetWaterStatsResponse">
<description>response with water stats response</description>
<arg name="total" type="int32u" array="false"/>
<arg name="stats" type="int32u" array="true"/>
</command>
<command source="client" code="0xFFF1000A" name="GetValveFreezeRecord" response="GetValveFreezeRecordResponse">
<description>get the valve freeze record</description>
<arg name="page" type="int8u" array="false"/>
</command>
<command source="server" code="0xFFF1000B" name="GetValveFreezeRecordResponse">
<description>response with valve freeze record</description>
<arg name="total" type="int32u" array="false"/>
<arg name="freeze" type="ValveFreezeStateStruct" array="true"/>
</command>
<command source="client" code="0xFFF1000C" name="GetValveTemperatureRecord" response="GetValveTemperatureRecordResponse">
<description>get the valve temperature record</description>
<arg name="page" type="int8u" array="false"/>
</command>
<command source="server" code="0xFFF1000D" name="GetValveTemperatureRecordResponse">
<description>response with valve temperature record</description>
<arg name="total" type="int32u" array="false"/>
<arg name="temperature" type="ValveTemperatureStateStruct" array="true"/>
</command>
<command source="client" code="0xFFF1000E" name="OpenValveWithVolume">
<description>open the valve with voluem</description>
<arg name="volume" type="int16u" array="false"/>
</command>
</cluster>
<deviceType>
<name>water-valve-data-device</name>
<domain>CHIP</domain>
<typeName>Water Valve Data Device</typeName>
<profileId editable="false">0x0FFF</profileId>
<deviceId editable="false">0xFFF10001</deviceId>
<class>Simple</class>
<scope>Endpoint</scope>
<clusters lockOthers="true">
<include cluster="WaterValveData" client="true" server="true" clientLocked="false" serverLocked="false"/>
</clusters>
</deviceType>
</configurator>
And I copy the zcl.json to my project and place it to src/default_zap/. And if I don't execute the zap-append and just execute the
west zap-gui -j src/default_zap/zcl.json --clusters src/default_zap/WaterValveData.xml
And we will see the error like:
champon@NC ~/D/P/GV1 (DeviceNoResponse) [250]> west zap-gui -j src/default_zap/zcl.json --clusters src/default_zap/WaterValveData.xml
Using ZAP file: /home/champon/Develop/Products/GV1/src/default_zap/GV1.zap
Using ZCL file: /home/champon/Develop/Products/GV1/src/default_zap/zcl.json
Using app templates: /home/champon/ncs/v3.1.1/modules/lib/matter/src/app/zap-templates/app-templates.json
ZAP installation directory: /home/champon/ncs/v3.1.1/modules/lib/matter/.zap-install
Found ZAP 2025.5.14 (up to date)
🔧 Using temporary state directory: /tmp/zap.Ea9wjH
[385668:1009/150431.617593:ERROR:bus.cc(407)] Failed to connect to the bus: '=' character not found or has no value following it
[385668:1009/150431.617645:ERROR:bus.cc(407)] Failed to connect to the bus: '=' character not found or has no value following it
[385668:1009/150431.617666:ERROR:bus.cc(407)] Failed to connect to the bus: '=' character not found or has no value following it
[385668:1009/150431.617676:ERROR:bus.cc(407)] Failed to connect to the bus: '=' character not found or has no value following it
Error while running sql statement:
INSERT INTO
STRUCT_ITEM (STRUCT_REF, NAME, FIELD_IDENTIFIER, IS_ARRAY, IS_ENUM, MIN_LENGTH, MAX_LENGTH, DEFAULT_VALUE, IS_WRITABLE, IS_NULLABLE, IS_OPTIONAL, IS_FABRIC_SENSITIVE, SIZE, DATA_TYPE_REF)
VALUES (
(SELECT
CASE
WHEN
(
SELECT
STRUCT_ID
FROM
STRUCT
INNER JOIN
DATA_TYPE ON STRUCT.STRUCT_ID = DATA_TYPE.DATA_TYPE_ID
INNER JOIN
DATA_TYPE_CLUSTER
ON
DATA_TYPE.DATA_TYPE_ID = DATA_TYPE_CLUSTER.DATA_TYPE_REF
WHERE
DATA_TYPE.PACKAGE_REF
IN
(2)
AND
DATA_TYPE.NAME = ?
AND
DATA_TYPE.DISCRIMINATOR_REF = (SELECT
DISCRIMINATOR_ID
FROM
DISCRIMINATOR
WHERE
NAME = "STRUCT"
AND
PACKAGE_REF
IN
(2))
AND
DATA_TYPE_CLUSTER.CLUSTER_CODE = ?
)
IS
NULL
THEN
(
SELECT
STRUCT_ID
FROM
STRUCT
INNER JOIN
DATA_TYPE ON STRUCT.STRUCT_ID = DATA_TYPE.DATA_TYPE_ID
LEFT JOIN
DATA_TYPE_CLUSTER ON DATA_TYPE.DATA_TYPE_ID = DATA_TYPE_CLUSTER.DATA_TYPE_REF
WHERE
DATA_TYPE.PACKAGE_REF
IN
(2)
AND
DATA_TYPE.NAME = ?
AND
DATA_TYPE.DISCRIMINATOR_REF = (SELECT
DISCRIMINATOR_ID
FROM
DISCRIMINATOR
WHERE
NAME = "STRUCT"
AND
PACKAGE_REF
IN
(2))
AND
DATA_TYPE_CLUSTER.CLUSTER_CODE IS NULL
)
ELSE
(
SELECT
STRUCT_ID
FROM
STRUCT
INNER JOIN
DATA_TYPE ON STRUCT.STRUCT_ID = DATA_TYPE.DATA_TYPE_ID
INNER JOIN
DATA_TYPE_CLUSTER
ON
DATA_TYPE.DATA_TYPE_ID = DATA_TYPE_CLUSTER.DATA_TYPE_REF
WHERE
DATA_TYPE.PACKAGE_REF
IN
(2)
AND
DATA_TYPE.NAME = ?
AND
DATA_TYPE.DISCRIMINATOR_REF = (SELECT
DISCRIMINATOR_ID
FROM
DISCRIMINATOR
WHERE
NAME = "STRUCT"
AND
PACKAGE_REF
IN
(2))
AND
DATA_TYPE_CLUSTER.CLUSTER_CODE = ?
)
END AS STRUCT_ID),
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
(SELECT
DATA_TYPE_ID
FROM
DATA_TYPE
WHERE
DATA_TYPE.PACKAGE_REF IN (2)
AND DATA_TYPE.NAME = ?)), values: ValveFreezeStateStruct,4294048769,ValveFreezeStateStruct,ValveFreezeStateStruct,4294048769,freezed,0,false,false,0,,,false,false,false,false,,boolean, Error: SQLITE_CONSTRAINT: NOT NULL constraint failed: STRUCT_ITEM.DATA_TYPE_REF
FATAL ERROR: Error::ThrowAsJavaScriptException napi_throw
----- Native stack trace -----
FATAL ERROR: command exited with status -6: /home/champon/ncs/v3.1.1/modules/lib/matter/.zap-install/zap /home/champon/Develop/Products/GV1/src/default_zap/GV1.zap --zcl /home/champon/Develop/Products/GV1/src/default_zap/zcl.json --gen /home/champon/ncs/v3.1.1/modules/lib/matter/src/app/zap-templates/app-templates.json --tempState
And after I execute the zap-append command:
west zap-append -o src/default_zap/zcl.json src/default_zap/WaterValveData.xml
I can execute the
west zap-gui -j src/default_zap/zcl.json --clusters src/default_zap/WaterValveData.xmlsuccessfully.
But I don't see your document ask me to execute the zap-append.
After adding the custom device, and use the following command to generate the code.
west zap-generate --full
I write a zcl_callbacks.cpp file to implement the generated commands.
#include <app/ConcreteAttributePath.h>
#include <lib/support/logging/CHIPLogging.h>
#include "platform/CHIPDeviceLayer.h"
#include <app-common/zap-generated/attributes/Accessors.h>
#include <app-common/zap-generated/callback.h>
#include <app-common/zap-generated/cluster-objects.h>
#include <sys/_stdint.h>
#include <zap-generated/PluginApplicationCallbacks.h>
#include <zap-generated/clusters/WaterValveData/Commands.h>
#include "app/data-model/List.h"
#include "app/task_executor.h"
#include "clusters/WaterValveData/Enums.h"
#include "clusters/WaterValveData/Structs.h"
#include "lib/support/logging/TextOnlyLogging.h"
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath &attributePath,
uint8_t type,
uint16_t size,
uint8_t *value)
{
ClusterId clusterId = attributePath.mClusterId;
AttributeId attributeId = attributePath.mAttributeId;
EndpointId endpointId = attributePath.mEndpointId;
uint32_t value32 = 0;
if (size == 1)
value32 = *value;
else if (size == 2)
value32 = *reinterpret_cast<uint16_t *>(value);
else if (size == 4)
value32 = *reinterpret_cast<uint32_t *>(value);
ChipLogProgress(DeviceLayer,
"====================Attribute change on endpoint %d, clusterId: %" PRIu32 ", attributeId: %" PRIu32
", type: %d, size: %d, value: %d",
endpointId,
clusterId,
attributeId,
type,
size,
value32);
}
bool emberAfWaterValveDataClusterGetWaterStatsCallback(
chip::app::CommandHandler *commandObj,
const chip::app::ConcreteCommandPath &commandPath,
const chip::app::Clusters::WaterValveData::Commands::GetWaterStats::DecodableType &commandData)
{
ChipLogProgress(DeviceLayer,
"[%s] Water stats requested, stats = %d last = %d",
__FUNCTION__,
static_cast<int>(commandData.stats),
commandData.last);
return true;
}
bool emberAfWaterValveDataClusterGetWaterUsageCallback(
chip::app::CommandHandler *commandObj,
const chip::app::ConcreteCommandPath &commandPath,
const chip::app::Clusters::WaterValveData::Commands::GetWaterUsage::DecodableType &commandData)
{
ChipLogProgress(DeviceLayer, "[%s] Water usage requested", __FUNCTION__);
return true;
}
bool emberAfWaterValveDataClusterSetValveSessionCallback(
chip::app::CommandHandler *commandObj,
const chip::app::ConcreteCommandPath &commandPath,
const chip::app::Clusters::WaterValveData::Commands::SetValveSession::DecodableType &commandData)
{
ChipLogProgress(
DeviceLayer,
"[%s] Set Valve session requested - hour: %u, minute: %u duration: %u weekdays: %u, volume: %u, operation: %d",
__FUNCTION__,
commandData.session.hour,
commandData.session.minute,
commandData.session.duration,
commandData.session.weekdays,
commandData.session.volume,
static_cast<int>(commandData.operation));
return true;
}
bool emberAfWaterValveDataClusterGetValveSessionLogsCallback(
chip::app::CommandHandler *commandObj,
const chip::app::ConcreteCommandPath &commandPath,
const chip::app::Clusters::WaterValveData::Commands::GetValveSessionLogs::DecodableType &commandData)
{
ChipLogProgress(DeviceLayer, "[%s] Get valve session logs page:%d", __FUNCTION__, commandData.page);
return true;
}
bool emberAfWaterValveDataClusterGetAllValveSessionCallback(
chip::app::CommandHandler *commandObj,
const chip::app::ConcreteCommandPath &commandPath,
const chip::app::Clusters::WaterValveData::Commands::GetAllValveSession::DecodableType &commandData)
{
return true;
}
bool emberAfWaterValveDataClusterOpenValveWithVolumeCallback(
chip::app::CommandHandler *commandObj,
const chip::app::ConcreteCommandPath &commandPath,
const chip::app::Clusters::WaterValveData::Commands::OpenValveWithVolume::DecodableType &commandData)
{
ChipLogProgress(DeviceLayer,
"[%s] Open valve with volume: %d.%dL",
__FUNCTION__,
commandData.volume / 10,
commandData.volume % 10);
return true;
}
bool emberAfWaterValveDataClusterGetValveTemperatureRecordCallback(
chip::app::CommandHandler *commandObj,
const chip::app::ConcreteCommandPath &commandPath,
const chip::app::Clusters::WaterValveData::Commands::GetValveTemperatureRecord::DecodableType &commandData)
{
ChipLogProgress(DeviceLayer, "[%s] Get valve temperature record page:%d", __FUNCTION__, commandData.page);
return true;
}
bool emberAfWaterValveDataClusterGetValveFreezeRecordCallback(
chip::app::CommandHandler *commandObj,
const chip::app::ConcreteCommandPath &commandPath,
const chip::app::Clusters::WaterValveData::Commands::GetValveFreezeRecord::DecodableType &commandData)
{
ChipLogProgress(DeviceLayer, "[%s] Get valve freeze record page:%d", __FUNCTION__, commandData.page);
return true;
}
void MatterWaterValveDataPluginServerInitCallback()
{
return;
}
void emberAfTemperatureMeasurementClusterInitCallback(chip::EndpointId endpoint)
{
ChipLogProgress(DeviceLayer, "[%s] Init temperature measurement cluster", __FUNCTION__);
}
void emberAfBooleanStateClusterServerInitCallback(chip::EndpointId endpoint)
{
}
void emberAfValveConfigurationAndControlClusterServerInitCallback(chip::EndpointId endpoint)
{
}
and here is my CMake file.
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr HINTS $ENV{ZEPHYR_BASE})
project(GV1)
include(${ZEPHYR_NRF_MODULE_DIR}/samples/matter/common/cmake/zap_helpers.cmake)
ncs_get_zap_parent_dir(ZAP_PARENT_DIR)
get_filename_component(CHIP_APP_ZAP_DIR ${ZAP_PARENT_DIR}/zap-generated REALPATH CACHE)
# Enable GNU STD support.
include(${ZEPHYR_CONNECTEDHOMEIP_MODULE_DIR}/config/nrfconnect/app/enable-gnu-std.cmake)
# NORDIC SDK APP START
# Include all source files that are located in the Matter common directory.
include(${ZEPHYR_NRF_MODULE_DIR}/samples/matter/common/cmake/source_common.cmake)
# Include Data Model utils
include(${ZEPHYR_NRF_MODULE_DIR}/samples/matter/common/cmake/data_model.cmake)
target_include_directories(app PRIVATE
src
${ZAP_PARENT_DIR}
)
target_sources(app PRIVATE
src/app_task.cpp
src/main.cpp
src/zcl_callbacks.cpp
)
ncs_configure_data_model(
EXTERNAL_CLUSTERS "WATER_VALVE_DATA_CLUSTER"
)
# NORDIC SDK APP END
But compile failed with:
4034.error.txt
And there is my template project which may help you reproduce the problem.
GV1.zip
I want to know what's wrong with my step and how to solve this problem.
Best regards,
Xiongwei.Wang