feat(matter): New Matter Endpoint (#10628)

* feat(matter): add new endpoint - color temperature light

---------

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
This commit is contained in:
Rodrigo Garcia 2024-11-20 17:02:34 -03:00 committed by GitHub
parent ff613b3f29
commit 6bc3ce68fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 829 additions and 5 deletions

View file

@ -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/MatterColorTemperatureLight.cpp
libraries/Matter/src/MatterUtil/ColorFormat.cpp
libraries/Matter/src/Matter.cpp)
set(ARDUINO_LIBRARY_PPP_SRCS

View file

@ -23,8 +23,8 @@ MatterDimmableLight DimmableLight;
// it will keep last OnOff & Brightness state stored, using Preferences
Preferences matterPref;
const char *onOffPrefKey = "OnOffState";
const char *brightnessPrefKey = "BrightnessState";
const char *onOffPrefKey = "OnOff";
const char *brightnessPrefKey = "Brightness";
// set your board RGB LED pin here
#ifdef RGB_BUILTIN

View file

@ -23,7 +23,7 @@ MatterOnOffLight OnOffLight;
// it will keep last OnOff state stored, using Preferences
Preferences matterPref;
const char *onOffPrefKey = "OnOffState";
const char *onOffPrefKey = "OnOff";
// set your board LED pin here
#ifdef LED_BUILTIN

View file

@ -0,0 +1,196 @@
// 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 <Matter.h>
#include <WiFi.h>
#include <Preferences.h>
// 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();
}
}
}

View file

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=huge_app",
"requires": [
"CONFIG_SOC_WIFI_SUPPORTED=y",
"CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
]
}

View file

@ -10,7 +10,12 @@ Matter KEYWORD1
ArduinoMatter KEYWORD1
MatterOnOffLight KEYWORD1
MatterDimmableLight KEYWORD1
MatterColorTemperatureLight KEYWORD1
MatterEndPoint KEYWORD1
CtColor_t KEYWORD1
XyColor_t KEYWORD1
HsvColor_t KEYWORD1
RgbColor_t KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
@ -30,12 +35,28 @@ setOnOff KEYWORD2
getOnOff KEYWORD2
setBrightness KEYWORD2
getBrightness KEYWORD2
setColorTemperature KEYWORD2
getColorTemperature KEYWORD2
toggle KEYWORD2
updateAccessory KEYWORD2
onChange KEYWORD2
onChangeOnOff KEYWORD2
onChangeBrightness KEYWORD2
onChangeColorTemperature KEYWORD2
XYToRgb KEYWORD2
HsvToRgb KEYWORD2
CTToRgb KEYWORD2
RgbToHsv 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

View file

@ -18,8 +18,10 @@
#include <Arduino.h>
#include <esp_matter.h>
#include <MatterUtil/ColorFormat.h>
#include <MatterEndpoints/MatterOnOffLight.h>
#include <MatterEndpoints/MatterDimmableLight.h>
#include <MatterEndpoints/MatterColorTemperatureLight.h>
using namespace esp_matter;
@ -47,6 +49,7 @@ public:
// list of Matter EndPoints Friend Classes
friend class MatterOnOffLight;
friend class MatterDimmableLight;
friend class MatterColorTemperatureLight;
protected:
static void _init();

View file

@ -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 <sdkconfig.h>
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
#include <Matter.h>
#include <app/server/Server.h>
#include <MatterEndpoints/MatterColorTemperatureLight.h>
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 */

View file

@ -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 <sdkconfig.h>
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
#include <Matter.h>
#include <MatterEndPoint.h>
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<bool(bool)>;
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<bool(uint8_t)>;
void onChangeBrightness(EndPointBrightnessCB onChangeCB) {
_onChangeBrightnessCB = onChangeCB;
}
// User Callbqck for whenever the Light temperature value is changed by the Matter Controller
using EndPointTemperatureCB = std::function<bool(uint16_t)>;
void onChangeColorTemperature(EndPointTemperatureCB onChangeCB) {
_onChangeTemperatureCB = onChangeCB;
}
// User Callback for whenever any parameter is changed by the Matter Controller
using EndPointCB = std::function<bool(bool, uint8_t, uint16_t)>;
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 */

View file

@ -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;
}

View file

@ -25,8 +25,8 @@ public:
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();

View file

@ -0,0 +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 <math.h>
// 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;
}

View file

@ -0,0 +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 <stdint.h>
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);