diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ccfecc2dac..8ab001c2d09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ endif() set(CORE_SRCS cores/esp32/base64.cpp cores/esp32/cbuf.cpp + cores/esp32/ColorFormat.c cores/esp32/chip-debug-report.cpp cores/esp32/esp32-hal-adc.c cores/esp32/esp32-hal-bt.c @@ -170,7 +171,7 @@ set(ARDUINO_LIBRARY_Matter_SRCS libraries/Matter/src/MatterEndpoints/MatterOnOffLight.cpp libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp - libraries/Matter/src/MatterUtil/ColorFormat.cpp + libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp libraries/Matter/src/Matter.cpp) set(ARDUINO_LIBRARY_PPP_SRCS diff --git a/cores/esp32/ColorFormat.c b/cores/esp32/ColorFormat.c new file mode 100644 index 00000000000..a01123545b3 --- /dev/null +++ b/cores/esp32/ColorFormat.c @@ -0,0 +1,279 @@ +/* + * + * 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))) + +const espHsvColor_t HSV_BLACK = {0, 0, 0}; +const espHsvColor_t HSV_WHITE = {0, 0, 254}; +const espHsvColor_t HSV_RED = {0, 254, 254}; +const espHsvColor_t HSV_YELLOW = {42, 254, 254}; +const espHsvColor_t HSV_GREEN = {84, 254, 254}; +const espHsvColor_t HSV_CYAN = {127, 254, 254}; +const espHsvColor_t HSV_BLUE = {169, 254, 254}; +const espHsvColor_t HSV_MAGENTA = {211, 254, 254}; + +const espRgbColor_t RGB_BLACK = {0, 0, 0}; +const espRgbColor_t RGB_WHITE = {255, 255, 255}; +const espRgbColor_t RGB_RED = {255, 0, 0}; +const espRgbColor_t RGB_YELLOW = {255, 255, 0}; +const espRgbColor_t RGB_GREEN = {0, 255, 0}; +const espRgbColor_t RGB_CYAN = {0, 255, 255}; +const espRgbColor_t RGB_BLUE = {0, 0, 255}; +const espRgbColor_t RGB_MAGENTA = {255, 0, 255}; + +// main color temperature values +const espCtColor_t COOL_WHITE_COLOR_TEMPERATURE = {142}; +const espCtColor_t DAYLIGHT_WHITE_COLOR_TEMPERATURE = {181}; +const espCtColor_t WHITE_COLOR_TEMPERATURE = {250}; +const espCtColor_t SOFT_WHITE_COLOR_TEMPERATURE = {370}; +const espCtColor_t WARM_WHITE_COLOR_TEMPERATURE = {454}; + +espRgbColor_t espHsvToRgbColor(uint16_t h, uint8_t s, uint8_t v) { + espHsvColor_t hsv = {h, s, v}; + return espHsvColorToRgbColor(hsv); +} + +espRgbColor_t espHsvColorToRgbColor(espHsvColor_t hsv) { + espRgbColor_t rgb; + + uint8_t region, p, q, t; + uint32_t h, s, v, remainder; + + if (hsv.s == 0) { + rgb.r = rgb.g = rgb.b = hsv.v; + } else { + h = hsv.h; + s = hsv.s; + v = hsv.v; + + region = h / 43; + remainder = (h - (region * 43)) * 6; + p = (v * (255 - s)) >> 8; + q = (v * (255 - ((s * remainder) >> 8))) >> 8; + t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8; + switch (region) { + case 0: rgb.r = v, rgb.g = t, rgb.b = p; break; + case 1: rgb.r = q, rgb.g = v, rgb.b = p; break; + case 2: rgb.r = p, rgb.g = v, rgb.b = t; break; + case 3: rgb.r = p, rgb.g = q, rgb.b = v; break; + case 4: rgb.r = t, rgb.g = p, rgb.b = v; break; + case 5: + default: rgb.r = v, rgb.g = p, rgb.b = q; break; + } + } + return rgb; +} + +espHsvColor_t espRgbToHsvColor(uint8_t r, uint8_t g, uint8_t b) { + espRgbColor_t rgb = {r, g, b}; + return espRgbColorToHsvColor(rgb); +} + +espHsvColor_t espRgbColorToHsvColor(espRgbColor_t rgb) { + espHsvColor_t hsv; + uint8_t rgbMin, rgbMax; + + rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b); + rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b); + + hsv.v = rgbMax; + if (hsv.v == 0) { + hsv.h = 0; + hsv.s = 0; + return hsv; + } + + hsv.s = 255 * (rgbMax - rgbMin) / hsv.v; + if (hsv.s == 0) { + hsv.h = 0; + return hsv; + } + if (rgbMax == rgb.r) { + hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin); + } else if (rgbMax == rgb.g) { + hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin); + } else { + hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin); + } + return hsv; +} + +espRgbColor_t espXYColorToRgbColor(uint8_t Level, espXyColor_t xy) { + return espXYToRgbColor(Level, xy.x, xy.y); +} + +espRgbColor_t espXYToRgbColor(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 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 + + espRgbColor_t rgb; + + float x, y, z; + float X, Y, Z; + float r, g, b; + + x = ((float)current_X) / 65535.0f; + y = ((float)current_Y) / 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; +} + +espXyColor_t espRgbToXYColor(uint8_t r, uint8_t g, uint8_t b) { + espRgbColor_t rgb = {r, g, b}; + return espRgbColorToXYColor(rgb); +} + +espXyColor_t espRgbColorToXYColor(espRgbColor_t rgb) { + // convert RGB to xy color space + + // 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 + + espXyColor_t xy; + + float r, g, b; + float X, Y, Z; + float x, y; + + r = ((float)rgb.r) / 255.0f; + g = ((float)rgb.g) / 255.0f; + b = ((float)rgb.b) / 255.0f; + + // convert RGB to XYZ - sRGB to CIE XYZ + r = (r <= 0.04045f ? r / 12.92f : pow((r + 0.055f) / 1.055f, 2.4f)); + g = (g <= 0.04045f ? g / 12.92f : pow((g + 0.055f) / 1.055f, 2.4f)); + b = (b <= 0.04045f ? b / 12.92f : pow((b + 0.055f) / 1.055f, 2.4f)); + + // https://gist.github.com/popcorn245/30afa0f98eea1c2fd34d + X = r * 0.649926f + g * 0.103455f + b * 0.197109f; + Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; + Z = r * 0.0000000f + g * 0.053077f + b * 1.035763f; + + // sR, sG and sB (standard RGB) input range = 0 ÷ 255 + // X, Y and Z output refer to a D65/2° standard illuminant. + X = r * 0.4124564f + g * 0.3575761f + b * 0.1804375f; + Y = r * 0.2126729f + g * 0.7151522f + b * 0.0721750f; + Z = r * 0.0193339f + g * 0.1191920f + b * 0.9503041f; + + // Calculate xy values + x = X / (X + Y + Z); + y = Y / (X + Y + Z); + + // convert to 0-65535 range + xy.x = (uint16_t)(x * 65535); + xy.y = (uint16_t)(y * 65535); + return xy; +} + +espRgbColor_t espCTToRgbColor(uint16_t ct) { + espCtColor_t ctColor = {ct}; + return espCTColorToRgbColor(ctColor); +} + +espRgbColor_t espCTColorToRgbColor(espCtColor_t ct) { + espRgbColor_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/cores/esp32/ColorFormat.h b/cores/esp32/ColorFormat.h new file mode 100644 index 00000000000..0bb87145d16 --- /dev/null +++ b/cores/esp32/ColorFormat.h @@ -0,0 +1,70 @@ +/* + * + * 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 +#ifdef __cplusplus +extern "C" { +#endif + +struct RgbColor_t { + uint8_t r; + uint8_t g; + uint8_t b; +}; + +struct HsvColor_t { + uint16_t h; + uint8_t s; + uint8_t v; +}; + +struct XyColor_t { + uint16_t x; + uint16_t y; +}; + +struct CtColor_t { + uint16_t ctMireds; +}; + +typedef struct RgbColor_t espRgbColor_t; +typedef struct HsvColor_t espHsvColor_t; +typedef struct XyColor_t espXyColor_t; +typedef struct CtColor_t espCtColor_t; + +espRgbColor_t espXYToRgbColor(uint8_t Level, uint16_t current_X, uint16_t current_Y); +espRgbColor_t espXYColorToRgb(uint8_t Level, espXyColor_t xy); +espXyColor_t espRgbColorToXYColor(espRgbColor_t rgb); +espXyColor_t espRgbToXYColor(uint8_t r, uint8_t g, uint8_t b); +espRgbColor_t espHsvColorToRgbColor(espHsvColor_t hsv); +espRgbColor_t espHsvToRgbColor(uint16_t h, uint8_t s, uint8_t v); +espRgbColor_t espCTColorToRgbColor(espCtColor_t ct); +espRgbColor_t espCTToRgbColor(uint16_t ct); +espHsvColor_t espRgbColorToHsvColor(espRgbColor_t rgb); +espHsvColor_t espRgbToHsvColor(uint8_t r, uint8_t g, uint8_t b); + +extern const espHsvColor_t HSV_BLACK, HSV_WHITE, HSV_RED, HSV_YELLOW, HSV_GREEN, HSV_CYAN, HSV_BLUE, HSV_MAGENTA; +extern const espCtColor_t COOL_WHITE_COLOR_TEMPERATURE, DAYLIGHT_WHITE_COLOR_TEMPERATURE, WHITE_COLOR_TEMPERATURE, SOFT_WHITE_COLOR_TEMPERATURE, + WARM_WHITE_COLOR_TEMPERATURE; +extern const espRgbColor_t RGB_BLACK, RGB_WHITE, RGB_RED, RGB_YELLOW, RGB_GREEN, RGB_CYAN, RGB_BLUE, RGB_MAGENTA; + +#ifdef __cplusplus +} +#endif diff --git a/libraries/ESP32/keywords.txt b/libraries/ESP32/keywords.txt index 7e36360c840..866e76babd8 100644 --- a/libraries/ESP32/keywords.txt +++ b/libraries/ESP32/keywords.txt @@ -6,14 +6,50 @@ # Datatypes (KEYWORD1) ####################################### -Serial4 KEYWORD1 +Serial4 KEYWORD1 +espCtColor_t KEYWORD1 +espXyColor_t KEYWORD1 +espHsvColor_t KEYWORD1 +espRgbColor_t KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### +espXYToRgbColor KEYWORD2 +espXYColorToRgb KEYWORD2 +espRgbColorToXYColor KEYWORD2 +espRgbToXYColor KEYWORD2 +espHsvColorToRgbColor KEYWORD2 +espHsvToRgbColor KEYWORD2 +espCTColorToRgbColor KEYWORD2 +espCTToRgbColor KEYWORD2 +espRgbColorToHsvColor KEYWORD2 +espRgbToHsvColor KEYWORD2 + ####################################### # Constants (LITERAL1) ####################################### RGB_BUILTIN LITERAL1 +HSV_BLACK LITERAL1 +HSV_WHITE LITERAL1 +HSV_RED LITERAL1 +HSV_YELLOW LITERAL1 +HSV_GREEN LITERAL1 +HSV_CYAN LITERAL1 +HSV_BLUE LITERAL1 +HSV_MAGENTA 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 +RGB_BLACK LITERAL1 +RGB_WHITE LITERAL1 +RGB_RED LITERAL1 +RGB_YELLOW LITERAL1 +RGB_GREEN LITERAL1 +RGB_CYAN LITERAL1 +RGB_BLUE LITERAL1 +RGB_MAGENTA LITERAL1 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 39392d90225..0ff30f53ec0 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,8 +47,7 @@ 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); + espRgbColor_t rgb_ct = espCTToRgbColor(temperature_Mireds); // simple intensity correction float brightnessPercent = (float)brightness / MatterColorTemperatureLight::MAX_BRIGHTNESS; rgb_ct.r = brightnessPercent * rgb_ct.r; @@ -106,7 +105,7 @@ void setup() { // 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); + uint16_t lastTemperature = matterPref.getUShort(temperaturePrefKey, WARM_WHITE_COLOR_TEMPERATURE.ctMireds); CW_WW_Light.begin(lastOnOffState, lastBrightness, lastTemperature); // set the callback function to handle the Light state change CW_WW_Light.onChange(setLightState); diff --git a/libraries/Matter/examples/Matter_ColorLight/Matter_ColorLight.ino b/libraries/Matter/examples/Matter_ColorLight/Matter_ColorLight.ino new file mode 100644 index 00000000000..2b9c4e4033a --- /dev/null +++ b/libraries/Matter/examples/Matter_ColorLight/Matter_ColorLight.ino @@ -0,0 +1,183 @@ +// 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 Light Endpoint +MatterColorLight ColorLight; + +// it will keep last OnOff & HSV Color state stored, using Preferences +Preferences matterPref; +const char *onOffPrefKey = "OnOff"; +const char *hsvColorPrefKey = "HSV"; + +// 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 Light +bool setLightState(bool state, espHsvColor_t colorHSV) { + + if (state) { +#ifdef RGB_BUILTIN + espRgbColor_t rgbColor = espHsvColorToRgbColor(colorHSV); + // set the RGB LED + rgbLedWrite(ledPin, rgbColor.r, rgbColor.g, rgbColor.b); +#else + // No Color RGB LED, just use the HSV value (brightness) to control the LED + analogWrite(ledPin, colorHSV.v); +#endif + } else { + digitalWrite(ledPin, LOW); + } + // store last HSV Color and OnOff state for when the Light is restarted / power goes off + matterPref.putBool(onOffPrefKey, state); + matterPref.putUInt(hsvColorPrefKey, colorHSV.h << 16 | colorHSV.s << 8 | colorHSV.v); + // 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 HSV color is blue HSV(169, 254, 254) + uint32_t prefHsvColor = matterPref.getUInt(hsvColorPrefKey, 169 << 16 | 254 << 8 | 254); + espHsvColor_t lastHsvColor = {uint8_t(prefHsvColor >> 16), uint8_t(prefHsvColor >> 8), uint8_t(prefHsvColor)}; + ColorLight.begin(lastOnOffState, lastHsvColor); + // set the callback function to handle the Light state change + ColorLight.onChange(setLightState); + + // lambda functions are used to set the attribute change callbacks + ColorLight.onChangeOnOff([](bool state) { + Serial.printf("Light OnOff changed to %s\r\n", state ? "ON" : "OFF"); + return true; + }); + ColorLight.onChangeColorHSV([](HsvColor_t hsvColor) { + Serial.printf("Light HSV Color changed to (%d,%d,%d)\r\n", hsvColor.h, hsvColor.s, hsvColor.v); + 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 | RGB Color: (%d,%d,%d) \r\n", ColorLight ? "ON" : "OFF", ColorLight.getColorRGB().r, ColorLight.getColorRGB().g, + ColorLight.getColorRGB().b + ); + // configure the Light based on initial on-off state and its color + ColorLight.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 | RGB Color: (%d,%d,%d) \r\n", ColorLight ? "ON" : "OFF", ColorLight.getColorRGB().r, ColorLight.getColorRGB().g, + ColorLight.getColorRGB().b + ); + // configure the Light based on initial on-off state and its color + ColorLight.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!"); + ColorLight.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."); + ColorLight = false; // turn the light off + Matter.decommission(); + } + } +} diff --git a/libraries/Matter/examples/Matter_ColorLight/ci.json b/libraries/Matter/examples/Matter_ColorLight/ci.json new file mode 100644 index 00000000000..d5f63487506 --- /dev/null +++ b/libraries/Matter/examples/Matter_ColorLight/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 bcc99253e72..39a74e76583 100644 --- a/libraries/Matter/keywords.txt +++ b/libraries/Matter/keywords.txt @@ -1,5 +1,5 @@ ####################################### -# Syntax Coloring Map For OpenThread +# Syntax Coloring Map For Matter ####################################### ####################################### @@ -10,12 +10,9 @@ Matter KEYWORD1 ArduinoMatter KEYWORD1 MatterOnOffLight KEYWORD1 MatterDimmableLight KEYWORD1 -MatterColorTemperatureLight KEYWORD1 +MatterColorTemperatureLight KEYWORD1 +MatterColorLight KEYWORD1 MatterEndPoint KEYWORD1 -CtColor_t KEYWORD1 -XyColor_t KEYWORD1 -HsvColor_t KEYWORD1 -RgbColor_t KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -37,26 +34,23 @@ setBrightness KEYWORD2 getBrightness KEYWORD2 setColorTemperature KEYWORD2 getColorTemperature KEYWORD2 +setColorRGB KEYWORD2 +getColorRGB KEYWORD2 +setColorHSV KEYWORD2 +getColorHSV KEYWORD2 toggle KEYWORD2 updateAccessory KEYWORD2 onChange KEYWORD2 onChangeOnOff KEYWORD2 onChangeBrightness KEYWORD2 -onChangeColorTemperature KEYWORD2 -XYToRgb KEYWORD2 -HsvToRgb KEYWORD2 -CTToRgb KEYWORD2 -RgbToHsv KEYWORD2 +onChangeColorTemperature KEYWORD2 +onChangeColorHSV 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 +MAX_BRIGHTNESS LITERAL1 +MAX_COLOR_TEMPERATURE LITERAL1 +MIN_COLOR_TEMPERATURE LITERAL1 diff --git a/libraries/Matter/src/Matter.cpp b/libraries/Matter/src/Matter.cpp index 857438cce03..89ef87b4db3 100644 --- a/libraries/Matter/src/Matter.cpp +++ b/libraries/Matter/src/Matter.cpp @@ -17,7 +17,6 @@ #include #include -#include "MatterEndPoint.h" using namespace esp_matter; using namespace esp_matter::attribute; diff --git a/libraries/Matter/src/Matter.h b/libraries/Matter/src/Matter.h index e9d8b715388..4d269474187 100644 --- a/libraries/Matter/src/Matter.h +++ b/libraries/Matter/src/Matter.h @@ -18,10 +18,11 @@ #include #include -#include +#include #include #include #include +#include using namespace esp_matter; @@ -50,6 +51,7 @@ class ArduinoMatter { friend class MatterOnOffLight; friend class MatterDimmableLight; friend class MatterColorTemperatureLight; + friend class MatterColorLight; protected: static void _init(); diff --git a/libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp b/libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp new file mode 100644 index 00000000000..b67cf6a23b1 --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp @@ -0,0 +1,307 @@ +// 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; + +// endpoint for color light device +namespace esp_matter { +using namespace cluster; +namespace endpoint { +namespace rgb_color_light { +typedef struct config { + cluster::descriptor::config_t descriptor; + cluster::identify::config_t identify; + cluster::groups::config_t groups; + cluster::scenes_management::config_t scenes_management; + cluster::on_off::config_t on_off; + cluster::level_control::config_t level_control; + cluster::color_control::config_t color_control; +} config_t; + +uint32_t get_device_type_id() { + return ESP_MATTER_EXTENDED_COLOR_LIGHT_DEVICE_TYPE_ID; +} + +uint8_t get_device_type_version() { + return ESP_MATTER_EXTENDED_COLOR_LIGHT_DEVICE_TYPE_VERSION; +} + +esp_err_t add(endpoint_t *endpoint, config_t *config) { + if (!endpoint) { + log_e("Endpoint cannot be NULL"); + return ESP_ERR_INVALID_ARG; + } + esp_err_t err = add_device_type(endpoint, get_device_type_id(), get_device_type_version()); + if (err != ESP_OK) { + log_e("Failed to add device type id:%" PRIu32 ",err: %d", get_device_type_id(), err); + return err; + } + + descriptor::create(endpoint, &(config->descriptor), CLUSTER_FLAG_SERVER); + cluster_t *identify_cluster = identify::create(endpoint, &(config->identify), CLUSTER_FLAG_SERVER); + identify::command::create_trigger_effect(identify_cluster); + groups::create(endpoint, &(config->groups), CLUSTER_FLAG_SERVER); + cluster_t *scenes_cluster = scenes_management::create(endpoint, &(config->scenes_management), CLUSTER_FLAG_SERVER); + scenes_management::command::create_copy_scene(scenes_cluster); + scenes_management::command::create_copy_scene_response(scenes_cluster); + + on_off::create(endpoint, &(config->on_off), CLUSTER_FLAG_SERVER, on_off::feature::lighting::get_id()); + level_control::create( + endpoint, &(config->level_control), CLUSTER_FLAG_SERVER, level_control::feature::on_off::get_id() | level_control::feature::lighting::get_id() + ); + color_control::create(endpoint, &(config->color_control), CLUSTER_FLAG_SERVER, color_control::feature::hue_saturation::get_id()); + return ESP_OK; +} + +endpoint_t *create(node_t *node, config_t *config, uint8_t flags, void *priv_data) { + endpoint_t *endpoint = endpoint::create(node, flags, priv_data); + add(endpoint, config); + return endpoint; +} +} // namespace rgb_color_light +} // namespace endpoint +} // namespace esp_matter + +bool MatterColorLight::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 RGB Color Light device has not begun."); + return false; + } + + log_d( + "RGB Color Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u, type: %u", endpoint_id, cluster_id, attribute_id, val->val.u32, + val->type + ); + + if (endpoint_id == getEndPointId()) { + switch (cluster_id) { + case OnOff::Id: + if (attribute_id == OnOff::Attributes::OnOff::Id) { + log_d("RGB Color 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, colorHSV); + } + if (ret == true) { + onOffState = val->val.b; + } + } + break; + case LevelControl::Id: + if (attribute_id == LevelControl::Attributes::CurrentLevel::Id) { + log_d("RGB Color Light Brightness changed to %d", val->val.u8); + if (_onChangeColorCB != NULL) { + ret &= _onChangeColorCB({colorHSV.h, colorHSV.s, val->val.u8}); + } + if (_onChangeCB != NULL) { + ret &= _onChangeCB(onOffState, {colorHSV.h, colorHSV.s, val->val.u8}); + } + if (ret == true) { + colorHSV.v = val->val.u8; + } + } + break; + case ColorControl::Id: + { + if (attribute_id != ColorControl::Attributes::CurrentHue::Id && attribute_id != ColorControl::Attributes::CurrentSaturation::Id) { + log_i("Color Control Attribute ID [%x] not processed.", attribute_id); + break; + } + espHsvColor_t hsvColor = {colorHSV.h, colorHSV.s, colorHSV.v}; + if (attribute_id == ColorControl::Attributes::CurrentHue::Id) { + log_d("RGB Light Hue changed to %d", val->val.u8); + hsvColor.h = val->val.u8; + } else { // attribute_id == ColorControl::Attributes::CurrentSaturation::Id) + log_d("RGB Light Saturation changed to %d", val->val.u8); + hsvColor.s = val->val.u8; + } + if (_onChangeColorCB != NULL) { + ret &= _onChangeColorCB(hsvColor); + } + if (_onChangeCB != NULL) { + ret &= _onChangeCB(onOffState, hsvColor); + } + if (ret == true) { + colorHSV = {hsvColor.h, hsvColor.s, hsvColor.v}; + } + break; + } + } + } + return ret; +} + +MatterColorLight::MatterColorLight() {} + +MatterColorLight::~MatterColorLight() { + end(); +} + +bool MatterColorLight::begin(bool initialState, espHsvColor_t _colorHSV) { + ArduinoMatter::_init(); + rgb_color_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 = _colorHSV.v; + light_config.level_control.lighting.start_up_current_level = nullptr; + + light_config.color_control.color_mode = (uint8_t)ColorControl::ColorMode::kCurrentHueAndCurrentSaturation; + light_config.color_control.enhanced_color_mode = (uint8_t)ColorControl::ColorMode::kCurrentHueAndCurrentSaturation; + light_config.color_control.hue_saturation.current_hue = _colorHSV.h; + light_config.color_control.hue_saturation.current_saturation = _colorHSV.s; + colorHSV = {_colorHSV.h, _colorHSV.s, _colorHSV.v}; + + // endpoint handles can be used to add/modify clusters. + endpoint_t *endpoint = rgb_color_light::create(node::get(), &light_config, ENDPOINT_FLAG_NONE, (void *)this); + if (endpoint == nullptr) { + log_e("Failed to create RGB Color light endpoint"); + return false; + } + + setEndPointId(endpoint::get_id(endpoint)); + log_i("RGB Color 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; +} + +void MatterColorLight::end() { + started = false; +} + +bool MatterColorLight::setOnOff(bool newState) { + if (!started) { + log_e("Matter RGB Color 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 MatterColorLight::updateAccessory() { + if (_onChangeCB != NULL) { + _onChangeCB(onOffState, colorHSV); + } +} + +bool MatterColorLight::getOnOff() { + return onOffState; +} + +bool MatterColorLight::toggle() { + return setOnOff(!onOffState); +} + +bool MatterColorLight::setColorRGB(espRgbColor_t _rgbColor) { + return setColorHSV(espRgbColorToHsvColor(_rgbColor)); +} + +espRgbColor_t MatterColorLight::getColorRGB() { + return espHsvColorToRgbColor(colorHSV); +} + +bool MatterColorLight::setColorHSV(espHsvColor_t _hsvColor) { + + if (!started) { + log_w("Matter RGB Color Light device has not begun."); + return false; + } + + // avoid processing the a "no-change" + if (colorHSV.h == _hsvColor.h && colorHSV.s == _hsvColor.s && colorHSV.v == _hsvColor.v) { + return true; + } + + colorHSV = {_hsvColor.h, _hsvColor.s, _hsvColor.v}; + + endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); + cluster_t *cluster = cluster::get(endpoint, ColorControl::Id); + // update hue + attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::CurrentHue::Id); + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + attribute::get_val(attribute, &val); + if (val.val.u8 != colorHSV.h) { + val.val.u8 = colorHSV.h; + attribute::update(endpoint_id, ColorControl::Id, ColorControl::Attributes::CurrentHue::Id, &val); + } + // update saturation + attribute = attribute::get(cluster, ColorControl::Attributes::CurrentSaturation::Id); + val = esp_matter_invalid(NULL); + attribute::get_val(attribute, &val); + if (val.val.u8 != colorHSV.s) { + val.val.u8 = colorHSV.s; + attribute::update(endpoint_id, ColorControl::Id, ColorControl::Attributes::CurrentSaturation::Id, &val); + } + // update value (brightness) + cluster = cluster::get(endpoint, LevelControl::Id); + attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id); + val = esp_matter_invalid(NULL); + attribute::get_val(attribute, &val); + if (val.val.u8 != colorHSV.v) { + val.val.u8 = colorHSV.v; + attribute::update(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, &val); + } + return true; +} + +espHsvColor_t MatterColorLight::getColorHSV() { + return colorHSV; +} + +MatterColorLight::operator bool() { + return getOnOff(); +} + +void MatterColorLight::operator=(bool newState) { + setOnOff(newState); +} +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterColorLight.h b/libraries/Matter/src/MatterEndpoints/MatterColorLight.h new file mode 100644 index 00000000000..13ff0decbc2 --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterColorLight.h @@ -0,0 +1,75 @@ +// 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 MatterColorLight : public MatterEndPoint { +public: + MatterColorLight(); + ~MatterColorLight(); + // default initial state is off, color is red 12% intensity HSV(0, 254, 31) + virtual bool begin(bool initialState = false, espHsvColor_t colorHSV = {0, 254, 31}); + // 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 setColorRGB(espRgbColor_t rgbColor); // returns true if successful + espRgbColor_t getColorRGB(); // returns current RGB Color + bool setColorHSV(espHsvColor_t hsvColor); // returns true if successful + espHsvColor_t getColorHSV(); // returns current HSV Color + + // 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 HSV Color value is changed by the Matter Controller + using EndPointRGBColorCB = std::function; + void onChangeColorHSV(EndPointRGBColorCB onChangeCB) { + _onChangeColorCB = 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) + espHsvColor_t colorHSV = {0}; // default initial color HSV is black, but it can be changed by begin(bool, espHsvColor_t) + EndPointOnOffCB _onChangeOnOffCB = NULL; + EndPointRGBColorCB _onChangeColorCB = NULL; + EndPointCB _onChangeCB = NULL; +}; +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h b/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h index a37f362f475..723849e354a 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h +++ b/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.h @@ -24,12 +24,6 @@ class MatterColorTemperatureLight : public MatterEndPoint { 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(); diff --git a/libraries/Matter/src/MatterUtil/ColorFormat.cpp b/libraries/Matter/src/MatterUtil/ColorFormat.cpp deleted file mode 100644 index 41d845dcdb9..00000000000 --- a/libraries/Matter/src/MatterUtil/ColorFormat.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* - * - * 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 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 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; - - float x, y, z; - float X, Y, Z; - float r, g, b; - - x = ((float)current_X) / 65535.0f; - y = ((float)current_Y) / 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 deleted file mode 100644 index 254a51c7144..00000000000 --- a/libraries/Matter/src/MatterUtil/ColorFormat.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * 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 current_X, uint16_t current_Y); -RgbColor_t HsvToRgb(HsvColor_t hsv); -RgbColor_t CTToRgb(CtColor_t ct); -HsvColor_t RgbToHsv(RgbColor_t rgb);