diff --git a/examples/lighting-app/lighting-common/include/ColorFormat.h b/examples/lighting-app/lighting-common/include/ColorFormat.h index 541155e6bf2f3c..1ad0cb0e75f4f4 100755 --- a/examples/lighting-app/lighting-common/include/ColorFormat.h +++ b/examples/lighting-app/lighting-common/include/ColorFormat.h @@ -40,5 +40,11 @@ struct XyColor_t 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); diff --git a/examples/lighting-app/lighting-common/src/ColorFormat.cpp b/examples/lighting-app/lighting-common/src/ColorFormat.cpp index dd76eebb782768..0b71b35d4a1ccb 100644 --- a/examples/lighting-app/lighting-common/src/ColorFormat.cpp +++ b/examples/lighting-app/lighting-common/src/ColorFormat.cpp @@ -130,3 +130,56 @@ RgbColor_t XYToRgb(uint8_t Level, uint16_t currentX, uint16_t currentY) return rgb; } + +RgbColor_t CTToRgb(CtColor_t ct) +{ + RgbColor_t rgb; + float r, g, b; + + // 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/examples/lighting-app/qpg/include/LightingManager.h b/examples/lighting-app/qpg/include/LightingManager.h index 309a2f5766c3f7..b1cb0ab9ad1a18 100644 --- a/examples/lighting-app/qpg/include/LightingManager.h +++ b/examples/lighting-app/qpg/include/LightingManager.h @@ -41,6 +41,7 @@ class LightingManager LEVEL_ACTION, COLOR_ACTION_XY, COLOR_ACTION_HSV, + COLOR_ACTION_CT, INVALID_ACTION } Action; @@ -66,6 +67,7 @@ class LightingManager XyColor_t mXY; HsvColor_t mHSV; RgbColor_t mRGB; + CtColor_t mCT; LightingCallback_fn mActionInitiated_CB; LightingCallback_fn mActionCompleted_CB; @@ -74,6 +76,7 @@ class LightingManager void SetLevel(uint8_t aLevel); void SetColor(uint16_t x, uint16_t y); void SetColor(uint8_t hue, uint8_t saturation); + void SetColorTemperature(CtColor_t ct); void UpdateLight(); diff --git a/examples/lighting-app/qpg/src/LightingManager.cpp b/examples/lighting-app/qpg/src/LightingManager.cpp index 3f53f9a02f2644..9f00b25db45927 100644 --- a/examples/lighting-app/qpg/src/LightingManager.cpp +++ b/examples/lighting-app/qpg/src/LightingManager.cpp @@ -66,6 +66,7 @@ bool LightingManager::InitiateAction(Action_t aAction, int32_t aActor, uint16_t State_t new_state; XyColor_t xy; HsvColor_t hsv; + CtColor_t ct; switch (aAction) { @@ -86,6 +87,10 @@ bool LightingManager::InitiateAction(Action_t aAction, int32_t aActor, uint16_t hsv = *reinterpret_cast(value); ChipLogProgress(NotSpecified, "LightMgr:COLOR: hsv:%u|%u->%u|%u", mHSV.h, mHSV.s, hsv.h, hsv.s); break; + case COLOR_ACTION_CT: + ct.ctMireds = *reinterpret_cast(value); + ChipLogProgress(NotSpecified, "LightMgr:COLOR: ct:%u->%u", mCT.ctMireds, ct.ctMireds); + break; default: ChipLogProgress(NotSpecified, "LightMgr:Unknown"); break; @@ -157,6 +162,10 @@ bool LightingManager::InitiateAction(Action_t aAction, int32_t aActor, uint16_t { SetColor(hsv.h, hsv.s); } + else if (aAction == COLOR_ACTION_CT) + { + SetColorTemperature(ct); + } else { Set(new_state == kState_On); @@ -195,6 +204,13 @@ void LightingManager::SetColor(uint8_t hue, uint8_t saturation) UpdateLight(); } +void LightingManager::SetColorTemperature(CtColor_t ct) +{ + mCT = ct; + mRGB = CTToRgb(ct); + UpdateLight(); +} + void LightingManager::Set(bool aOn) { if (aOn) diff --git a/examples/lighting-app/qpg/src/ZclCallbacks.cpp b/examples/lighting-app/qpg/src/ZclCallbacks.cpp index 0d365320c7cf12..9faedea78dcec3 100644 --- a/examples/lighting-app/qpg/src/ZclCallbacks.cpp +++ b/examples/lighting-app/qpg/src/ZclCallbacks.cpp @@ -65,16 +65,14 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & return; } - if ((attributeId != ColorControl::Attributes::CurrentX::Id) && (attributeId != ColorControl::Attributes::CurrentY::Id) && - (attributeId != ColorControl::Attributes::CurrentHue::Id) && - (attributeId != ColorControl::Attributes::CurrentSaturation::Id)) - { - ChipLogProgress(Zcl, "Unknown attribute ID: " ChipLogFormatMEI, ChipLogValueMEI(attributeId)); - return; - } - - if (size == sizeof(uint16_t)) + /* XY color space */ + if (attributeId == ColorControl::Attributes::CurrentX::Id || attributeId == ColorControl::Attributes::CurrentY::Id) { + if (size != sizeof(uint16_t)) + { + ChipLogError(Zcl, "Wrong length for ColorControl value: %d", size); + return; + } XyColor_t xy; if (attributeId == ColorControl::Attributes::CurrentX::Id) { @@ -90,20 +88,37 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & EmberAfStatus status = ColorControl::Attributes::CurrentX::Get(endpoint, &xy.x); assert(status == EMBER_ZCL_STATUS_SUCCESS); } + ChipLogProgress(Zcl, "New XY color: %u|%u", xy.x, xy.y); LightingMgr().InitiateAction(LightingManager::COLOR_ACTION_XY, 0, sizeof(xy), (uint8_t *) &xy); } - else if (size == sizeof(uint8_t)) + /* HSV color space */ + else if (attributeId == ColorControl::Attributes::CurrentHue::Id || + attributeId == ColorControl::Attributes::CurrentSaturation::Id || + attributeId == ColorControl::Attributes::EnhancedCurrentHue::Id) { + if (size != sizeof(uint8_t)) + { + ChipLogError(Zcl, "Wrong length for ColorControl value: %d", size); + return; + } HsvColor_t hsv; - if (attributeId == ColorControl::Attributes::CurrentHue::Id) + if (attributeId == ColorControl::Attributes::EnhancedCurrentHue::Id) + { + // We only support 8-bit hue. Assuming hue is linear, normalize 16-bit to 8-bit. + hsv.h = (uint8_t)((*reinterpret_cast(value)) >> 8); + // get saturation from cluster value storage + EmberAfStatus status = ColorControl::Attributes::CurrentSaturation::Get(endpoint, &hsv.s); + assert(status == EMBER_ZCL_STATUS_SUCCESS); + } + else if (attributeId == ColorControl::Attributes::CurrentHue::Id) { hsv.h = *value; // get saturation from cluster value storage EmberAfStatus status = ColorControl::Attributes::CurrentSaturation::Get(endpoint, &hsv.s); assert(status == EMBER_ZCL_STATUS_SUCCESS); } - if (attributeId == ColorControl::Attributes::CurrentSaturation::Id) + else if (attributeId == ColorControl::Attributes::CurrentSaturation::Id) { hsv.s = *value; // get hue from cluster value storage @@ -113,16 +128,19 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & ChipLogProgress(Zcl, "New HSV color: %u|%u", hsv.h, hsv.s); LightingMgr().InitiateAction(LightingManager::COLOR_ACTION_HSV, 0, sizeof(hsv), (uint8_t *) &hsv); } + else if (attributeId == ColorControl::Attributes::ColorTemperatureMireds::Id) + { + CtColor_t ct; + ct.ctMireds = *reinterpret_cast(value); + ChipLogProgress(Zcl, "New CT color: %u", ct.ctMireds); + LightingMgr().InitiateAction(LightingManager::COLOR_ACTION_CT, 0, sizeof(ct), (uint8_t *) &ct.ctMireds); + } else { - ChipLogError(Zcl, "Wrong length for ColorControl value: %d", size); + ChipLogProgress(Zcl, "Unknown attribute ID: " ChipLogFormatMEI, ChipLogValueMEI(attributeId)); + return; } } - else - { - ChipLogProgress(Zcl, "Unknown cluster ID: " ChipLogFormatMEI, ChipLogValueMEI(clusterId)); - return; - } } /** @brief OnOff Cluster Init