* Worked on ESP32 dimmer with Zero cross Until now the ESP32 does not support zero-cross dimmer. I take a sneak how they did in in ESPhome and adapted the approach to TASMOTA. At the end it works that smooth that likely i will change ESP8266 either so we have a common code. Currently ESP8266 is not touched. There is a minor issue with savedata == default. When changing the dimmer value the interrupts get stopped during write of the config data to flash. * ESP8266 Dimmer added Worked all so well and the code is much smaller. There is no need for reconfiguration on existing users. But there are settings not needed anymore. Will work on the documentation. Anyhow existing installations can upgrade without hickup * Optimized endpoints at dimmer 0 and 100 * Removed debug stuff * Fix Issue at dimmer = 0 * Small bugfix * Final checked Version * Update xsns_01_counter.ino * Add missing func * Update xsns_01_counter.ino * Moved out of the house of counter and build my own one * New ZeroCross Driver * Update xdrv_91_zerocrossDimmer.ino * evolving * Delete xdrv_91_zerocrossDimmer.ino * Add files via upload * Changed drv number from 1 to 68 * Commit to merge
367 lines
13 KiB
C++
367 lines
13 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// 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 ac_dimmer_table[] = { // don't put in PROGMEM for performance reasons
|
|
{ 0, 0 },
|
|
{ 10, 64 },
|
|
{ 50, 175 },
|
|
{ 100, 235 },
|
|
{ 500, 485 },
|
|
{ 900, 704 },
|
|
{ 950, 748 },
|
|
{ 990, 850 },
|
|
{ 1023, 1023 },
|
|
{ 0xFFFF, 0xFFFF } // fail-safe if out of range
|
|
};
|
|
|
|
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(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;
|
|
}
|
|
|
|
#ifdef ESP8266
|
|
#define POW FastPrecisePowf
|
|
#else
|
|
#define POW powf
|
|
#endif
|
|
|
|
//
|
|
// 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];
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: RgbToXy X=%5_f Y=%5_f Z=%5_f TOTAL=%5_f", &XYZ[0], &XYZ[1], &XYZ[2], &XYZ_sum);
|
|
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;
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: XyToRgb x=%5_f y=%5_f z=%5_f", &x, &y, &z);
|
|
XYZ[0] = x / y;
|
|
XYZ[1] = 1.0f;
|
|
XYZ[2] = z / y;
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: XyToRgb X=%5_f Y=%5_f Z=%5_f", &XYZ[0], &XYZ[1], &XYZ[2]);
|
|
static const float rgb_factors[] = { 1.612f, -0.203f, -0.302f,
|
|
-0.509f, 1.412f, 0.066f,
|
|
0.026f, -0.072f, 0.962f };
|
|
mat3x3(rgb_factors, XYZ, rgb);
|
|
// AddLog(LOG_LEVEL_DEBUG, ">>>: XyToRgb rr=%5_f gg=%5_f bb=%5_f", &rgb[0], &rgb[1], &rgb[2]);
|
|
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 power select to 10 bits timing based on sinus curve
|
|
uint16_t ac_zero_cross_power(uint16_t v) {
|
|
return ledGamma_internal(v, ac_dimmer_table);
|
|
}
|
|
|
|
// 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));
|
|
}
|
|
|
|
// Reverse 10 bits
|
|
uint16_t ledGammaReverse(uint16_t vg) {
|
|
return ledGammaReverse_internal(vg, gamma_table);
|
|
}
|
|
|
|
// 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);
|
|
}
|