Tasmota/tasmota/tasmota_xdrv_driver/xdrv_04_light_utils.ino
stefanbode 39abde2583
Worked on ESP32 dimmer with Zero cross (#18481)
* 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
2023-04-25 13:46:19 +02:00

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