From 48e6b265dedd744daca38ec00fa2bb6056b17cc4 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 19 Nov 2024 20:51:42 -0300 Subject: [PATCH 1/8] feat(matter): add new endpoint - color temperature light --- CMakeLists.txt | 2 + .../MatterDimmableLight.ino | 14 +- .../MatterOnOffLight/MatterOnOffLight.ino | 9 +- .../Matter_CW_WW_Light/Matter_CW_WW_Light.ino | 190 ++++++++++++++ .../examples/Matter_CW_WW_Light/ci.json | 7 + libraries/Matter/keywords.txt | 13 + libraries/Matter/src/Matter.h | 3 + .../MatterColorTemperatureLight.cpp | 245 ++++++++++++++++++ .../MatterColorTemperatureLight.h | 94 +++++++ .../MatterEndpoints/MatterDimmableLight.cpp | 6 + .../src/MatterEndpoints/MatterDimmableLight.h | 4 +- .../Matter/src/MatterUtil/ColorFormat.cpp | 231 +++++++++++++++++ libraries/Matter/src/MatterUtil/ColorFormat.h | 51 ++++ 13 files changed, 857 insertions(+), 12 deletions(-) create mode 100644 libraries/Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino create mode 100644 libraries/Matter/examples/Matter_CW_WW_Light/ci.json create mode 100644 libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp create mode 100644 libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h create mode 100644 libraries/Matter/src/MatterUtil/ColorFormat.cpp create mode 100644 libraries/Matter/src/MatterUtil/ColorFormat.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ba41d9ae962..c62c7c3c89c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,6 +169,8 @@ set(ARDUINO_LIBRARY_OpenThread_SRCS set(ARDUINO_LIBRARY_Matter_SRCS libraries/Matter/src/MatterEndpoints/MatterOnOffLight.cpp libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp + libraries/Matter/src/MatterEndpoints/MatterTemperatureLight.cpp + libraries/Matter/src/MatterUtil/ColorFormat.cpp libraries/Matter/src/Matter.cpp) set(ARDUINO_LIBRARY_PPP_SRCS diff --git a/libraries/Matter/examples/MatterDimmableLight/MatterDimmableLight.ino b/libraries/Matter/examples/MatterDimmableLight/MatterDimmableLight.ino index ded0118b7d1..cac511926aa 100644 --- a/libraries/Matter/examples/MatterDimmableLight/MatterDimmableLight.ino +++ b/libraries/Matter/examples/MatterDimmableLight/MatterDimmableLight.ino @@ -22,7 +22,9 @@ MatterDimmableLight DimmableLight; // it will keep last OnOff & Brightness state stored, using Preferences -Preferences lastStatePref; +Preferences matterPref; +const char *onOffPrefKey = "OnOff"; +const char *brightnessPrefKey = "Brightness"; // set your board RGB LED pin here #ifdef RGB_BUILTIN @@ -51,8 +53,8 @@ bool setLightState(bool state, uint8_t brightness) { digitalWrite(ledPin, LOW); } // store last Brightness and OnOff state for when the Light is restarted / power goes off - lastStatePref.putUChar("lastBrightness", brightness); - lastStatePref.putBool("lastOnOffState", state); + matterPref.putUChar(brightnessPrefKey, brightness); + matterPref.putBool(onOffPrefKey, state); // This callback must return the success state to Matter core return true; } @@ -86,11 +88,11 @@ void setup() { delay(500); // Initialize Matter EndPoint - lastStatePref.begin("matterLight", false); + matterPref.begin("MatterPrefs", false); // default OnOff state is ON if not stored before - bool lastOnOffState = lastStatePref.getBool("lastOnOffState", true); + bool lastOnOffState = matterPref.getBool(onOffPrefKey, true); // default brightness ~= 6% (15/255) - uint8_t lastBrightness = lastStatePref.getUChar("lastBrightness", 15); + uint8_t lastBrightness = matterPref.getUChar(brightnessPrefKey, 15); DimmableLight.begin(lastOnOffState, lastBrightness); // set the callback function to handle the Light state change DimmableLight.onChange(setLightState); diff --git a/libraries/Matter/examples/MatterOnOffLight/MatterOnOffLight.ino b/libraries/Matter/examples/MatterOnOffLight/MatterOnOffLight.ino index 675e9e989f2..0bdd0eb19b7 100644 --- a/libraries/Matter/examples/MatterOnOffLight/MatterOnOffLight.ino +++ b/libraries/Matter/examples/MatterOnOffLight/MatterOnOffLight.ino @@ -22,7 +22,8 @@ MatterOnOffLight OnOffLight; // it will keep last OnOff state stored, using Preferences -Preferences lastStatePref; +Preferences matterPref; +const char *onOffPrefKey = "OnOff"; // set your board LED pin here #ifdef LED_BUILTIN @@ -48,7 +49,7 @@ bool setLightOnOff(bool state) { digitalWrite(ledPin, LOW); } // store last OnOff state for when the Light is restarted / power goes off - lastStatePref.putBool("lastOnOffState", state); + matterPref.putBool(onOffPrefKey, state); // This callback must return the success state to Matter core return true; } @@ -82,8 +83,8 @@ void setup() { delay(500); // Initialize Matter EndPoint - lastStatePref.begin("matterLight", false); - bool lastOnOffState = lastStatePref.getBool("lastOnOffState", true); + matterPref.begin("MatterPrefs", false); + bool lastOnOffState = matterPref.getBool(onOffPrefKey, true); OnOffLight.begin(lastOnOffState); OnOffLight.onChange(setLightOnOff); diff --git a/libraries/Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino b/libraries/Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino new file mode 100644 index 00000000000..abb81d53a22 --- /dev/null +++ b/libraries/Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino @@ -0,0 +1,190 @@ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +// Matter Manager +#include +#include +#include + +// List of Matter Endpoints for this Node +// Color Temperature CW/WW Light Endpoint +MatterColorTemperatureLight CW_WW_Light; + +// it will keep last OnOff & Brightness state stored, using Preferences +Preferences matterPref; +const char *onOffPrefKey = "OnOff"; +const char *brightnessPrefKey = "Brightness"; +const char *temperaturePrefKey = "Temperature"; + +// set your board RGB LED pin here +#ifdef RGB_BUILTIN +const uint8_t ledPin = RGB_BUILTIN; +#else +const uint8_t ledPin = 2; // Set your pin here if your board has not defined LED_BUILTIN +#warning "Do not forget to set the RGB LED pin" +#endif + +// set your board USER BUTTON pin here +const uint8_t buttonPin = 0; // Set your pin here. Using BOOT Button. C6/C3 use GPIO9. + +// WiFi is manually set and started +const char *ssid = "your-ssid"; // Change this to your WiFi SSID +const char *password = "your-password"; // Change this to your WiFi password + +// Set the RGB LED Light based on the current state of the Color Temperature Light +bool setLightState(bool state, uint8_t brightness, uint16_t temperature_Mireds) { + + if (state) { +#ifdef RGB_BUILTIN + CtColor_t ct = { temperature_Mireds }; + RgbColor_t rgb_ct = CTToRgb(ct); + // simple intensity correction + float brightnessPercent = (float)brightness / MatterColorTemperatureLight::MAX_BRIGHTNESS; + rgb_ct.r = brightnessPercent * rgb_ct.r; + rgb_ct.g = brightnessPercent * rgb_ct.g; + rgb_ct.b = brightnessPercent * rgb_ct.b; + // set the RGB LED + rgbLedWrite(ledPin, rgb_ct.r, rgb_ct.g, rgb_ct.b); +#else + // No Color RGB LED, just use the brightness to control the LED + analogWrite(ledPin, _brightness); +#endif + } else { + digitalWrite(ledPin, LOW); + } + // store last Brightness and OnOff state for when the Light is restarted / power goes off + matterPref.putUChar(brightnessPrefKey, brightness); + matterPref.putBool(onOffPrefKey, state); + matterPref.putUShort(temperaturePrefKey, temperature_Mireds); + // This callback must return the success state to Matter core + return true; +} + +void setup() { + // Initialize the USER BUTTON (Boot button) GPIO that will act as a toggle switch + pinMode(buttonPin, INPUT_PULLUP); + // Initialize the LED (light) GPIO and Matter End Point + pinMode(ledPin, OUTPUT); + + Serial.begin(115200); + while (!Serial) { + delay(100); + } + + // We start by connecting to a WiFi network + Serial.print("Connecting to "); + Serial.println(ssid); + // enable IPv6 + WiFi.enableIPv6(true); + // Manually connect to WiFi + WiFi.begin(ssid, password); + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println("\r\nWiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + delay(500); + + // Initialize Matter EndPoint + matterPref.begin("MatterPrefs", false); + // default OnOff state is ON if not stored before + bool lastOnOffState = matterPref.getBool(onOffPrefKey, true); + // default brightness ~= 6% (15/255) + uint8_t lastBrightness = matterPref.getUChar(brightnessPrefKey, 15); + // default temperature ~= 454 Mireds (Warm White) + uint16_t lastTemperature = matterPref.getUShort(temperaturePrefKey, MatterColorTemperatureLight::WARM_WHITE_COLOR_TEMPERATURE); + CW_WW_Light.begin(lastOnOffState, lastBrightness, lastTemperature); + // set the callback function to handle the Light state change + CW_WW_Light.onChange(setLightState); + + // lambda functions are used to set the attribute change callbacks + CW_WW_Light.onChangeOnOff([](bool state) { + Serial.printf("Light OnOff changed to %s\r\n", state ? "ON" : "OFF"); + return true; + }); + CW_WW_Light.onChangeBrightness([](uint8_t level) { + Serial.printf("Light Brightness changed to %d\r\n", level); + return true; + }); + CW_WW_Light.onChangeColorTemperature([](uint16_t temperature) { + Serial.printf("Light Color Temperature changed to %d\r\n", temperature); + return true; + }); + + // Matter beginning - Last step, after all EndPoints are initialized + Matter.begin(); + // This may be a restart of a already commissioned Matter accessory + if (Matter.isDeviceCommissioned()) { + Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use."); + Serial.printf("Initial state: %s | brightness: %d | Color Temperature: %d mireds \r\n", CW_WW_Light ? "ON" : "OFF", CW_WW_Light.getBrightness(), CW_WW_Light.getColorTemperature()); + // configure the Light based on initial on-off state and brightness + CW_WW_Light.updateAccessory(); + } +} +// Button control +uint32_t button_time_stamp = 0; // debouncing control +bool button_state = false; // false = released | true = pressed +const uint32_t debouceTime = 250; // button debouncing time (ms) +const uint32_t decommissioningTimeout = 10000; // keep the button pressed for 10s to decommission the light + +void loop() { + // Check Matter Light Commissioning state, which may change during execution of loop() + if (!Matter.isDeviceCommissioned()) { + Serial.println(""); + Serial.println("Matter Node is not commissioned yet."); + Serial.println("Initiate the device discovery in your Matter environment."); + Serial.println("Commission it to your Matter hub with the manual pairing code or QR code"); + Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str()); + Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str()); + // waits for Matter Light Commissioning. + uint32_t timeCount = 0; + while (!Matter.isDeviceCommissioned()) { + delay(100); + if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec + Serial.println("Matter Node not commissioned yet. Waiting for commissioning."); + } + } + Serial.printf("Initial state: %s | brightness: %d | Color Temperature: %d mireds \r\n", CW_WW_Light ? "ON" : "OFF", CW_WW_Light.getBrightness(), CW_WW_Light.getColorTemperature()); + // configure the Light based on initial on-off state and brightness + CW_WW_Light.updateAccessory(); + Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use."); + } + + // A button is also used to control the light + // Check if the button has been pressed + if (digitalRead(buttonPin) == LOW && !button_state) { + // deals with button debouncing + button_time_stamp = millis(); // record the time while the button is pressed. + button_state = true; // pressed. + } + + // Onboard User Button is used as a Light toggle switch or to decommission it + uint32_t time_diff = millis() - button_time_stamp; + if (button_state && time_diff > debouceTime && digitalRead(buttonPin) == HIGH) { + button_state = false; // released + // Toggle button is released - toggle the light + Serial.println("User button released. Toggling Light!"); + CW_WW_Light.toggle(); // Matter Controller also can see the change + + // Factory reset is triggered if the button is pressed longer than 10 seconds + if (time_diff > decommissioningTimeout) { + Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again."); + CW_WW_Light = false; // turn the light off + Matter.decommission(); + } + } +} diff --git a/libraries/Matter/examples/Matter_CW_WW_Light/ci.json b/libraries/Matter/examples/Matter_CW_WW_Light/ci.json new file mode 100644 index 00000000000..556a8a9ee6b --- /dev/null +++ b/libraries/Matter/examples/Matter_CW_WW_Light/ci.json @@ -0,0 +1,7 @@ +{ + "fqbn_append": "PartitionScheme=huge_app", + "requires": [ + "CONFIG_SOC_WIFI_SUPPORTED=y", + "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" + ] +} diff --git a/libraries/Matter/keywords.txt b/libraries/Matter/keywords.txt index 98abce410b1..6bd297802e9 100644 --- a/libraries/Matter/keywords.txt +++ b/libraries/Matter/keywords.txt @@ -10,6 +10,7 @@ Matter KEYWORD1 ArduinoMatter KEYWORD1 MatterOnOffLight KEYWORD1 MatterDimmableLight KEYWORD1 +MatterColorTemperatureLight KEYWORD1 MatterEndPoint KEYWORD1 ####################################### @@ -30,12 +31,24 @@ setOnOff KEYWORD2 getOnOff KEYWORD2 setBrightness KEYWORD2 getBrightness KEYWORD2 +setTemperature KEYWORD2 +getTemperature KEYWORD2 toggle KEYWORD2 updateAccessory KEYWORD2 onChange KEYWORD2 onChangeOnOff KEYWORD2 onChangeBrightness KEYWORD2 +onChangeTemperature KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### + +MAX_BRIGHTNESS LITERAL1 +MAX_COLOR_TEMPERATURE LITERAL1 +MIN_COLOR_TEMPERATURE LITERAL1 +COOL_WHITE_COLOR_TEMPERATURE LITERAL1 +DAYLIGHT_WHITE_COLOR_TEMPERATURE LITERAL1 +WHITE_COLOR_TEMPERATURE LITERAL1 +SOFT_WHITE_COLOR_TEMPERATURE LITERAL1 +WARM_WHITE_COLOR_TEMPERATURE LITERAL1 diff --git a/libraries/Matter/src/Matter.h b/libraries/Matter/src/Matter.h index f88b7788016..e9d8b715388 100644 --- a/libraries/Matter/src/Matter.h +++ b/libraries/Matter/src/Matter.h @@ -18,8 +18,10 @@ #include #include +#include #include #include +#include using namespace esp_matter; @@ -47,6 +49,7 @@ class ArduinoMatter { // list of Matter EndPoints Friend Classes friend class MatterOnOffLight; friend class MatterDimmableLight; + friend class MatterColorTemperatureLight; protected: static void _init(); diff --git a/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp b/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp new file mode 100644 index 00000000000..7bbcb83dcfe --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp @@ -0,0 +1,245 @@ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include +#include + +using namespace esp_matter; +using namespace esp_matter::endpoint; +using namespace chip::app::Clusters; + +bool MatterColorTemperatureLight::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) { + bool ret = true; + if (!started) { + log_e("Matter CW_WW Light device has not begun."); + return false; + } + + log_d("CW_WW Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32); + + if (endpoint_id == getEndPointId()) { + switch (cluster_id) { + case OnOff::Id: + if (attribute_id == OnOff::Attributes::OnOff::Id) { + log_d("CW_WW Light On/Off State changed to %d", val->val.b); + if (_onChangeOnOffCB != NULL) { + ret &= _onChangeOnOffCB(val->val.b); + } + if (_onChangeCB != NULL) { + ret &= _onChangeCB(val->val.b, brightnessLevel, colorTemperatureLevel); + } + if (ret == true) { + onOffState = val->val.b; + } + } + break; + case LevelControl::Id: + if (attribute_id == LevelControl::Attributes::CurrentLevel::Id) { + log_d("CW_WW Light Brightness changed to %d", val->val.u8); + if (_onChangeBrightnessCB != NULL) { + ret &= _onChangeBrightnessCB(val->val.u8); + } + if (_onChangeCB != NULL) { + ret &= _onChangeCB(onOffState, val->val.u8, colorTemperatureLevel); + } + if (ret == true) { + brightnessLevel = val->val.u8; + } + } + break; + case ColorControl::Id: + if (attribute_id == ColorControl::Attributes::ColorTemperatureMireds::Id) { + log_d("CW_WW Light Temperature changed to %d", val->val.u16); + if (_onChangeTemperatureCB != NULL) { + ret &= _onChangeTemperatureCB(val->val.u16); + } + if (_onChangeCB != NULL) { + ret &= _onChangeCB(onOffState, brightnessLevel, val->val.u16); + } + if (ret == true) { + colorTemperatureLevel = val->val.u16; + } + } + break; + } + } + return ret; +} + +MatterColorTemperatureLight::MatterColorTemperatureLight() {} + +MatterColorTemperatureLight::~MatterColorTemperatureLight() { + end(); +} + +bool MatterColorTemperatureLight::begin(bool initialState, uint8_t brightness, uint16_t ColorTemperature) { + ArduinoMatter::_init(); + color_temperature_light::config_t light_config; + + light_config.on_off.on_off = initialState; + light_config.on_off.lighting.start_up_on_off = nullptr; + onOffState = initialState; + + light_config.level_control.current_level = brightness; + light_config.level_control.lighting.start_up_current_level = nullptr; + brightnessLevel = brightness; + + light_config.color_control.color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature; + light_config.color_control.enhanced_color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature; + light_config.color_control.color_temperature.color_temperature_mireds = ColorTemperature; + light_config.color_control.color_temperature.startup_color_temperature_mireds = nullptr; + colorTemperatureLevel = ColorTemperature; + + // endpoint handles can be used to add/modify clusters. + endpoint_t *endpoint = color_temperature_light::create(node::get(), &light_config, ENDPOINT_FLAG_NONE, (void *)this); + if (endpoint == nullptr) { + log_e("Failed to create CW_WW light endpoint"); + return false; + } + + setEndPointId(endpoint::get_id(endpoint)); + log_i("CW_WW Light created with endpoint_id %d", getEndPointId()); + + /* Mark deferred persistence for some attributes that might be changed rapidly */ + cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id); + attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id); + attribute::set_deferred_persistence(current_level_attribute); + + cluster_t *color_control_cluster = cluster::get(endpoint, ColorControl::Id); + attribute_t *color_temp_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::ColorTemperatureMireds::Id); + attribute::set_deferred_persistence(color_temp_attribute); + + started = true; + return true; +} + +void MatterColorTemperatureLight::end() { + started = false; +} + +bool MatterColorTemperatureLight::setOnOff(bool newState) { + if (!started) { + log_e("Matter CW_WW Light device has not begun."); + return false; + } + + // avoid processing the a "no-change" + if (onOffState == newState) { + return true; + } + + onOffState = newState; + + endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); + cluster_t *cluster = cluster::get(endpoint, OnOff::Id); + attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + attribute::get_val(attribute, &val); + + if (val.val.b != onOffState) { + val.val.b = onOffState; + attribute::update(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id, &val); + } + return true; +} + +void MatterColorTemperatureLight::updateAccessory() { + if (_onChangeCB != NULL) { + _onChangeCB(onOffState, brightnessLevel, colorTemperatureLevel); + } +} + +bool MatterColorTemperatureLight::getOnOff() { + return onOffState; +} + +bool MatterColorTemperatureLight::toggle() { + return setOnOff(!onOffState); +} + +bool MatterColorTemperatureLight::setBrightness(uint8_t newBrightness) { + if (!started) { + log_w("Matter CW_WW Light device has not begun."); + return false; + } + + // avoid processing the a "no-change" + if (brightnessLevel == newBrightness) { + return true; + } + + brightnessLevel = newBrightness; + + endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); + cluster_t *cluster = cluster::get(endpoint, LevelControl::Id); + attribute_t *attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id); + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + attribute::get_val(attribute, &val); + + if (val.val.u8 != brightnessLevel) { + val.val.u8 = brightnessLevel; + attribute::update(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, &val); + } + return true; +} + +uint8_t MatterColorTemperatureLight::getBrightness() { + return brightnessLevel; +} + +bool MatterColorTemperatureLight::setColorTemperature(uint16_t newTemperature) { + if (!started) { + log_w("Matter CW_WW Light device has not begun."); + return false; + } + + // avoid processing the a "no-change" + if (colorTemperatureLevel == newTemperature) { + return true; + } + + colorTemperatureLevel = newTemperature; + + endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); + cluster_t *cluster = cluster::get(endpoint, ColorControl::Id); + attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::ColorTemperatureMireds::Id); + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + attribute::get_val(attribute, &val); + + if (val.val.u16 != colorTemperatureLevel) { + val.val.u16 = colorTemperatureLevel; + attribute::update(endpoint_id, ColorControl::Id, ColorControl::Attributes::ColorTemperatureMireds::Id, &val); + } + return true; +} + +uint16_t MatterColorTemperatureLight::getColorTemperature() { + return colorTemperatureLevel; +} + +MatterColorTemperatureLight::operator bool() { + return getOnOff(); +} + +void MatterColorTemperatureLight::operator=(bool newState) { + setOnOff(newState); +} +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h b/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h new file mode 100644 index 00000000000..3673097ad41 --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h @@ -0,0 +1,94 @@ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include + +class MatterColorTemperatureLight : public MatterEndPoint { +public: + static const uint8_t MAX_BRIGHTNESS = 255; + static const uint16_t MAX_COLOR_TEMPERATURE = 500; + static const uint16_t MIN_COLOR_TEMPERATURE = 100; + // main color temperature values + static const uint16_t COOL_WHITE_COLOR_TEMPERATURE = 142; + static const uint16_t DAYLIGHT_WHITE_COLOR_TEMPERATURE = 181; + static const uint16_t WHITE_COLOR_TEMPERATURE = 250; + static const uint16_t SOFT_WHITE_COLOR_TEMPERATURE = 370; + static const uint16_t WARM_WHITE_COLOR_TEMPERATURE = 454; + + MatterColorTemperatureLight(); + ~MatterColorTemperatureLight(); + // default initial state is off, brightness is 64 (25%) and temperature is 370 (Soft White) + virtual bool begin(bool initialState = false, uint8_t brightness = 64, uint16_t colorTemperature = 370); + // this will just stop processing Light Matter events + void end(); + + bool setOnOff(bool newState); // returns true if successful + bool getOnOff(); // returns current light state + bool toggle(); // returns true if successful + + bool setBrightness(uint8_t newBrightness); // returns true if successful + uint8_t getBrightness(); // returns current brightness + + bool setColorTemperature(uint16_t newTemperature); // returns true if successful + uint16_t getColorTemperature(); // returns current temperature + + // used to update the state of the light using the current Matter Light internal state + // It is necessary to set a user callback function using onChange() to handle the physical light state + void updateAccessory(); + + operator bool(); // returns current on/off light state + void operator=(bool state); // turns light on or off + + // this function is called by Matter internal event processor. It could be overwritten by the application, if necessary. + bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val); + + // User Callback for whenever the Light On/Off state is changed by the Matter Controller + using EndPointOnOffCB = std::function; + void onChangeOnOff(EndPointOnOffCB onChangeCB) { + _onChangeOnOffCB = onChangeCB; + } + // User Callback for whenever the Light brightness value [0..255] is changed by the Matter Controller + using EndPointBrightnessCB = std::function; + void onChangeBrightness(EndPointBrightnessCB onChangeCB) { + _onChangeBrightnessCB = onChangeCB; + } + + // User Callbqck for whenever the Light temperature value is changed by the Matter Controller + using EndPointTemperatureCB = std::function; + void onChangeColorTemperature(EndPointTemperatureCB onChangeCB) { + _onChangeTemperatureCB = onChangeCB; + } + + // User Callback for whenever any parameter is changed by the Matter Controller + using EndPointCB = std::function; + void onChange(EndPointCB onChangeCB) { + _onChangeCB = onChangeCB; + } + +protected: + bool started = false; + bool onOffState = false; // default initial state is off, but it can be changed by begin(bool) + uint8_t brightnessLevel = 0; // default initial brightness is 0, but it can be changed by begin(bool, uint8_t) + uint16_t colorTemperatureLevel = 0; // default initial color temperature is 0, but it can be changed by begin(bool, uint8_t, uint16_t) + EndPointOnOffCB _onChangeOnOffCB = NULL; + EndPointBrightnessCB _onChangeBrightnessCB = NULL; + EndPointTemperatureCB _onChangeTemperatureCB = NULL; + EndPointCB _onChangeCB = NULL; +}; +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp b/libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp index 7907ae3a90a..c3991e0c0f3 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp +++ b/libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp @@ -94,6 +94,12 @@ bool MatterDimmableLight::begin(bool initialState, uint8_t brightness) { setEndPointId(endpoint::get_id(endpoint)); log_i("Dimmable Light created with endpoint_id %d", getEndPointId()); + + /* Mark deferred persistence for some attributes that might be changed rapidly */ + cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id); + attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id); + attribute::set_deferred_persistence(current_level_attribute); + started = true; return true; } diff --git a/libraries/Matter/src/MatterEndpoints/MatterDimmableLight.h b/libraries/Matter/src/MatterEndpoints/MatterDimmableLight.h index fbfccde6105..aacce883277 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterDimmableLight.h +++ b/libraries/Matter/src/MatterEndpoints/MatterDimmableLight.h @@ -25,8 +25,8 @@ class MatterDimmableLight : public MatterEndPoint { MatterDimmableLight(); ~MatterDimmableLight(); - // default initial state is off and brightness is 0 - virtual bool begin(bool initialState = false, uint8_t brightness = 0); + // default initial state is off and brightness is 64 (25%) + virtual bool begin(bool initialState = false, uint8_t brightness = 64); // this will just stop processing Light Matter events void end(); diff --git a/libraries/Matter/src/MatterUtil/ColorFormat.cpp b/libraries/Matter/src/MatterUtil/ColorFormat.cpp new file mode 100644 index 00000000000..5416455dd71 --- /dev/null +++ b/libraries/Matter/src/MatterUtil/ColorFormat.cpp @@ -0,0 +1,231 @@ +/* + * + * Copyright (c) 2021 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 "ColorFormat.h" + +#include + +// define a clamp macro to substitute the std::clamp macro which is available from C++17 onwards +#define clamp(a, min, max) ((a) < (min) ? (min) : ((a) > (max) ? (max) : (a))) + +RgbColor_t HsvToRgb(HsvColor_t hsv) +{ + RgbColor_t rgb; + + uint16_t i = hsv.h / 60; + uint16_t rgb_max = hsv.v; + uint16_t rgb_min = (uint16_t)(rgb_max * (100 - hsv.s)) / 100; + uint16_t diff = hsv.h % 60; + uint16_t rgb_adj = (uint16_t)((rgb_max - rgb_min) * diff) / 60; + + switch (i) + { + case 0: + rgb.r = (uint8_t) rgb_max; + rgb.g = (uint8_t)(rgb_min + rgb_adj); + rgb.b = (uint8_t) rgb_min; + break; + case 1: + rgb.r = (uint8_t)(rgb_max - rgb_adj); + rgb.g = (uint8_t) rgb_max; + rgb.b = (uint8_t) rgb_min; + break; + case 2: + rgb.r = (uint8_t) rgb_min; + rgb.g = (uint8_t) rgb_max; + rgb.b = (uint8_t)(rgb_min + rgb_adj); + break; + case 3: + rgb.r = (uint8_t) rgb_min; + rgb.g = (uint8_t)(rgb_max - rgb_adj); + rgb.b = (uint8_t) rgb_max; + break; + case 4: + rgb.r = (uint8_t)(rgb_min + rgb_adj); + rgb.g = (uint8_t) rgb_min; + rgb.b = (uint8_t) rgb_max; + break; + default: + rgb.r = (uint8_t) rgb_max; + rgb.g = (uint8_t) rgb_min; + rgb.b = (uint8_t)(rgb_max - rgb_adj); + break; + } + + return rgb; +} + +HsvColor_t RgbToHsv(RgbColor_t rgb) +{ + HsvColor_t hsv; + + uint16_t rgb_max = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b); + uint16_t rgb_min = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b); + uint16_t diff = rgb_max - rgb_min; + + if (diff == 0) + { + hsv.h = 0; + } + else if (rgb_max == rgb.r) + { + hsv.h = (uint8_t)(60 * ((rgb.g - rgb.b) * 100) / diff); + } + else if (rgb_max == rgb.g) + { + hsv.h = (uint8_t)(60 * (((rgb.b - rgb.r) * 100) / diff + 2 * 100)); + } + else + { + hsv.h = (uint8_t)(60 * (((rgb.r - rgb.g) * 100) / diff + 4 * 100)); + } + + if (rgb_max == 0) + { + hsv.s = 0; + } + else + { + hsv.s = (uint8_t)((diff * 100) / rgb_max); + } + + hsv.v = (uint8_t) rgb_max; + if (hsv.h < 0) + { + hsv.h += 360; + } + + return hsv; +} + +RgbColor_t XYToRgb(uint8_t Level, uint16_t currentX, uint16_t currentY) +{ + // convert xyY color space to RGB + + // https://www.easyrgb.com/en/math.php + // https://en.wikipedia.org/wiki/SRGB + // refer https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space + + // The currentX/currentY attribute contains the current value of the normalized chromaticity value of x/y. + // The value of x/y shall be related to the currentX/currentY attribute by the relationship + // x = currentX/65536 + // y = currentY/65536 + // z = 1-x-y + + RgbColor_t rgb; + + float x, y, z; + float X, Y, Z; + float r, g, b; + + x = ((float) currentX) / 65535.0f; + y = ((float) currentY) / 65535.0f; + + z = 1.0f - x - y; + + // Calculate XYZ values + + // Y - given brightness in 0 - 1 range + Y = ((float) Level) / 254.0f; + X = (Y / y) * x; + Z = (Y / y) * z; + + // X, Y and Z input refer to a D65/2° standard illuminant. + // sR, sG and sB (standard RGB) output range = 0 ÷ 255 + // convert XYZ to RGB - CIE XYZ to sRGB + X = X / 100.0f; + Y = Y / 100.0f; + Z = Z / 100.0f; + + r = (X * 3.2406f) - (Y * 1.5372f) - (Z * 0.4986f); + g = -(X * 0.9689f) + (Y * 1.8758f) + (Z * 0.0415f); + b = (X * 0.0557f) - (Y * 0.2040f) + (Z * 1.0570f); + + // apply gamma 2.2 correction + r = (r <= 0.0031308f ? 12.92f * r : (1.055f) * pow(r, (1.0f / 2.4f)) - 0.055f); + g = (g <= 0.0031308f ? 12.92f * g : (1.055f) * pow(g, (1.0f / 2.4f)) - 0.055f); + b = (b <= 0.0031308f ? 12.92f * b : (1.055f) * pow(b, (1.0f / 2.4f)) - 0.055f); + + // Round off + r = clamp(r, 0, 1); + g = clamp(g, 0, 1); + b = clamp(b, 0, 1); + + // these rgb values are in the range of 0 to 1, convert to limit of HW specific LED + rgb.r = (uint8_t)(r * 255); + rgb.g = (uint8_t)(g * 255); + rgb.b = (uint8_t)(b * 255); + + return rgb; +} + +RgbColor_t CTToRgb(CtColor_t ct) +{ + RgbColor_t rgb = {0, 0, 0}; + float r, g, b; + + if (ct.ctMireds == 0) { + return rgb; + } + // Algorithm credits to Tanner Helland: https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html + + // Convert Mireds to centiKelvins. k = 1,000,000/mired + float ctCentiKelvin = 10000 / ct.ctMireds; + + // Red + if (ctCentiKelvin <= 66) + { + r = 255; + } + else + { + r = 329.698727446f * pow(ctCentiKelvin - 60, -0.1332047592f); + } + + // Green + if (ctCentiKelvin <= 66) + { + g = 99.4708025861f * log(ctCentiKelvin) - 161.1195681661f; + } + else + { + g = 288.1221695283f * pow(ctCentiKelvin - 60, -0.0755148492f); + } + + // Blue + if (ctCentiKelvin >= 66) + { + b = 255; + } + else + { + if (ctCentiKelvin <= 19) + { + b = 0; + } + else + { + b = 138.5177312231 * log(ctCentiKelvin - 10) - 305.0447927307; + } + } + rgb.r = (uint8_t) clamp(r, 0, 255); + rgb.g = (uint8_t) clamp(g, 0, 255); + rgb.b = (uint8_t) clamp(b, 0, 255); + + return rgb; +} diff --git a/libraries/Matter/src/MatterUtil/ColorFormat.h b/libraries/Matter/src/MatterUtil/ColorFormat.h new file mode 100644 index 00000000000..8a8a1fbc106 --- /dev/null +++ b/libraries/Matter/src/MatterUtil/ColorFormat.h @@ -0,0 +1,51 @@ +/* + * + * Copyright (c) 2021 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 + +struct RgbColor_t +{ + uint8_t r; + uint8_t g; + uint8_t b; +}; + +struct HsvColor_t +{ + int16_t h; + uint8_t s; + uint8_t v; +}; + +struct XyColor_t +{ + uint16_t x; + uint16_t y; +}; + +struct CtColor_t +{ + uint16_t ctMireds; +}; + +RgbColor_t XYToRgb(uint8_t Level, uint16_t currentX, uint16_t currentY); +RgbColor_t HsvToRgb(HsvColor_t hsv); +RgbColor_t CTToRgb(CtColor_t ct); +HsvColor_t RgbToHsv(RgbColor_t rgb); From feec0159e9af50a6650f6d0db3fcfc869593130a Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 19 Nov 2024 21:04:12 -0300 Subject: [PATCH 2/8] fix(matter): color temperature new keywords --- libraries/Matter/keywords.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/Matter/keywords.txt b/libraries/Matter/keywords.txt index 6bd297802e9..ca5f760ffde 100644 --- a/libraries/Matter/keywords.txt +++ b/libraries/Matter/keywords.txt @@ -31,14 +31,14 @@ setOnOff KEYWORD2 getOnOff KEYWORD2 setBrightness KEYWORD2 getBrightness KEYWORD2 -setTemperature KEYWORD2 -getTemperature KEYWORD2 +setColorTemperature KEYWORD2 +getColorTemperature KEYWORD2 toggle KEYWORD2 updateAccessory KEYWORD2 onChange KEYWORD2 onChangeOnOff KEYWORD2 onChangeBrightness KEYWORD2 -onChangeTemperature KEYWORD2 +onChangeColorTemperature KEYWORD2 ####################################### # Constants (LITERAL1) From 2f37d7a797631cbeb21259231316f05a018a8b2e Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 19 Nov 2024 21:08:08 -0300 Subject: [PATCH 3/8] fix(matter): fixed name of source code for color temperature light --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c62c7c3c89c..9ccfecc2dac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,7 +169,7 @@ set(ARDUINO_LIBRARY_OpenThread_SRCS set(ARDUINO_LIBRARY_Matter_SRCS libraries/Matter/src/MatterEndpoints/MatterOnOffLight.cpp libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp - libraries/Matter/src/MatterEndpoints/MatterTemperatureLight.cpp + libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp libraries/Matter/src/MatterUtil/ColorFormat.cpp libraries/Matter/src/Matter.cpp) From eadfafe301d0646ebe5ebaaaa6082cd432a49264 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 19 Nov 2024 21:20:25 -0300 Subject: [PATCH 4/8] fix(matter): example has a bad symbol name --- .../Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino b/libraries/Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino index abb81d53a22..ea6d652f18b 100644 --- a/libraries/Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino +++ b/libraries/Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino @@ -58,7 +58,7 @@ bool setLightState(bool state, uint8_t brightness, uint16_t temperature_Mireds) rgbLedWrite(ledPin, rgb_ct.r, rgb_ct.g, rgb_ct.b); #else // No Color RGB LED, just use the brightness to control the LED - analogWrite(ledPin, _brightness); + analogWrite(ledPin, brightness); #endif } else { digitalWrite(ledPin, LOW); From f7c25b1f9add926c5a6e83a5fd4e820734ff5baf Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 19 Nov 2024 21:31:23 -0300 Subject: [PATCH 5/8] feat(matter): adding color operation keywords --- libraries/Matter/keywords.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/Matter/keywords.txt b/libraries/Matter/keywords.txt index ca5f760ffde..bcc99253e72 100644 --- a/libraries/Matter/keywords.txt +++ b/libraries/Matter/keywords.txt @@ -12,6 +12,10 @@ MatterOnOffLight KEYWORD1 MatterDimmableLight KEYWORD1 MatterColorTemperatureLight KEYWORD1 MatterEndPoint KEYWORD1 +CtColor_t KEYWORD1 +XyColor_t KEYWORD1 +HsvColor_t KEYWORD1 +RgbColor_t KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -39,6 +43,10 @@ onChange KEYWORD2 onChangeOnOff KEYWORD2 onChangeBrightness KEYWORD2 onChangeColorTemperature KEYWORD2 +XYToRgb KEYWORD2 +HsvToRgb KEYWORD2 +CTToRgb KEYWORD2 +RgbToHsv KEYWORD2 ####################################### # Constants (LITERAL1) From 3e0d084d1a6e1ce1d821999783e1e688700e6862 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:38:40 +0000 Subject: [PATCH 6/8] ci(pre-commit): Apply automatic fixes --- .../Matter_CW_WW_Light/Matter_CW_WW_Light.ino | 12 +- .../MatterColorTemperatureLight.h | 12 +- .../Matter/src/MatterUtil/ColorFormat.cpp | 434 ++++++++---------- libraries/Matter/src/MatterUtil/ColorFormat.h | 98 ++-- 4 files changed, 265 insertions(+), 291 deletions(-) diff --git a/libraries/Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino b/libraries/Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino index ea6d652f18b..39392d90225 100644 --- a/libraries/Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino +++ b/libraries/Matter/examples/Matter_CW_WW_Light/Matter_CW_WW_Light.ino @@ -47,7 +47,7 @@ bool setLightState(bool state, uint8_t brightness, uint16_t temperature_Mireds) if (state) { #ifdef RGB_BUILTIN - CtColor_t ct = { temperature_Mireds }; + CtColor_t ct = {temperature_Mireds}; RgbColor_t rgb_ct = CTToRgb(ct); // simple intensity correction float brightnessPercent = (float)brightness / MatterColorTemperatureLight::MAX_BRIGHTNESS; @@ -130,7 +130,10 @@ void setup() { // This may be a restart of a already commissioned Matter accessory if (Matter.isDeviceCommissioned()) { Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use."); - Serial.printf("Initial state: %s | brightness: %d | Color Temperature: %d mireds \r\n", CW_WW_Light ? "ON" : "OFF", CW_WW_Light.getBrightness(), CW_WW_Light.getColorTemperature()); + Serial.printf( + "Initial state: %s | brightness: %d | Color Temperature: %d mireds \r\n", CW_WW_Light ? "ON" : "OFF", CW_WW_Light.getBrightness(), + CW_WW_Light.getColorTemperature() + ); // configure the Light based on initial on-off state and brightness CW_WW_Light.updateAccessory(); } @@ -158,7 +161,10 @@ void loop() { Serial.println("Matter Node not commissioned yet. Waiting for commissioning."); } } - Serial.printf("Initial state: %s | brightness: %d | Color Temperature: %d mireds \r\n", CW_WW_Light ? "ON" : "OFF", CW_WW_Light.getBrightness(), CW_WW_Light.getColorTemperature()); + Serial.printf( + "Initial state: %s | brightness: %d | Color Temperature: %d mireds \r\n", CW_WW_Light ? "ON" : "OFF", CW_WW_Light.getBrightness(), + CW_WW_Light.getColorTemperature() + ); // configure the Light based on initial on-off state and brightness CW_WW_Light.updateAccessory(); Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use."); diff --git a/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h b/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h index 3673097ad41..a37f362f475 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h +++ b/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h @@ -21,15 +21,15 @@ class MatterColorTemperatureLight : public MatterEndPoint { public: - static const uint8_t MAX_BRIGHTNESS = 255; + static const uint8_t MAX_BRIGHTNESS = 255; static const uint16_t MAX_COLOR_TEMPERATURE = 500; static const uint16_t MIN_COLOR_TEMPERATURE = 100; // main color temperature values - static const uint16_t COOL_WHITE_COLOR_TEMPERATURE = 142; - static const uint16_t DAYLIGHT_WHITE_COLOR_TEMPERATURE = 181; - static const uint16_t WHITE_COLOR_TEMPERATURE = 250; - static const uint16_t SOFT_WHITE_COLOR_TEMPERATURE = 370; - static const uint16_t WARM_WHITE_COLOR_TEMPERATURE = 454; + static const uint16_t COOL_WHITE_COLOR_TEMPERATURE = 142; + static const uint16_t DAYLIGHT_WHITE_COLOR_TEMPERATURE = 181; + static const uint16_t WHITE_COLOR_TEMPERATURE = 250; + static const uint16_t SOFT_WHITE_COLOR_TEMPERATURE = 370; + static const uint16_t WARM_WHITE_COLOR_TEMPERATURE = 454; MatterColorTemperatureLight(); ~MatterColorTemperatureLight(); diff --git a/libraries/Matter/src/MatterUtil/ColorFormat.cpp b/libraries/Matter/src/MatterUtil/ColorFormat.cpp index 5416455dd71..df3e2c79500 100644 --- a/libraries/Matter/src/MatterUtil/ColorFormat.cpp +++ b/libraries/Matter/src/MatterUtil/ColorFormat.cpp @@ -1,231 +1,203 @@ -/* - * - * Copyright (c) 2021 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 "ColorFormat.h" - -#include - -// define a clamp macro to substitute the std::clamp macro which is available from C++17 onwards -#define clamp(a, min, max) ((a) < (min) ? (min) : ((a) > (max) ? (max) : (a))) - -RgbColor_t HsvToRgb(HsvColor_t hsv) -{ - RgbColor_t rgb; - - uint16_t i = hsv.h / 60; - uint16_t rgb_max = hsv.v; - uint16_t rgb_min = (uint16_t)(rgb_max * (100 - hsv.s)) / 100; - uint16_t diff = hsv.h % 60; - uint16_t rgb_adj = (uint16_t)((rgb_max - rgb_min) * diff) / 60; - - switch (i) - { - case 0: - rgb.r = (uint8_t) rgb_max; - rgb.g = (uint8_t)(rgb_min + rgb_adj); - rgb.b = (uint8_t) rgb_min; - break; - case 1: - rgb.r = (uint8_t)(rgb_max - rgb_adj); - rgb.g = (uint8_t) rgb_max; - rgb.b = (uint8_t) rgb_min; - break; - case 2: - rgb.r = (uint8_t) rgb_min; - rgb.g = (uint8_t) rgb_max; - rgb.b = (uint8_t)(rgb_min + rgb_adj); - break; - case 3: - rgb.r = (uint8_t) rgb_min; - rgb.g = (uint8_t)(rgb_max - rgb_adj); - rgb.b = (uint8_t) rgb_max; - break; - case 4: - rgb.r = (uint8_t)(rgb_min + rgb_adj); - rgb.g = (uint8_t) rgb_min; - rgb.b = (uint8_t) rgb_max; - break; - default: - rgb.r = (uint8_t) rgb_max; - rgb.g = (uint8_t) rgb_min; - rgb.b = (uint8_t)(rgb_max - rgb_adj); - break; - } - - return rgb; -} - -HsvColor_t RgbToHsv(RgbColor_t rgb) -{ - HsvColor_t hsv; - - uint16_t rgb_max = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b); - uint16_t rgb_min = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b); - uint16_t diff = rgb_max - rgb_min; - - if (diff == 0) - { - hsv.h = 0; - } - else if (rgb_max == rgb.r) - { - hsv.h = (uint8_t)(60 * ((rgb.g - rgb.b) * 100) / diff); - } - else if (rgb_max == rgb.g) - { - hsv.h = (uint8_t)(60 * (((rgb.b - rgb.r) * 100) / diff + 2 * 100)); - } - else - { - hsv.h = (uint8_t)(60 * (((rgb.r - rgb.g) * 100) / diff + 4 * 100)); - } - - if (rgb_max == 0) - { - hsv.s = 0; - } - else - { - hsv.s = (uint8_t)((diff * 100) / rgb_max); - } - - hsv.v = (uint8_t) rgb_max; - if (hsv.h < 0) - { - hsv.h += 360; - } - - return hsv; -} - -RgbColor_t XYToRgb(uint8_t Level, uint16_t currentX, uint16_t currentY) -{ - // convert xyY color space to RGB - - // https://www.easyrgb.com/en/math.php - // https://en.wikipedia.org/wiki/SRGB - // refer https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space - - // The currentX/currentY attribute contains the current value of the normalized chromaticity value of x/y. - // The value of x/y shall be related to the currentX/currentY attribute by the relationship - // x = currentX/65536 - // y = currentY/65536 - // z = 1-x-y - - RgbColor_t rgb; - - float x, y, z; - float X, Y, Z; - float r, g, b; - - x = ((float) currentX) / 65535.0f; - y = ((float) currentY) / 65535.0f; - - z = 1.0f - x - y; - - // Calculate XYZ values - - // Y - given brightness in 0 - 1 range - Y = ((float) Level) / 254.0f; - X = (Y / y) * x; - Z = (Y / y) * z; - - // X, Y and Z input refer to a D65/2° standard illuminant. - // sR, sG and sB (standard RGB) output range = 0 ÷ 255 - // convert XYZ to RGB - CIE XYZ to sRGB - X = X / 100.0f; - Y = Y / 100.0f; - Z = Z / 100.0f; - - r = (X * 3.2406f) - (Y * 1.5372f) - (Z * 0.4986f); - g = -(X * 0.9689f) + (Y * 1.8758f) + (Z * 0.0415f); - b = (X * 0.0557f) - (Y * 0.2040f) + (Z * 1.0570f); - - // apply gamma 2.2 correction - r = (r <= 0.0031308f ? 12.92f * r : (1.055f) * pow(r, (1.0f / 2.4f)) - 0.055f); - g = (g <= 0.0031308f ? 12.92f * g : (1.055f) * pow(g, (1.0f / 2.4f)) - 0.055f); - b = (b <= 0.0031308f ? 12.92f * b : (1.055f) * pow(b, (1.0f / 2.4f)) - 0.055f); - - // Round off - r = clamp(r, 0, 1); - g = clamp(g, 0, 1); - b = clamp(b, 0, 1); - - // these rgb values are in the range of 0 to 1, convert to limit of HW specific LED - rgb.r = (uint8_t)(r * 255); - rgb.g = (uint8_t)(g * 255); - rgb.b = (uint8_t)(b * 255); - - return rgb; -} - -RgbColor_t CTToRgb(CtColor_t ct) -{ - RgbColor_t rgb = {0, 0, 0}; - float r, g, b; - - if (ct.ctMireds == 0) { - return rgb; - } - // Algorithm credits to Tanner Helland: https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html - - // Convert Mireds to centiKelvins. k = 1,000,000/mired - float ctCentiKelvin = 10000 / ct.ctMireds; - - // Red - if (ctCentiKelvin <= 66) - { - r = 255; - } - else - { - r = 329.698727446f * pow(ctCentiKelvin - 60, -0.1332047592f); - } - - // Green - if (ctCentiKelvin <= 66) - { - g = 99.4708025861f * log(ctCentiKelvin) - 161.1195681661f; - } - else - { - g = 288.1221695283f * pow(ctCentiKelvin - 60, -0.0755148492f); - } - - // Blue - if (ctCentiKelvin >= 66) - { - b = 255; - } - else - { - if (ctCentiKelvin <= 19) - { - b = 0; - } - else - { - b = 138.5177312231 * log(ctCentiKelvin - 10) - 305.0447927307; - } - } - rgb.r = (uint8_t) clamp(r, 0, 255); - rgb.g = (uint8_t) clamp(g, 0, 255); - rgb.b = (uint8_t) clamp(b, 0, 255); - - return rgb; -} +/* + * + * Copyright (c) 2021 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 "ColorFormat.h" + +#include + +// define a clamp macro to substitute the std::clamp macro which is available from C++17 onwards +#define clamp(a, min, max) ((a) < (min) ? (min) : ((a) > (max) ? (max) : (a))) + +RgbColor_t HsvToRgb(HsvColor_t hsv) { + RgbColor_t rgb; + + uint16_t i = hsv.h / 60; + uint16_t rgb_max = hsv.v; + uint16_t rgb_min = (uint16_t)(rgb_max * (100 - hsv.s)) / 100; + uint16_t diff = hsv.h % 60; + uint16_t rgb_adj = (uint16_t)((rgb_max - rgb_min) * diff) / 60; + + switch (i) { + case 0: + rgb.r = (uint8_t)rgb_max; + rgb.g = (uint8_t)(rgb_min + rgb_adj); + rgb.b = (uint8_t)rgb_min; + break; + case 1: + rgb.r = (uint8_t)(rgb_max - rgb_adj); + rgb.g = (uint8_t)rgb_max; + rgb.b = (uint8_t)rgb_min; + break; + case 2: + rgb.r = (uint8_t)rgb_min; + rgb.g = (uint8_t)rgb_max; + rgb.b = (uint8_t)(rgb_min + rgb_adj); + break; + case 3: + rgb.r = (uint8_t)rgb_min; + rgb.g = (uint8_t)(rgb_max - rgb_adj); + rgb.b = (uint8_t)rgb_max; + break; + case 4: + rgb.r = (uint8_t)(rgb_min + rgb_adj); + rgb.g = (uint8_t)rgb_min; + rgb.b = (uint8_t)rgb_max; + break; + default: + rgb.r = (uint8_t)rgb_max; + rgb.g = (uint8_t)rgb_min; + rgb.b = (uint8_t)(rgb_max - rgb_adj); + break; + } + + return rgb; +} + +HsvColor_t RgbToHsv(RgbColor_t rgb) { + HsvColor_t hsv; + + uint16_t rgb_max = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b); + uint16_t rgb_min = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b); + uint16_t diff = rgb_max - rgb_min; + + if (diff == 0) { + hsv.h = 0; + } else if (rgb_max == rgb.r) { + hsv.h = (uint8_t)(60 * ((rgb.g - rgb.b) * 100) / diff); + } else if (rgb_max == rgb.g) { + hsv.h = (uint8_t)(60 * (((rgb.b - rgb.r) * 100) / diff + 2 * 100)); + } else { + hsv.h = (uint8_t)(60 * (((rgb.r - rgb.g) * 100) / diff + 4 * 100)); + } + + if (rgb_max == 0) { + hsv.s = 0; + } else { + hsv.s = (uint8_t)((diff * 100) / rgb_max); + } + + hsv.v = (uint8_t)rgb_max; + if (hsv.h < 0) { + hsv.h += 360; + } + + return hsv; +} + +RgbColor_t XYToRgb(uint8_t Level, uint16_t currentX, uint16_t currentY) { + // convert xyY color space to RGB + + // https://www.easyrgb.com/en/math.php + // https://en.wikipedia.org/wiki/SRGB + // refer https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space + + // The currentX/currentY attribute contains the current value of the normalized chromaticity value of x/y. + // The value of x/y shall be related to the currentX/currentY attribute by the relationship + // x = currentX/65536 + // y = currentY/65536 + // z = 1-x-y + + RgbColor_t rgb; + + float x, y, z; + float X, Y, Z; + float r, g, b; + + x = ((float)currentX) / 65535.0f; + y = ((float)currentY) / 65535.0f; + + z = 1.0f - x - y; + + // Calculate XYZ values + + // Y - given brightness in 0 - 1 range + Y = ((float)Level) / 254.0f; + X = (Y / y) * x; + Z = (Y / y) * z; + + // X, Y and Z input refer to a D65/2° standard illuminant. + // sR, sG and sB (standard RGB) output range = 0 ÷ 255 + // convert XYZ to RGB - CIE XYZ to sRGB + X = X / 100.0f; + Y = Y / 100.0f; + Z = Z / 100.0f; + + r = (X * 3.2406f) - (Y * 1.5372f) - (Z * 0.4986f); + g = -(X * 0.9689f) + (Y * 1.8758f) + (Z * 0.0415f); + b = (X * 0.0557f) - (Y * 0.2040f) + (Z * 1.0570f); + + // apply gamma 2.2 correction + r = (r <= 0.0031308f ? 12.92f * r : (1.055f) * pow(r, (1.0f / 2.4f)) - 0.055f); + g = (g <= 0.0031308f ? 12.92f * g : (1.055f) * pow(g, (1.0f / 2.4f)) - 0.055f); + b = (b <= 0.0031308f ? 12.92f * b : (1.055f) * pow(b, (1.0f / 2.4f)) - 0.055f); + + // Round off + r = clamp(r, 0, 1); + g = clamp(g, 0, 1); + b = clamp(b, 0, 1); + + // these rgb values are in the range of 0 to 1, convert to limit of HW specific LED + rgb.r = (uint8_t)(r * 255); + rgb.g = (uint8_t)(g * 255); + rgb.b = (uint8_t)(b * 255); + + return rgb; +} + +RgbColor_t CTToRgb(CtColor_t ct) { + RgbColor_t rgb = {0, 0, 0}; + float r, g, b; + + if (ct.ctMireds == 0) { + return rgb; + } + // Algorithm credits to Tanner Helland: https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html + + // Convert Mireds to centiKelvins. k = 1,000,000/mired + float ctCentiKelvin = 10000 / ct.ctMireds; + + // Red + if (ctCentiKelvin <= 66) { + r = 255; + } else { + r = 329.698727446f * pow(ctCentiKelvin - 60, -0.1332047592f); + } + + // Green + if (ctCentiKelvin <= 66) { + g = 99.4708025861f * log(ctCentiKelvin) - 161.1195681661f; + } else { + g = 288.1221695283f * pow(ctCentiKelvin - 60, -0.0755148492f); + } + + // Blue + if (ctCentiKelvin >= 66) { + b = 255; + } else { + if (ctCentiKelvin <= 19) { + b = 0; + } else { + b = 138.5177312231 * log(ctCentiKelvin - 10) - 305.0447927307; + } + } + rgb.r = (uint8_t)clamp(r, 0, 255); + rgb.g = (uint8_t)clamp(g, 0, 255); + rgb.b = (uint8_t)clamp(b, 0, 255); + + return rgb; +} diff --git a/libraries/Matter/src/MatterUtil/ColorFormat.h b/libraries/Matter/src/MatterUtil/ColorFormat.h index 8a8a1fbc106..ab3da2f144b 100644 --- a/libraries/Matter/src/MatterUtil/ColorFormat.h +++ b/libraries/Matter/src/MatterUtil/ColorFormat.h @@ -1,51 +1,47 @@ -/* - * - * Copyright (c) 2021 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 - -struct RgbColor_t -{ - uint8_t r; - uint8_t g; - uint8_t b; -}; - -struct HsvColor_t -{ - int16_t h; - uint8_t s; - uint8_t v; -}; - -struct XyColor_t -{ - uint16_t x; - uint16_t y; -}; - -struct CtColor_t -{ - uint16_t ctMireds; -}; - -RgbColor_t XYToRgb(uint8_t Level, uint16_t currentX, uint16_t currentY); -RgbColor_t HsvToRgb(HsvColor_t hsv); -RgbColor_t CTToRgb(CtColor_t ct); -HsvColor_t RgbToHsv(RgbColor_t rgb); +/* + * + * Copyright (c) 2021 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 + +struct RgbColor_t { + uint8_t r; + uint8_t g; + uint8_t b; +}; + +struct HsvColor_t { + int16_t h; + uint8_t s; + uint8_t v; +}; + +struct XyColor_t { + uint16_t x; + uint16_t y; +}; + +struct CtColor_t { + uint16_t ctMireds; +}; + +RgbColor_t XYToRgb(uint8_t Level, uint16_t currentX, uint16_t currentY); +RgbColor_t HsvToRgb(HsvColor_t hsv); +RgbColor_t CTToRgb(CtColor_t ct); +HsvColor_t RgbToHsv(RgbColor_t rgb); From bb3a9a5e7d24d2de76a31272957819cd4774b045 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Wed, 20 Nov 2024 16:00:33 -0300 Subject: [PATCH 7/8] feat(matter): ci codespell fix currenty -> current_y --- libraries/Matter/src/MatterUtil/ColorFormat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Matter/src/MatterUtil/ColorFormat.h b/libraries/Matter/src/MatterUtil/ColorFormat.h index ab3da2f144b..254a51c7144 100644 --- a/libraries/Matter/src/MatterUtil/ColorFormat.h +++ b/libraries/Matter/src/MatterUtil/ColorFormat.h @@ -41,7 +41,7 @@ struct CtColor_t { uint16_t ctMireds; }; -RgbColor_t XYToRgb(uint8_t Level, uint16_t currentX, uint16_t currentY); +RgbColor_t XYToRgb(uint8_t Level, uint16_t current_X, uint16_t current_Y); RgbColor_t HsvToRgb(HsvColor_t hsv); RgbColor_t CTToRgb(CtColor_t ct); HsvColor_t RgbToHsv(RgbColor_t rgb); From fbb4dd4fa2913bffce3dec76aa52e36438474f64 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Wed, 20 Nov 2024 16:01:31 -0300 Subject: [PATCH 8/8] feat(matter): ci codespell fix currenty -> current_y --- libraries/Matter/src/MatterUtil/ColorFormat.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/Matter/src/MatterUtil/ColorFormat.cpp b/libraries/Matter/src/MatterUtil/ColorFormat.cpp index df3e2c79500..41d845dcdb9 100644 --- a/libraries/Matter/src/MatterUtil/ColorFormat.cpp +++ b/libraries/Matter/src/MatterUtil/ColorFormat.cpp @@ -99,17 +99,17 @@ HsvColor_t RgbToHsv(RgbColor_t rgb) { return hsv; } -RgbColor_t XYToRgb(uint8_t Level, uint16_t currentX, uint16_t currentY) { +RgbColor_t XYToRgb(uint8_t Level, uint16_t current_X, uint16_t current_Y) { // convert xyY color space to RGB // https://www.easyrgb.com/en/math.php // https://en.wikipedia.org/wiki/SRGB // refer https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space - // The currentX/currentY attribute contains the current value of the normalized chromaticity value of x/y. - // The value of x/y shall be related to the currentX/currentY attribute by the relationship - // x = currentX/65536 - // y = currentY/65536 + // The current_X/current_Y attribute contains the current value of the normalized chromaticity value of x/y. + // The value of x/y shall be related to the current_X/current_Y attribute by the relationship + // x = current_X/65536 + // y = current_Y/65536 // z = 1-x-y RgbColor_t rgb; @@ -118,8 +118,8 @@ RgbColor_t XYToRgb(uint8_t Level, uint16_t currentX, uint16_t currentY) { float X, Y, Z; float r, g, b; - x = ((float)currentX) / 65535.0f; - y = ((float)currentY) / 65535.0f; + x = ((float)current_X) / 65535.0f; + y = ((float)current_Y) / 65535.0f; z = 1.0f - x - y;