diff --git a/CHANGELOG.md b/CHANGELOG.md index 649f15289..5bb01ac2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin (#8630) - Command ``CTRange`` to specify the visible CT range the bulb is capable of (#10311) - Command ``VirtualCT`` to simulate or fine tune CT bulbs with 3,4,5 channels (#10311) +- Disable `USE_LIGHT`` light support for ZBBridge (saves 17.6kb) ### Breaking Changed - Replaced MFRC522 13.56MHz rfid card reader GPIO selection from ``SPI CS`` by ``RC522 CS`` diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index 142045bab..87022257c 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -509,7 +509,7 @@ #undef USE_SONOFF_D1 // Disable support for Sonoff D1 Dimmer (+0k7 code) // -- Optional light modules ---------------------- -//#undef USE_LIGHT // Enable Dimmer/Light support +#undef USE_LIGHT // Disable Dimmer/Light support #undef USE_LIGHT_VIRTUAL_CT // Disable support for Virtual White Color Temperature (SO106) #undef USE_WS2812 // Disable WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 1fe064b08..3d57bdc95 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -947,7 +947,7 @@ void HandleRoot(void) char scolor[8]; snprintf_P(scolor, sizeof(scolor), PSTR("#%02X%02X%02X"), dcolor, dcolor, dcolor); // Saturation start color from Black to White uint8_t red, green, blue; - LightHsToRgb(hue, 255, &red, &green, &blue); + HsToRgb(hue, 255, &red, &green, &blue); snprintf_P(stemp, sizeof(stemp), PSTR("#%02X%02X%02X"), red, green, blue); // Saturation end color WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Saturation diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 5443a8a55..90d94ffb7 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -196,83 +196,6 @@ const vct_pivot_t CT_PIVOTS_RGB PROGMEM = { 255, 255, 255, 0, 0 }; const vct_pivot_t CT_PIVOTS_CWW PROGMEM = { 0, 0, 0, 255, 0 }; const vct_pivot_t CT_PIVOTS_WWW PROGMEM = { 0, 0, 0, 0, 255 }; -// New version of Gamma correction compute -// Instead of a table, we do a multi-linear approximation, which is close enough -// At low levels, the slope is a bit higher than actual gamma, to make changes smoother -// Internal resolution is 10 bits. - -typedef struct gamma_table_t { - uint16_t to_src; - uint16_t to_gamma; -} gamma_table_t; - -const gamma_table_t gamma_table[] = { // don't put in PROGMEM for performance reasons - { 1, 1 }, - { 4, 1 }, - { 209, 13 }, - { 312, 41 }, - { 457, 106 }, - { 626, 261 }, - { 762, 450 }, - { 895, 703 }, - { 1023, 1023 }, - { 0xFFFF, 0xFFFF } // fail-safe if out of range -}; - -// simplified Gamma table for Fade, cheating a little at low brightness -const gamma_table_t gamma_table_fast[] = { - { 384, 192 }, - { 768, 576 }, - { 1023, 1023 }, - { 0xFFFF, 0xFFFF } // fail-safe if out of range -}; - -// For reference, below are the computed gamma tables, via ledGamma() -// for 8 bits output: -// 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -// 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, -// 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, -// 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, -// 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, -// 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, 18, -// 18, 19, 19, 20, 20, 21, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, -// 25, 26, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 36, 37, 38, -// 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, 51, 52, 53, -// 54, 55, 56, 57, 58, 59, 60, 61, 61, 62, 63, 64, 65, 67, 68, 69, -// 71, 72, 73, 75, 76, 78, 79, 80, 82, 83, 85, 86, 87, 89, 90, 91, -// 93, 94, 95, 97, 98,100,101,102,104,105,107,108,109,111,112,114, -// 116,118,120,122,124,125,127,129,131,133,135,137,139,141,143,144, -// 146,148,150,152,154,156,158,160,162,164,166,168,170,171,173,175, -// 178,180,183,185,188,190,193,195,198,200,203,205,208,210,213,215, -// 218,220,223,225,228,230,233,235,238,240,243,245,248,250,253,255 -// -// and for 10 bits output: -// 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, -// 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, -// 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, -// 12, 12, 13, 13, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, -// 26, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, -// 45, 47, 49, 50, 52, 54, 56, 58, 59, 61, 63, 65, 67, 68, 70, 72, -// 74, 76, 77, 79, 81, 83, 84, 86, 88, 90, 92, 93, 95, 97, 99, 101, -// 102, 104, 106, 110, 113, 117, 121, 124, 128, 132, 135, 139, 143, 146, 150, 154, -// 158, 162, 166, 169, 173, 177, 180, 184, 188, 191, 195, 199, 202, 206, 210, 213, -// 217, 221, 224, 228, 232, 235, 239, 243, 246, 250, 254, 257, 261, 267, 272, 278, -// 283, 289, 294, 300, 305, 311, 317, 322, 328, 333, 339, 344, 350, 356, 361, 367, -// 372, 378, 383, 389, 394, 400, 406, 411, 417, 422, 428, 433, 439, 444, 450, 458, -// 465, 473, 480, 488, 496, 503, 511, 518, 526, 534, 541, 549, 557, 564, 572, 579, -// 587, 595, 602, 610, 617, 627, 635, 642, 650, 657, 665, 673, 680, 688, 695, 703, -// 713, 723, 733, 743, 753, 763, 773, 783, 793, 803, 813, 823, 833, 843, 853, 863, -// 873, 883, 893, 903, 913, 923, 933, 943, 953, 963, 973, 983, 993,1003,1013,1023 -// -// Output for Dimmer 0..100 values -// 0, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, -// 10, 10, 11, 12, 12, 13, 15, 17, 21, 23, 26, 28, 31, 34, 37, -// 40, 43, 49, 52, 58, 61, 67, 70, 76, 79, 84, 90, 93, 99,102, -// 110,117,128,135,146,158,166,177,184,195,202,213,221,232,239, -// 250,261,272,289,300,317,328,344,356,372,389,400,417,428,444, -// 458,480,496,518,534,557,579,595,617,635,657,673,695,713,743, -// 773,793,823,843,873,893,923,943,973,993,1023 - struct LIGHT { uint32_t strip_timer_counter = 0; // Bars and Gradient power_t power = 0; // Power for each channel if SetOption68, or boolean if single light @@ -728,13 +651,6 @@ class LightStateClass { _r, _g, _b, _wc, _ww); #endif } - - // new version of RGB to HSB with only integer calculation - static void RgbToHsb(uint8_t r, uint8_t g, uint8_t b, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri); - static void HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b); - static void RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y); - static void XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb); - }; @@ -742,167 +658,6 @@ class LightStateClass { * LightStateClass implementation \*********************************************************************************************/ -// new version with only integer computing -// brightness is not needed, it is controlled via Dimmer -void LightStateClass::RgbToHsb(uint8_t ir, uint8_t ig, uint8_t ib, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri) { - uint32_t r = ir; - uint32_t g = ig; - uint32_t b = ib; - uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; // 0..255 - uint32_t min = (r < g && r < b) ? r : (g < b) ? g : b; // 0..255 - uint32_t d = max - min; // 0..255 - - uint16_t hue = 0; // hue value in degrees ranges from 0 to 359 - uint8_t sat = 0; // 0..255 - uint8_t bri = max; // 0..255 - - if (d != 0) { - sat = changeUIntScale(d, 0, max, 0, 255); - if (r == max) { - hue = (g > b) ? changeUIntScale(g-b,0,d,0,60) : 360 - changeUIntScale(b-g,0,d,0,60); - } else if (g == max) { - hue = (b > r) ? 120 + changeUIntScale(b-r,0,d,0,60) : 120 - changeUIntScale(r-b,0,d,0,60); - } else { - hue = (r > g) ? 240 + changeUIntScale(r-g,0,d,0,60) : 240 - changeUIntScale(g-r,0,d,0,60); - } - hue = hue % 360; // 0..359 - } - - if (r_hue) *r_hue = hue; - if (r_sat) *r_sat = sat; - if (r_bri) *r_bri = bri; - //AddLog_P(LOG_LEVEL_DEBUG_MORE, "RgbToHsb rgb (%d %d %d) hsb (%d %d %d)", r, g, b, hue, sat, bri); -} - -void LightStateClass::HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { - uint32_t r = 255; // default to white - uint32_t g = 255; - uint32_t b = 255; - // we take brightness at 100%, brightness should be set separately - hue = hue % 360; // normalize to 0..359 - - if (sat > 0) { - uint32_t i = hue / 60; // quadrant 0..5 - uint32_t f = hue % 60; // 0..59 - uint32_t q = 255 - changeUIntScale(f, 0, 60, 0, sat); // 0..59 - uint32_t p = 255 - sat; - uint32_t t = 255 - changeUIntScale(60 - f, 0, 60, 0, sat); - - switch (i) { - case 0: - //r = 255; - g = t; - b = p; - break; - case 1: - r = q; - //g = 255; - b = p; - break; - case 2: - r = p; - //g = 255; - b = t; - break; - case 3: - r = p; - g = q; - //b = 255; - break; - case 4: - r = t; - g = p; - //b = 255; - break; - default: - //r = 255; - g = p; - b = q; - break; - } - } - if (r_r) *r_r = r; - if (r_g) *r_g = g; - if (r_b) *r_b = b; -} - -#define POW FastPrecisePowf - -// -// Matrix 3x3 multiplied to a 3 vector, result in a 3 vector -// -void mat3x3(const float *mat33, const float *vec3, float *res3) { - for (uint32_t i = 0; i < 3; i++) { - const float * v = vec3; - *res3 = 0.0f; - for (uint32_t j = 0; j < 3; j++) { - *res3 += *mat33++ * *v++; - } - res3++; - } -} - -void LightStateClass::RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y) { - float x = 0.31271f; // default medium white - float y = 0.32902f; - - if (i_r + i_b + i_g > 0) { - float rgb[3] = { (float)i_r, (float)i_g, (float)i_b }; - // https://gist.github.com/popcorn245/30afa0f98eea1c2fd34d - // Gamma correction - for (uint32_t i = 0; i < 3; i++) { - rgb[i] = rgb[i] / 255.0f; - rgb[i] = (rgb[i] > 0.04045f) ? POW((rgb[i] + 0.055f) / (1.0f + 0.055f), 2.4f) : (rgb[i] / 12.92f); - } - - // conversion to X, Y, Z - // Y is also the Luminance - float XYZ[3]; - static const float XYZ_factors[] = { 0.649926f, 0.103455f, 0.197109f, - 0.234327f, 0.743075f, 0.022598f, - 0.000000f, 0.053077f, 1.035763f }; - mat3x3(XYZ_factors, rgb, XYZ); - - float XYZ_sum = XYZ[0] + XYZ[1] + XYZ[2]; - x = XYZ[0] / XYZ_sum; - y = XYZ[1] / XYZ_sum; - // we keep the raw gamut, one nice thing could be to convert to a narrower gamut - } - if (r_x) *r_x = x; - if (r_y) *r_y = y; -} - -void LightStateClass::XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb) -{ - float XYZ[3], rgb[3]; - x = (x > 0.99f ? 0.99f : (x < 0.01f ? 0.01f : x)); - y = (y > 0.99f ? 0.99f : (y < 0.01f ? 0.01f : y)); - float z = 1.0f - x - y; - XYZ[0] = x / y; - XYZ[1] = 1.0f; - XYZ[2] = z / y; - - static const float rgb_factors[] = { 3.2406f, -1.5372f, -0.4986f, - -0.9689f, 1.8758f, 0.0415f, - 0.0557f, -0.2040f, 1.0570f }; - mat3x3(rgb_factors, XYZ, rgb); - float max = (rgb[0] > rgb[1] && rgb[0] > rgb[2]) ? rgb[0] : (rgb[1] > rgb[2]) ? rgb[1] : rgb[2]; - - for (uint32_t i = 0; i < 3; i++) { - rgb[i] = rgb[i] / max; // normalize to max == 1.0 - rgb[i] = (rgb[i] <= 0.0031308f) ? 12.92f * rgb[i] : 1.055f * POW(rgb[i], (1.0f / 2.4f)) - 0.055f; // gamma - } - - int32_t irgb[3]; - for (uint32_t i = 0; i < 3; i++) { - irgb[i] = rgb[i] * 255.0f + 0.5f; - } - - if (rr) { *rr = (irgb[0] > 255 ? 255: (irgb[0] < 0 ? 0 : irgb[0])); } - if (rg) { *rg = (irgb[1] > 255 ? 255: (irgb[1] < 0 ? 0 : irgb[1])); } - if (rb) { *rb = (irgb[2] > 255 ? 255: (irgb[2] < 0 ? 0 : irgb[2])); } -} - class LightControllerClass { private: LightStateClass *_state; @@ -1167,18 +922,6 @@ public: LightStateClass light_state = LightStateClass(); LightControllerClass light_controller = LightControllerClass(light_state); -/*********************************************************************************************\ - * Change scales from 8 bits to 10 bits and vice versa -\*********************************************************************************************/ -// 8 to 10 to 8 is guaranteed to give the same result -uint16_t change8to10(uint8_t v) { - return changeUIntScale(v, 0, 255, 0, 1023); -} -// change from 10 bits to 8 bits, but any non-zero input will be non-zero -uint8_t change10to8(uint16_t v) { - return (0 == v) ? 0 : changeUIntScale(v, 4, 1023, 1, 255); -} - /*********************************************************************************************\ * CT (White Color Temperature) \*********************************************************************************************/ @@ -1241,54 +984,6 @@ void setAlexaCTRange(void) { // depending on SetOption82, full or limited CT } } -/*********************************************************************************************\ - * Gamma correction -\*********************************************************************************************/ -// Calculate the gamma corrected value for LEDS -uint16_t ledGamma_internal(uint16_t v, const struct gamma_table_t *gt_ptr) { - uint16_t from_src = 0; - uint16_t from_gamma = 0; - - for (const gamma_table_t *gt = gt_ptr; ; gt++) { - uint16_t to_src = gt->to_src; - uint16_t to_gamma = gt->to_gamma; - if (v <= to_src) { - return changeUIntScale(v, from_src, to_src, from_gamma, to_gamma); - } - from_src = to_src; - from_gamma = to_gamma; - } -} -// Calculate the reverse gamma value for LEDS -uint16_t ledGammaReverse_internal(uint16_t vg, const struct gamma_table_t *gt_ptr) { - uint16_t from_src = 0; - uint16_t from_gamma = 0; - - for (const gamma_table_t *gt = gt_ptr; ; gt++) { - uint16_t to_src = gt->to_src; - uint16_t to_gamma = gt->to_gamma; - if (vg <= to_gamma) { - return changeUIntScale(vg, from_gamma, to_gamma, from_src, to_src); - } - from_src = to_src; - from_gamma = to_gamma; - } -} - -// 10 bits in, 10 bits out -uint16_t ledGamma10_10(uint16_t v) { - return ledGamma_internal(v, gamma_table); -} -// 10 bits resolution, 8 bits in -uint16_t ledGamma10(uint8_t v) { - return ledGamma10_10(change8to10(v)); -} - -// Legacy function -uint8_t ledGamma(uint8_t v) { - return change10to8(ledGamma10(v)); -} - /********************************************************************************************/ void LightPwmOffset(uint32_t offset) @@ -1513,10 +1208,6 @@ void LightGetXY(float *X, float *Y) { light_state.getXY(X, Y); } -void LightHsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { - light_state.HsToRgb(hue, sat, r_r, r_g, r_b); -} - // If SetOption68 is set, get the brightness for a specific device uint8_t LightGetBri(uint8_t device) { uint8_t bri = 254; // default value if relay @@ -2120,14 +1811,14 @@ bool isChannelCT(uint32_t channel) { // Calculate the Gamma correction, if any, for fading, using the fast Gamma curve (10 bits in+out) uint16_t fadeGamma(uint32_t channel, uint16_t v) { if (isChannelGammaCorrected(channel)) { - return ledGamma_internal(v, gamma_table_fast); + return ledGammaFast(v); } else { return v; } } uint16_t fadeGammaReverse(uint32_t channel, uint16_t vg) { if (isChannelGammaCorrected(channel)) { - return ledGammaReverse_internal(vg, gamma_table_fast); + return leddGammaReverseFast(vg); } else { return vg; } diff --git a/tasmota/xdrv_04_light_utils.ino b/tasmota/xdrv_04_light_utils.ino new file mode 100644 index 000000000..8ab379d62 --- /dev/null +++ b/tasmota/xdrv_04_light_utils.ino @@ -0,0 +1,335 @@ +/* + xdrv_04_light_utils.ino - Converter functions for lights + + Copyright (C) 2020 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// This file is compiled even if `USE_LIGHT` is not defined and provides +// general purpose converter fucntions + + +// New version of Gamma correction compute +// Instead of a table, we do a multi-linear approximation, which is close enough +// At low levels, the slope is a bit higher than actual gamma, to make changes smoother +// Internal resolution is 10 bits. + +typedef struct gamma_table_t { + uint16_t to_src; + uint16_t to_gamma; +} gamma_table_t; + +const gamma_table_t gamma_table[] = { // don't put in PROGMEM for performance reasons + { 1, 1 }, + { 4, 1 }, + { 209, 13 }, + { 312, 41 }, + { 457, 106 }, + { 626, 261 }, + { 762, 450 }, + { 895, 703 }, + { 1023, 1023 }, + { 0xFFFF, 0xFFFF } // fail-safe if out of range +}; + +// simplified Gamma table for Fade, cheating a little at low brightness +const gamma_table_t gamma_table_fast[] = { + { 384, 192 }, + { 768, 576 }, + { 1023, 1023 }, + { 0xFFFF, 0xFFFF } // fail-safe if out of range +}; + +// For reference, below are the computed gamma tables, via ledGamma() +// for 8 bits output: +// 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, +// 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, +// 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, +// 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, +// 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, 18, +// 18, 19, 19, 20, 20, 21, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, +// 25, 26, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 36, 37, 38, +// 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, 51, 52, 53, +// 54, 55, 56, 57, 58, 59, 60, 61, 61, 62, 63, 64, 65, 67, 68, 69, +// 71, 72, 73, 75, 76, 78, 79, 80, 82, 83, 85, 86, 87, 89, 90, 91, +// 93, 94, 95, 97, 98,100,101,102,104,105,107,108,109,111,112,114, +// 116,118,120,122,124,125,127,129,131,133,135,137,139,141,143,144, +// 146,148,150,152,154,156,158,160,162,164,166,168,170,171,173,175, +// 178,180,183,185,188,190,193,195,198,200,203,205,208,210,213,215, +// 218,220,223,225,228,230,233,235,238,240,243,245,248,250,253,255 +// +// and for 10 bits output: +// 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, +// 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, +// 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, +// 12, 12, 13, 13, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, +// 26, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, +// 45, 47, 49, 50, 52, 54, 56, 58, 59, 61, 63, 65, 67, 68, 70, 72, +// 74, 76, 77, 79, 81, 83, 84, 86, 88, 90, 92, 93, 95, 97, 99, 101, +// 102, 104, 106, 110, 113, 117, 121, 124, 128, 132, 135, 139, 143, 146, 150, 154, +// 158, 162, 166, 169, 173, 177, 180, 184, 188, 191, 195, 199, 202, 206, 210, 213, +// 217, 221, 224, 228, 232, 235, 239, 243, 246, 250, 254, 257, 261, 267, 272, 278, +// 283, 289, 294, 300, 305, 311, 317, 322, 328, 333, 339, 344, 350, 356, 361, 367, +// 372, 378, 383, 389, 394, 400, 406, 411, 417, 422, 428, 433, 439, 444, 450, 458, +// 465, 473, 480, 488, 496, 503, 511, 518, 526, 534, 541, 549, 557, 564, 572, 579, +// 587, 595, 602, 610, 617, 627, 635, 642, 650, 657, 665, 673, 680, 688, 695, 703, +// 713, 723, 733, 743, 753, 763, 773, 783, 793, 803, 813, 823, 833, 843, 853, 863, +// 873, 883, 893, 903, 913, 923, 933, 943, 953, 963, 973, 983, 993,1003,1013,1023 +// +// Output for Dimmer 0..100 values +// 0, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, +// 10, 10, 11, 12, 12, 13, 15, 17, 21, 23, 26, 28, 31, 34, 37, +// 40, 43, 49, 52, 58, 61, 67, 70, 76, 79, 84, 90, 93, 99,102, +// 110,117,128,135,146,158,166,177,184,195,202,213,221,232,239, +// 250,261,272,289,300,317,328,344,356,372,389,400,417,428,444, +// 458,480,496,518,534,557,579,595,617,635,657,673,695,713,743, +// 773,793,823,843,873,893,923,943,973,993,1023 + + +/*********************************************************************************************\ + * Color converters +\*********************************************************************************************/ + +// new version with only integer computing +// brightness is not needed, it is controlled via Dimmer +void RgbToHsb(uint8_t ir, uint8_t ig, uint8_t ib, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri) { + uint32_t r = ir; + uint32_t g = ig; + uint32_t b = ib; + uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; // 0..255 + uint32_t min = (r < g && r < b) ? r : (g < b) ? g : b; // 0..255 + uint32_t d = max - min; // 0..255 + + uint16_t hue = 0; // hue value in degrees ranges from 0 to 359 + uint8_t sat = 0; // 0..255 + uint8_t bri = max; // 0..255 + + if (d != 0) { + sat = changeUIntScale(d, 0, max, 0, 255); + if (r == max) { + hue = (g > b) ? changeUIntScale(g-b,0,d,0,60) : 360 - changeUIntScale(b-g,0,d,0,60); + } else if (g == max) { + hue = (b > r) ? 120 + changeUIntScale(b-r,0,d,0,60) : 120 - changeUIntScale(r-b,0,d,0,60); + } else { + hue = (r > g) ? 240 + changeUIntScale(r-g,0,d,0,60) : 240 - changeUIntScale(g-r,0,d,0,60); + } + hue = hue % 360; // 0..359 + } + + if (r_hue) *r_hue = hue; + if (r_sat) *r_sat = sat; + if (r_bri) *r_bri = bri; + //AddLog_P(LOG_LEVEL_DEBUG_MORE, "RgbToHsb rgb (%d %d %d) hsb (%d %d %d)", r, g, b, hue, sat, bri); +} + +void HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { + uint32_t r = 255; // default to white + uint32_t g = 255; + uint32_t b = 255; + // we take brightness at 100%, brightness should be set separately + hue = hue % 360; // normalize to 0..359 + + if (sat > 0) { + uint32_t i = hue / 60; // quadrant 0..5 + uint32_t f = hue % 60; // 0..59 + uint32_t q = 255 - changeUIntScale(f, 0, 60, 0, sat); // 0..59 + uint32_t p = 255 - sat; + uint32_t t = 255 - changeUIntScale(60 - f, 0, 60, 0, sat); + + switch (i) { + case 0: + //r = 255; + g = t; + b = p; + break; + case 1: + r = q; + //g = 255; + b = p; + break; + case 2: + r = p; + //g = 255; + b = t; + break; + case 3: + r = p; + g = q; + //b = 255; + break; + case 4: + r = t; + g = p; + //b = 255; + break; + default: + //r = 255; + g = p; + b = q; + break; + } + } + if (r_r) *r_r = r; + if (r_g) *r_g = g; + if (r_b) *r_b = b; +} + +#define POW FastPrecisePowf + +// +// Matrix 3x3 multiplied to a 3 vector, result in a 3 vector +// +void mat3x3(const float *mat33, const float *vec3, float *res3) { + for (uint32_t i = 0; i < 3; i++) { + const float * v = vec3; + *res3 = 0.0f; + for (uint32_t j = 0; j < 3; j++) { + *res3 += *mat33++ * *v++; + } + res3++; + } +} + +void RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y) { + float x = 0.31271f; // default medium white + float y = 0.32902f; + + if (i_r + i_b + i_g > 0) { + float rgb[3] = { (float)i_r, (float)i_g, (float)i_b }; + // https://gist.github.com/popcorn245/30afa0f98eea1c2fd34d + // Gamma correction + for (uint32_t i = 0; i < 3; i++) { + rgb[i] = rgb[i] / 255.0f; + rgb[i] = (rgb[i] > 0.04045f) ? POW((rgb[i] + 0.055f) / (1.0f + 0.055f), 2.4f) : (rgb[i] / 12.92f); + } + + // conversion to X, Y, Z + // Y is also the Luminance + float XYZ[3]; + static const float XYZ_factors[] = { 0.649926f, 0.103455f, 0.197109f, + 0.234327f, 0.743075f, 0.022598f, + 0.000000f, 0.053077f, 1.035763f }; + mat3x3(XYZ_factors, rgb, XYZ); + + float XYZ_sum = XYZ[0] + XYZ[1] + XYZ[2]; + x = XYZ[0] / XYZ_sum; + y = XYZ[1] / XYZ_sum; + // we keep the raw gamut, one nice thing could be to convert to a narrower gamut + } + if (r_x) *r_x = x; + if (r_y) *r_y = y; +} + +void XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb) +{ + float XYZ[3], rgb[3]; + x = (x > 0.99f ? 0.99f : (x < 0.01f ? 0.01f : x)); + y = (y > 0.99f ? 0.99f : (y < 0.01f ? 0.01f : y)); + float z = 1.0f - x - y; + XYZ[0] = x / y; + XYZ[1] = 1.0f; + XYZ[2] = z / y; + + static const float rgb_factors[] = { 3.2406f, -1.5372f, -0.4986f, + -0.9689f, 1.8758f, 0.0415f, + 0.0557f, -0.2040f, 1.0570f }; + mat3x3(rgb_factors, XYZ, rgb); + float max = (rgb[0] > rgb[1] && rgb[0] > rgb[2]) ? rgb[0] : (rgb[1] > rgb[2]) ? rgb[1] : rgb[2]; + + for (uint32_t i = 0; i < 3; i++) { + rgb[i] = rgb[i] / max; // normalize to max == 1.0 + rgb[i] = (rgb[i] <= 0.0031308f) ? 12.92f * rgb[i] : 1.055f * POW(rgb[i], (1.0f / 2.4f)) - 0.055f; // gamma + } + + int32_t irgb[3]; + for (uint32_t i = 0; i < 3; i++) { + irgb[i] = rgb[i] * 255.0f + 0.5f; + } + + if (rr) { *rr = (irgb[0] > 255 ? 255: (irgb[0] < 0 ? 0 : irgb[0])); } + if (rg) { *rg = (irgb[1] > 255 ? 255: (irgb[1] < 0 ? 0 : irgb[1])); } + if (rb) { *rb = (irgb[2] > 255 ? 255: (irgb[2] < 0 ? 0 : irgb[2])); } +} + +/*********************************************************************************************\ + * Change scales from 8 bits to 10 bits and vice versa +\*********************************************************************************************/ +// 8 to 10 to 8 is guaranteed to give the same result +uint16_t change8to10(uint8_t v) { + return changeUIntScale(v, 0, 255, 0, 1023); +} +// change from 10 bits to 8 bits, but any non-zero input will be non-zero +uint8_t change10to8(uint16_t v) { + return (0 == v) ? 0 : changeUIntScale(v, 4, 1023, 1, 255); +} + +/*********************************************************************************************\ + * Gamma correction +\*********************************************************************************************/ +// Calculate the gamma corrected value for LEDS +uint16_t ledGamma_internal(uint16_t v, const struct gamma_table_t *gt_ptr) { + uint16_t from_src = 0; + uint16_t from_gamma = 0; + + for (const gamma_table_t *gt = gt_ptr; ; gt++) { + uint16_t to_src = gt->to_src; + uint16_t to_gamma = gt->to_gamma; + if (v <= to_src) { + return changeUIntScale(v, from_src, to_src, from_gamma, to_gamma); + } + from_src = to_src; + from_gamma = to_gamma; + } +} +// Calculate the reverse gamma value for LEDS +uint16_t ledGammaReverse_internal(uint16_t vg, const struct gamma_table_t *gt_ptr) { + uint16_t from_src = 0; + uint16_t from_gamma = 0; + + for (const gamma_table_t *gt = gt_ptr; ; gt++) { + uint16_t to_src = gt->to_src; + uint16_t to_gamma = gt->to_gamma; + if (vg <= to_gamma) { + return changeUIntScale(vg, from_gamma, to_gamma, from_src, to_src); + } + from_src = to_src; + from_gamma = to_gamma; + } +} + +// 10 bits in, 10 bits out +uint16_t ledGamma10_10(uint16_t v) { + return ledGamma_internal(v, gamma_table); +} + +// 10 bits resolution, 8 bits in +uint16_t ledGamma10(uint8_t v) { + return ledGamma10_10(change8to10(v)); +} + +// Legacy function +uint8_t ledGamma(uint8_t v) { + return change10to8(ledGamma10(v)); +} + +// Fast versions for Fading +uint16_t ledGammaFast(uint16_t v) { + return ledGamma_internal(v, gamma_table_fast); +} + +uint16_t leddGammaReverseFast(uint16_t vg) { + return ledGammaReverse_internal(vg, gamma_table_fast); +} \ No newline at end of file diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index bf4b8887a..44c4e2b21 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -5848,8 +5848,8 @@ void Script_Handle_Hue(String *path) { String x_str = tok_x.getStr(); String y_str = tok_y.getStr(); uint8_t rr,gg,bb; - LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); - LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); + XyToRgb(x, y, &rr, &gg, &bb); + RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); if (resp) { response += ","; } response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); response.replace("{id", String(device)); diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino index b0a18d16a..93252af12 100644 --- a/tasmota/xdrv_20_hue.ino +++ b/tasmota/xdrv_20_hue.ino @@ -17,7 +17,7 @@ along with this program. If not, see . */ -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && (defined(USE_ZIGBEE) || defined(USE_LIGHT)) /*********************************************************************************************\ * Philips Hue bridge emulation * @@ -303,6 +303,7 @@ uint16_t prev_ct = 254; char prev_x_str[24] = "\0"; // store previously set xy by Alexa app char prev_y_str[24] = "\0"; +#ifdef USE_LIGHT uint8_t getLocalLightSubtype(uint8_t device) { if (TasmotaGlobal.light_type) { if (device >= Light.device) { @@ -422,6 +423,7 @@ void HueLightStatus2(uint8_t device, String *response) *response += buf; free(buf); } +#endif // USE_LIGHT // generate a unique lightId mixing local IP address and device number // it is limited to 32 devices. @@ -496,7 +498,9 @@ void HueGlobalConfig(String *path) { path->remove(0,1); // cut leading / to get response = F("{\"lights\":{"); bool appending = false; // do we need to add a comma to append +#ifdef USE_LIGHT CheckHue(&response, appending); +#endif // USE_LIGHT #ifdef USE_ZIGBEE ZigbeeCheckHue(&response, appending); #endif // USE_ZIGBEE @@ -515,6 +519,7 @@ void HueAuthentication(String *path) AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Authentication Result (%s)"), response); } +#ifdef USE_LIGHT // refactored to remove code duplicates void CheckHue(String * response, bool &appending) { uint8_t maxhue = (TasmotaGlobal.devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : TasmotaGlobal.devices_present; @@ -621,8 +626,8 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { strlcpy(prev_x_str, tok_x.getStr(), sizeof(prev_x_str)); strlcpy(prev_y_str, tok_y.getStr(), sizeof(prev_y_str)); uint8_t rr,gg,bb; - LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); - LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); + XyToRgb(x, y, &rr, &gg, &bb); + RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); prev_hue = changeUIntScale(hue, 0, 360, 0, 65535); // calculate back prev_hue prev_sat = (sat > 254 ? 254 : sat); //AddLog_P(LOG_LEVEL_DEBUG_MORE, "XY RGB (%d %d %d) HS (%d %d)", rr,gg,bb,hue,sat); @@ -728,6 +733,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { } free(buf); } +#endif // USE_LIGHT void HueLights(String *path) { @@ -744,7 +750,9 @@ void HueLights(String *path) if (path->endsWith(F("/lights"))) { // Got /lights response = "{"; bool appending = false; +#ifdef USE_LIGHT CheckHue(&response, appending); +#endif // USE_LIGHT #ifdef USE_ZIGBEE ZigbeeCheckHue(&response, appending); #endif // USE_ZIGBEE @@ -771,9 +779,11 @@ void HueLights(String *path) return Script_Handle_Hue(path); } #endif +#ifdef USE_LIGHT if ((device >= 1) || (device <= maxhue)) { HueLightsCommand(device, device_id, response); } +#endif // USE_LIGHT } else if(path->indexOf(F("/lights/")) >= 0) { // Got /lights/ID @@ -797,12 +807,14 @@ void HueLights(String *path) } #endif +#ifdef USE_LIGHT if ((device < 1) || (device > maxhue)) { device = 1; } response += F("{\"state\":"); HueLightStatus1(device, &response); HueLightStatus2(device, &response); +#endif // USE_LIGHT } else { response = "{}"; @@ -835,7 +847,9 @@ void HueGroups(String *path) ZigbeeHueGroups(&response); #endif // USE_ZIGBEE response.replace("{l1", lights); +#ifdef USE_LIGHT HueLightStatus1(1, &response); +#endif // USE_LIGHT response += F("}"); } diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino index 100faef9c..cc77d2314 100644 --- a/tasmota/xdrv_23_zigbee_3_hue.ino +++ b/tasmota/xdrv_23_zigbee_3_hue.ino @@ -18,7 +18,7 @@ */ #ifdef USE_ZIGBEE -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) // Add global functions for Hue Emulation diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index 799274dd0..9bdf84175 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -2048,11 +2048,11 @@ void ZigbeeShow(bool json) if (light.validHue() && light.validSat() && (channels >= 3)) { uint8_t r,g,b; uint8_t sat = changeUIntScale(light.getSat(), 0, 254, 0, 255); // scale to 0..255 - LightStateClass::HsToRgb(light.getHue(), sat, &r, &g, &b); + HsToRgb(light.getHue(), sat, &r, &g, &b); WSContentSend_P(msg[ZB_WEB_COLOR_RGB], r,g,b,r,g,b); } else if (light.validX() && light.validY() && (channels >= 3)) { uint8_t r,g,b; - LightStateClass::XyToRgb(light.getX() / 65535.0f, light.getY() / 65535.0f, &r, &g, &b); + XyToRgb(light.getX() / 65535.0f, light.getY() / 65535.0f, &r, &g, &b); WSContentSend_P(msg[ZB_WEB_COLOR_RGB], r,g,b,r,g,b); } }