mirror of https://github.com/arendst/Tasmota.git
Merge pull request #5748 from s-hadinger/fix/better_colormode
Allow all 5 PWM channels individually adressable with LEDs.
This commit is contained in:
commit
fa64a0bf4d
|
@ -4,6 +4,8 @@
|
||||||
* Fix Shelly 2.5 overtemp
|
* Fix Shelly 2.5 overtemp
|
||||||
* Set gamma correction as default behavior, ie "Ledtable 1"
|
* Set gamma correction as default behavior, ie "Ledtable 1"
|
||||||
* Refactored management of lights, using classes and integers instead of floats.
|
* Refactored management of lights, using classes and integers instead of floats.
|
||||||
|
* Extend PWM resolution from 8 to 10 bits for low brightness lights
|
||||||
|
* Allow all 5 PWM channels individually adressable with LEDs. (#5741)
|
||||||
*
|
*
|
||||||
* 6.5.0.8 20190413
|
* 6.5.0.8 20190413
|
||||||
* Add Tuya Dimmer 10 second heartbeat serial packet required by some Tuya dimmer secondary MCUs
|
* Add Tuya Dimmer 10 second heartbeat serial packet required by some Tuya dimmer secondary MCUs
|
||||||
|
|
|
@ -134,6 +134,10 @@ const char kLightCommands[] PROGMEM =
|
||||||
D_CMND_PIXELS "|" D_CMND_RGBWWTABLE "|" D_CMND_ROTATION "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|"
|
D_CMND_PIXELS "|" D_CMND_RGBWWTABLE "|" D_CMND_ROTATION "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|"
|
||||||
D_CMND_WHITE "|" D_CMND_WIDTH "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR "|UNDOCA" ;
|
D_CMND_WHITE "|" D_CMND_WIDTH "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR "|UNDOCA" ;
|
||||||
|
|
||||||
|
// Light color mode, either RGB alone, or white-CT alone, or both only available if ct_rgb_linked is false
|
||||||
|
enum LightColorModes {
|
||||||
|
LCM_RGB = 1, LCM_CT = 2, LCM_BOTH = 3 };
|
||||||
|
|
||||||
struct LRgbColor {
|
struct LRgbColor {
|
||||||
uint8_t R, G, B;
|
uint8_t R, G, B;
|
||||||
};
|
};
|
||||||
|
@ -280,137 +284,179 @@ uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max,
|
||||||
// This class is an abstraction of the current light state.
|
// This class is an abstraction of the current light state.
|
||||||
// It allows for b/w, full colors, or white colortone
|
// It allows for b/w, full colors, or white colortone
|
||||||
//
|
//
|
||||||
// This class has 3 independant slots
|
// This class has 2 independant slots
|
||||||
// 1/ Brightness 0.255, dimmer controls both RGB and WC (warm-cold)
|
// 1/ Brightness 0.255, dimmer controls both RGB and WC (warm-cold)
|
||||||
|
// 1/ RGB and Hue/Sat - always kept in sync and stored at full brightness,
|
||||||
|
// i.e. R G or B are 255
|
||||||
|
// briRGB specifies the brightness for the RGB slot.
|
||||||
// If Brightness is 0, it is equivalent to Off (for compatibility)
|
// If Brightness is 0, it is equivalent to Off (for compatibility)
|
||||||
// Dimmer is Brightness converted to range 0..100
|
// Dimmer is Brightness converted to range 0..100
|
||||||
// 2/ RGB and Hue/Sat - always kept in sync and stored at full brightness,
|
// 2/ White with colortone - or WC (Warm / Cold)
|
||||||
// i.e. R G or B are 255
|
// ct is 153..500 temperature (153=cold, 500=warm)
|
||||||
// 3/ White with colortone - or WC (Warm / Cold)
|
// briCT specifies the brightness for white channel
|
||||||
// ct is either 0: no white colortone control, revert to RGB
|
|
||||||
// ct is 153..500 temperature
|
|
||||||
// Optional whiteBri to contraol separately the brightness of white channel
|
|
||||||
//
|
//
|
||||||
// RGB and Hue/Sat are always kept in sync
|
// Dimmer (0.100) is automatically derived from brightness
|
||||||
// Brightness is stored in full range 0..255
|
|
||||||
// Dimmer (0.100) is autoamtically derived from brightness
|
|
||||||
//
|
//
|
||||||
// Light has two states: either color (HS) when ct==0, or white with
|
// INVARIANTS:
|
||||||
// colortone if ct > 0.
|
// 1. RGB components are always stored at full brightness and modulated with briRGB
|
||||||
|
// ((R == 255) || (G == 255) || (B == 255))
|
||||||
|
// 2. RGB and Hue/Sat are always kept in sync whether you use setRGB() or setHS()
|
||||||
|
// 3. Warm/Cold white channels are always stored at full brightness
|
||||||
|
// ((WW == 255) || (WC == 255))
|
||||||
|
// 4. WC/WW and CT are always kept in sync.
|
||||||
|
// Note: if you use setCT() then WC+WW == 255 (both channels are linked)
|
||||||
|
// but if you use setWC() both channels can be set independantly
|
||||||
|
// 5. If RGB or CT channels are deactivated, then corresponding brightness is zero
|
||||||
|
// if (colot_tone == LCM_RGB) then briCT = 0
|
||||||
|
// if (color_tone == LCM_CT) then briRGB = 0
|
||||||
|
// if (colot_tone == LCM_BOTH) then briRGB and briCT can have any values
|
||||||
//
|
//
|
||||||
//
|
// Note: If you want the actual RGB, you need to multiply with Bri, or use getActualRGBCW()
|
||||||
// Note: RGB is internally stored always at full brightness (ie. one of R,G,B is 255)
|
|
||||||
// If you want the actual RGB, you need to multiply with Bri,
|
|
||||||
// or use getActualRGBCW()
|
|
||||||
// Note: all values are stored as unsigned integer, no floats.
|
// Note: all values are stored as unsigned integer, no floats.
|
||||||
// Note: you can query vaules from this singleton. But to change values,
|
// Note: you can query vaules from this singleton. But to change values,
|
||||||
// use the LightController - changing this object will have no effect on actual light.
|
// use the LightController - changing this object will have no effect on actual light.
|
||||||
//
|
//
|
||||||
class LightStateClass {
|
class LightStateClass {
|
||||||
private:
|
private:
|
||||||
uint16_t _ct = 0; // 0 or 153..500
|
|
||||||
uint16_t _hue = 0; // 0..359
|
uint16_t _hue = 0; // 0..359
|
||||||
uint8_t _sat = 255; // 0..255
|
uint8_t _sat = 255; // 0..255
|
||||||
uint8_t _bri = 255; // 0..255
|
uint8_t _briRGB = 255; // 0..255
|
||||||
// dimmer is same as _bri but with a range of 0%-100%
|
// dimmer is same as _bri but with a range of 0%-100%
|
||||||
uint8_t _r = 255; // 0..255
|
uint8_t _r = 255; // 0..255
|
||||||
uint8_t _g = 255; // 0..255
|
uint8_t _g = 255; // 0..255
|
||||||
uint8_t _b = 255; // 0..255
|
uint8_t _b = 255; // 0..255
|
||||||
// are RGB and CT linked, i.e. if we set CT then RGB channels are off
|
|
||||||
bool _ct_rgb_linked = true;
|
uint8_t _subtype = 0; // local copy of light_subtype, if we need multiple lights
|
||||||
uint8_t _whiteBri = 255;
|
uint16_t _ct = 153; // 153..500, default to 153 (cold white)
|
||||||
|
uint8_t _wc = 255; // white cold channel
|
||||||
|
uint8_t _ww = 0; // white warm channel
|
||||||
|
uint8_t _briCT = 255;
|
||||||
|
|
||||||
|
uint8_t _color_mode = LCM_RGB; // RGB by default
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LightStateClass() {
|
LightStateClass() {
|
||||||
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::Constructor RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _bri);
|
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::Constructor RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _bri);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setCTRGBLinked(bool ct_rgb_linked) {
|
void setSubType(uint8_t sub_type) {
|
||||||
bool prev = _ct_rgb_linked;
|
_subtype = sub_type; // set sub_type at initialization, shoudln't be changed afterwards
|
||||||
_ct_rgb_linked = ct_rgb_linked;
|
|
||||||
return prev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isCTRGBLinked() {
|
// This function is a bit hairy, it will try to match the rerquired
|
||||||
return _ct_rgb_linked;
|
// colormode with the features of the device:
|
||||||
|
// LST_NONE: LCM_RGB
|
||||||
|
// LST_SINGLE: LCM_RGB
|
||||||
|
// LST_COLDWARM: LCM_CT
|
||||||
|
// LST_RGB: LCM_RGB
|
||||||
|
// LST_RGBW: LCM_RGB, LCM_CT or LCM_BOTH
|
||||||
|
// LST_RGBWC: LCM_RGB, LCM_CT or LCM_BOTH
|
||||||
|
uint8_t setColorMode(uint8_t cm) {
|
||||||
|
uint8_t prev_cm = _color_mode;
|
||||||
|
if (cm < LCM_RGB) { cm = LCM_RGB; }
|
||||||
|
if (cm > LCM_BOTH) { cm = LCM_BOTH; }
|
||||||
|
uint8_t maxbri = (_briRGB >= _briCT) ? _briRGB : _briCT;
|
||||||
|
|
||||||
|
switch (_subtype) {
|
||||||
|
case LST_COLDWARM:
|
||||||
|
_color_mode = LCM_CT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LST_NONE:
|
||||||
|
case LST_SINGLE:
|
||||||
|
case LST_RGB:
|
||||||
|
default:
|
||||||
|
_color_mode = LCM_RGB;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LST_RGBW:
|
||||||
|
case LST_RGBWC:
|
||||||
|
_color_mode = cm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (LCM_RGB == _color_mode) {
|
||||||
|
_briCT = 0;
|
||||||
|
if (0 == _briRGB) { _briRGB = maxbri; }
|
||||||
|
}
|
||||||
|
if (LCM_CT == _color_mode) {
|
||||||
|
_briRGB = 0;
|
||||||
|
if (0 == _briCT) { _briCT = maxbri; }
|
||||||
|
}
|
||||||
|
#ifdef DEBUG_LIGHT
|
||||||
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setColorMode prev_cm (%d) req_cm (%d) new_cm (%d)", prev_cm, cm, _color_mode);
|
||||||
|
#endif
|
||||||
|
return prev_cm;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setWhite() {
|
inline uint8_t getColorMode() {
|
||||||
_r = _g = _b = 255;
|
return _color_mode;
|
||||||
_hue = 0;
|
}
|
||||||
_sat = 0;
|
|
||||||
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setWhite RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _bri);
|
void addRGBMode() {
|
||||||
|
setColorMode(_color_mode | LCM_RGB);
|
||||||
|
}
|
||||||
|
void addCTMode() {
|
||||||
|
setColorMode(_color_mode | LCM_CT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get RGB color, always at full brightness (ie. one of the components is 255)
|
// Get RGB color, always at full brightness (ie. one of the components is 255)
|
||||||
void getRGB(uint8_t *r, uint8_t *g, uint8_t *b) {
|
void getRGB(uint8_t *r, uint8_t *g, uint8_t *b) {
|
||||||
if (r) *r = _r;
|
if (r) { *r = _r; }
|
||||||
if (g) *g = _g;
|
if (g) { *g = _g; }
|
||||||
if (b) *b = _b;
|
if (b) { *b = _b; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// get full brightness values for wamr and cold channels.
|
// get full brightness values for wamr and cold channels.
|
||||||
// either w=c=0 (off) or w+c=255
|
// either w=c=0 (off) or w+c=255
|
||||||
void getCW(uint8_t *rc, uint8_t *rw) {
|
void getCW(uint8_t *rc, uint8_t *rw) {
|
||||||
uint16_t ct = _ct;
|
if (rc) { *rc = _wc; }
|
||||||
uint16_t w = changeUIntScale(ct, 153, 500, 0, 255);
|
if (rw) { *rw = _ww; }
|
||||||
if (rw) { *rw = (ct ? w : 0); }
|
|
||||||
if (rc) { *rc = (ct ? 255 - w : 0); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the actual RGB corrected with Brightness, ready to drive leds
|
// Get the actual values for each channel, ie multiply with brightness
|
||||||
// return Bri
|
void getActualRGBWC(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *w, uint8_t *c) {
|
||||||
uint8_t getActualRGBCW(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *c, uint8_t *w) {
|
bool rgb_channels_on = _color_mode & LCM_RGB;
|
||||||
uint16_t bri = _bri;
|
bool ct_channels_on = _color_mode & LCM_CT;
|
||||||
uint16_t wBri = _whiteBri;
|
|
||||||
bool rgb_channels_off = _ct && _ct_rgb_linked;
|
|
||||||
|
|
||||||
if (r) { *r = rgb_channels_off ? 0 : changeUIntScale(_r, 0, 255, 0, bri); }
|
if (r) { *r = rgb_channels_on ? changeUIntScale(_r, 0, 255, 0, _briRGB) : 0; }
|
||||||
if (g) { *g = rgb_channels_off ? 0 : changeUIntScale(_g, 0, 255, 0, bri); }
|
if (g) { *g = rgb_channels_on ? changeUIntScale(_g, 0, 255, 0, _briRGB) : 0; }
|
||||||
if (b) { *b = rgb_channels_off ? 0 : changeUIntScale(_b, 0, 255, 0, bri); }
|
if (b) { *b = rgb_channels_on ? changeUIntScale(_b, 0, 255, 0, _briRGB) : 0; }
|
||||||
|
|
||||||
if (_ct) {
|
if (c) { *c = ct_channels_on ? changeUIntScale(_wc, 0, 255, 0, _briCT) : 0; }
|
||||||
// change range from 153..500 to 0..255
|
if (w) { *w = ct_channels_on ? changeUIntScale(_ww, 0, 255, 0, _briCT) : 0; }
|
||||||
uint8_t iwarm, icold;
|
|
||||||
getCW(&icold, &iwarm);
|
|
||||||
if (c) { *w = changeUIntScale(icold, 0, 255, 0, wBri); }
|
|
||||||
if (w) { *c = changeUIntScale(iwarm, 0, 255, 0, wBri); }
|
|
||||||
} else {
|
|
||||||
if (w) { *w = 0; }
|
|
||||||
if (c) { *c = 0; }
|
|
||||||
}
|
|
||||||
return _bri;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t getChannels(uint8_t *channels) {
|
uint8_t getChannels(uint8_t *channels) {
|
||||||
return getActualRGBCW(&channels[0], &channels[1], &channels[2], &channels[3], &channels[4]);
|
getActualRGBWC(&channels[0], &channels[1], &channels[2], &channels[3], &channels[4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) {
|
void getHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) {
|
||||||
if (hue) *hue = _hue;
|
if (hue) { *hue = _hue; }
|
||||||
if (sat) *sat = _sat;
|
if (sat) { *sat = _sat; }
|
||||||
if (bri) *bri = _bri;
|
if (bri) { *bri = _briRGB; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// getBri() is guaranteed to give the same result as setBri() - no rounding errors.
|
// getBri() is guaranteed to give the same result as setBri() - no rounding errors.
|
||||||
uint8_t getBri() {
|
uint8_t getBri(void) {
|
||||||
return _bri; // 0..255
|
// return the max of _briCT and _briRGB
|
||||||
|
return (_briRGB >= _briCT) ? _briRGB : _briCT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the Optional white Brightness
|
// get the white Brightness
|
||||||
uint8_t getWhiteBri() {
|
inline uint8_t getBriCT() {
|
||||||
return _whiteBri;
|
return _briCT;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t getDimmer() {
|
uint8_t getDimmer() {
|
||||||
uint8_t dimmer = changeUIntScale(_bri, 0, 255, 0, 100); // 0.100
|
uint8_t bri = getBri();
|
||||||
|
uint8_t dimmer = changeUIntScale(bri, 0, 255, 0, 100); // 0.100
|
||||||
// if brightness is non zero, force dimmer to be non-zero too
|
// if brightness is non zero, force dimmer to be non-zero too
|
||||||
if ((dimmer == 0) && (_bri > 0)) { dimmer = 1; }
|
if ((dimmer == 0) && (bri > 0)) { dimmer = 1; }
|
||||||
return dimmer;
|
return dimmer;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t getCT() {
|
inline uint16_t getCT() {
|
||||||
return _ct; // 0 or 153..500
|
return _ct; // 153..500
|
||||||
}
|
}
|
||||||
|
|
||||||
// get current color in XY format
|
// get current color in XY format
|
||||||
|
@ -420,66 +466,117 @@ class LightStateClass {
|
||||||
|
|
||||||
// setters -- do not use directly, use the light_controller instead
|
// setters -- do not use directly, use the light_controller instead
|
||||||
// sets both master Bri and whiteBri
|
// sets both master Bri and whiteBri
|
||||||
void setBri(uint8_t bri, bool syncWhiteBri = true) {
|
void setBri(uint8_t bri) {
|
||||||
_bri = bri; // 0..255
|
setBriRGB(_color_mode & LCM_RGB ? bri : 0);
|
||||||
if (syncWhiteBri) { _whiteBri = bri; }
|
setBriCT(_color_mode & LCM_CT ? bri : 0);
|
||||||
#ifdef DEBUG_LIGHT
|
#ifdef DEBUG_LIGHT
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setBri RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _bri);
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setBri RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// whanges the white brightness, leaving master Bri untouched
|
// changes the RGB brightness alone
|
||||||
void setWhiteBri(uint8_t wBri) {
|
uint8_t setBriRGB(uint8_t bri_rgb) {
|
||||||
_whiteBri = wBri;
|
uint8_t prev_bri = _briRGB;
|
||||||
|
_briRGB = bri_rgb;
|
||||||
|
if (bri_rgb > 0) { addRGBMode(); }
|
||||||
|
return prev_bri;
|
||||||
|
}
|
||||||
|
|
||||||
|
// changes the white brightness alone
|
||||||
|
uint8_t setBriCT(uint8_t bri_ct) {
|
||||||
|
uint8_t prev_bri = _briCT;
|
||||||
|
_briCT = bri_ct;
|
||||||
|
if (bri_ct > 0) { addCTMode(); }
|
||||||
|
return prev_bri;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t getBriRGB() {
|
||||||
|
return _briRGB;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDimmer(uint8_t dimmer) {
|
void setDimmer(uint8_t dimmer) {
|
||||||
_bri = changeUIntScale(dimmer, 0, 100, 0, 255); // 0..255
|
setBri(changeUIntScale(dimmer, 0, 100, 0, 255)); // 0..255
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCT(uint16_t ct) {
|
void setCT(uint16_t ct) {
|
||||||
if (0 == ct) {
|
if (0 == ct) {
|
||||||
// disable ct mode
|
// disable ct mode
|
||||||
_ct = 0;
|
setColorMode(LCM_RGB); // try deactivating CT mode, setColorMode() will check which is legal
|
||||||
} else {
|
} else {
|
||||||
_ct = (ct < 153 ? 153 : (ct > 500 ? 500 : ct));
|
ct = (ct < 153 ? 153 : (ct > 500 ? 500 : ct));
|
||||||
|
_ww = changeUIntScale(ct, 153, 500, 0, 255);
|
||||||
|
_wc = 255 - _ww;
|
||||||
|
_ct = ct;
|
||||||
|
addCTMode();
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_LIGHT
|
#ifdef DEBUG_LIGHT
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCT RGB raw (%d %d %d) HS (%d %d) bri (%d) CT (%d)", _r, _g, _b, _hue, _sat, _bri, _ct);
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCT RGB raw (%d %d %d) HS (%d %d) briRGB (%d) briCT (%d) CT (%d)", _r, _g, _b, _hue, _sat, _briRGB, _briCT, _ct);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// recalibrate W and C, in case a channel was changed independently
|
// Manually set Warm/Cold channels.
|
||||||
// w+c must be 255, recalculate ct temperature accordingly
|
// There are two modes:
|
||||||
// returns brightness
|
// 1. (free_range == false, default)
|
||||||
uint8_t setCW(uint8_t w, uint8_t c) {
|
// In this mode there is only one virtual white channel with color temperature
|
||||||
uint16_t wc = w + c;
|
// As a side effect, WC+WW = 255. It means also that the sum of light power
|
||||||
if (wc > 0) {
|
// from white LEDs is always equal to briCT. It is not possible here
|
||||||
uint16_t ct = changeUIntScale(w, 0, wc, 153, 500);
|
// to set both white LEDs at full power, hence protecting power supplies
|
||||||
setCT(ct);
|
// from overlaoding.
|
||||||
|
// 2. (free_range == true)
|
||||||
|
// In this mode, values of WC and WW are free -- both channels can be set
|
||||||
|
// at full power.
|
||||||
|
// In this mode, we always scale both channels so that one at least is 255.
|
||||||
|
//
|
||||||
|
// We automatically adjust briCT to have the right values of channels
|
||||||
|
void setWC(uint8_t w, uint8_t c, bool free_range = false) {
|
||||||
|
uint16_t max = (w > c) ? w : c; // 0..255
|
||||||
|
uint16_t sum = c + w;
|
||||||
|
|
||||||
|
if (0 == max) {
|
||||||
|
_briCT = 0; // brightness set to null
|
||||||
|
setColorMode(LCM_RGB); // try deactivating CT mode, setColorMode() will check which is legal
|
||||||
} else {
|
} else {
|
||||||
setCT(0);
|
if (!free_range) {
|
||||||
|
// we need to normalize to sum = 255
|
||||||
|
_ww = changeUIntScale(w, 0, sum, 0, 255);
|
||||||
|
_wc = 255 - _ww;
|
||||||
|
} else { // we normalize to max = 255
|
||||||
|
_ww = changeUIntScale(w, 0, max, 0, 255);
|
||||||
|
_wc = changeUIntScale(c, 0, max, 0, 255);
|
||||||
|
}
|
||||||
|
_ct = changeUIntScale(w, 0, sum, 153, 500);
|
||||||
|
addCTMode(); // activate CT mode if needed
|
||||||
|
if (_color_mode & LCM_CT) { _briCT = free_range ? max : (sum > 255 ? 255 : sum); }
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_LIGHT
|
#ifdef DEBUG_LIGHT
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCW CW (%d %d) CT (%d)", c, w, _ct);
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setWC WC (%d %d) CT (%d) briCT (%d)", w, c, _ct, _briCT);
|
||||||
#endif
|
#endif
|
||||||
return (wc > 255 ? 255 : wc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sets RGB and returns the Brightness. Bri is unchanged here.
|
// sets RGB and returns the Brightness. Bri is updated unless keep_bri is true
|
||||||
uint8_t setRGB(uint8_t r, uint8_t g, uint8_t b) {
|
uint8_t setRGB(uint8_t r, uint8_t g, uint8_t b, bool keep_bri = false) {
|
||||||
uint16_t hue;
|
uint16_t hue;
|
||||||
uint8_t sat;
|
uint8_t sat;
|
||||||
|
#ifdef DEBUG_LIGHT
|
||||||
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB input (%d %d %d)", r, g, b);
|
||||||
|
#endif
|
||||||
|
|
||||||
uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; // 0..255
|
uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; // 0..255
|
||||||
|
|
||||||
if (0 == max) {
|
if (0 == max) {
|
||||||
r = g = b = 255;
|
r = g = b = 255;
|
||||||
} else if (255 > max) {
|
setColorMode(LCM_CT); // try deactivating RGB, setColorMode() will check if this is legal
|
||||||
// we need to normalize rgb
|
} else {
|
||||||
r = changeUIntScale(r, 0, max, 0, 255);
|
if (255 > max) {
|
||||||
g = changeUIntScale(g, 0, max, 0, 255);
|
// we need to normalize rgb
|
||||||
b = changeUIntScale(b, 0, max, 0, 255);
|
r = changeUIntScale(r, 0, max, 0, 255);
|
||||||
|
g = changeUIntScale(g, 0, max, 0, 255);
|
||||||
|
b = changeUIntScale(b, 0, max, 0, 255);
|
||||||
|
}
|
||||||
|
addRGBMode();
|
||||||
|
}
|
||||||
|
if (!keep_bri) {
|
||||||
|
_briRGB = (_color_mode & LCM_RGB) ? max : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
RgbToHsb(r, g, b, &hue, &sat, nullptr);
|
RgbToHsb(r, g, b, &hue, &sat, nullptr);
|
||||||
|
@ -488,9 +585,8 @@ class LightStateClass {
|
||||||
_b = b;
|
_b = b;
|
||||||
_hue = hue;
|
_hue = hue;
|
||||||
_sat = sat;
|
_sat = sat;
|
||||||
_ct = 0; // no ct mode
|
|
||||||
#ifdef DEBUG_LIGHT
|
#ifdef DEBUG_LIGHT
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _bri);
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB);
|
||||||
#endif
|
#endif
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
@ -503,10 +599,10 @@ class LightStateClass {
|
||||||
_b = b;
|
_b = b;
|
||||||
_hue = hue;
|
_hue = hue;
|
||||||
_sat = sat;
|
_sat = sat;
|
||||||
_ct = 0; // no ct mode
|
addRGBMode();
|
||||||
#ifdef DEBUG_LIGHT
|
#ifdef DEBUG_LIGHT
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS HS (%d %d) rgb (%d %d %d)", hue, sat, r, g, b);
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS HS (%d %d) rgb (%d %d %d)", hue, sat, r, g, b);
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _bri);
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,26 +610,13 @@ class LightStateClass {
|
||||||
// Channels are: R G B CW WW
|
// Channels are: R G B CW WW
|
||||||
// Brightness is automatically recalculated to adjust channels to the desired values
|
// Brightness is automatically recalculated to adjust channels to the desired values
|
||||||
void setChannels(uint8_t *channels) {
|
void setChannels(uint8_t *channels) {
|
||||||
uint8_t briRGB = setRGB(channels[0], channels[1], channels[2]);
|
setRGB(channels[0], channels[1], channels[2]);
|
||||||
uint8_t briCW = setCW(channels[3], channels[4]);
|
setWC(channels[3], channels[4], true); // free range for WC and WW
|
||||||
#ifdef DEBUG_LIGHT
|
#ifdef DEBUG_LIGHT
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels (%d %d %d %d %d)",
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels (%d %d %d %d %d)",
|
||||||
channels[0], channels[1], channels[2], channels[3], channels[4]);
|
channels[0], channels[1], channels[2], channels[3], channels[4]);
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels CT (%d) briRGB (%d) briCW (%d) linked (%d)",
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels CT (%d) briRGB (%d) briCT (%d)", _ct, _briRGB, _briCT);
|
||||||
_ct, briRGB, briCW, _ct_rgb_linked);
|
|
||||||
#endif
|
#endif
|
||||||
if (_ct_rgb_linked){
|
|
||||||
// if RGB and CT are linked, we set Brightness to either CT or RGB
|
|
||||||
if (_ct) {
|
|
||||||
setBri(briCW);
|
|
||||||
} else {
|
|
||||||
setBri(briRGB);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// we need to store the two brightnesses separately
|
|
||||||
setBri(briRGB);
|
|
||||||
setWhiteBri(briCW);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// new version of RGB to HSB with only integer calculation
|
// new version of RGB to HSB with only integer calculation
|
||||||
|
@ -696,17 +779,35 @@ void LightStateClass::XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_
|
||||||
}
|
}
|
||||||
|
|
||||||
class LightControllerClass {
|
class LightControllerClass {
|
||||||
|
private:
|
||||||
LightStateClass *_state;
|
LightStateClass *_state;
|
||||||
|
|
||||||
|
// are RGB and CT linked, i.e. if we set CT then RGB channels are off
|
||||||
|
bool _ct_rgb_linked = true;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LightControllerClass(LightStateClass& state) {
|
LightControllerClass(LightStateClass& state) {
|
||||||
_state = &state;
|
_state = &state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setSubType(uint8_t sub_type) {
|
||||||
|
_state->setSubType(sub_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool setCTRGBLinked(bool ct_rgb_linked) {
|
||||||
|
bool prev = _ct_rgb_linked;
|
||||||
|
_ct_rgb_linked = ct_rgb_linked;
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isCTRGBLinked() {
|
||||||
|
return _ct_rgb_linked;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_LIGHT
|
#ifdef DEBUG_LIGHT
|
||||||
void debugLogs() {
|
void debugLogs() {
|
||||||
uint8_t r,g,b,c,w;
|
uint8_t r,g,b,c,w;
|
||||||
_state->getActualRGBCW(&r,&g,&b,&c,&w);
|
_state->getActualRGBWC(&r,&g,&b,&w,&c);
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs rgb (%d %d %d) cw (%d %d)",
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs rgb (%d %d %d) cw (%d %d)",
|
||||||
r, g, b, c, w);
|
r, g, b, c, w);
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs lightCurrent (%d %d %d %d %d)",
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs lightCurrent (%d %d %d %d %d)",
|
||||||
|
@ -723,24 +824,21 @@ public:
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings light_type/sub (%d %d)",
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings light_type/sub (%d %d)",
|
||||||
light_type, light_subtype);
|
light_type, light_subtype);
|
||||||
#endif
|
#endif
|
||||||
|
// TODO
|
||||||
// set the RGB from settings
|
// set the RGB from settings
|
||||||
_state->setRGB(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]);
|
_state->setRGB(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]);
|
||||||
|
|
||||||
// get CT only for lights that support it
|
// get CT only for lights that support it
|
||||||
if ((LST_COLDWARM == light_subtype) || (LST_RGBW <= light_subtype)) {
|
if ((LST_COLDWARM == light_subtype) || (LST_RGBW <= light_subtype)) {
|
||||||
// calculate whether we have CT set
|
// TODO check
|
||||||
uint32_t c = Settings.light_color[3];
|
_state->setWC(Settings.light_color[3], Settings.light_color[4], true);
|
||||||
uint32_t w = Settings.light_color[4];
|
|
||||||
uint32_t ct = ((c > 0) || (w > 0)) ? changeUIntScale(w, 0, 255, 153, 500) : 0;
|
|
||||||
_state->setCT(ct);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set Dimmer
|
// set Dimmer
|
||||||
_state->setDimmer(Settings.light_dimmer);
|
_state->setDimmer(Settings.light_dimmer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeCT(uint16_t new_ct) {
|
void changeCTB(uint16_t new_ct, uint8_t briCT) {
|
||||||
/* Color Temperature (https://developers.meethue.com/documentation/core-concepts)
|
/* Color Temperature (https://developers.meethue.com/documentation/core-concepts)
|
||||||
*
|
*
|
||||||
* ct = 153 = 2000K = Warm = CCWW = 00FF
|
* ct = 153 = 2000K = Warm = CCWW = 00FF
|
||||||
|
@ -751,6 +849,8 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_state->setCT(new_ct);
|
_state->setCT(new_ct);
|
||||||
|
_state->setBriCT(briCT);
|
||||||
|
if (_ct_rgb_linked) { _state->setColorMode(LCM_CT); } // try to force CT
|
||||||
saveSettings();
|
saveSettings();
|
||||||
calcLevels();
|
calcLevels();
|
||||||
//debugLogs();
|
//debugLogs();
|
||||||
|
@ -767,65 +867,67 @@ public:
|
||||||
calcLevels();
|
calcLevels();
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeRGB(uint8_t r, uint8_t g, uint8_t b) {
|
void changeRGB(uint8_t r, uint8_t g, uint8_t b, bool keep_bri = false) {
|
||||||
_state->setRGB(r, g, b);
|
_state->setRGB(r, g, b, keep_bri);
|
||||||
|
if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } // try to force RGB
|
||||||
saveSettings();
|
saveSettings();
|
||||||
calcLevels();
|
calcLevels();
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate the levels for each channel
|
// calculate the levels for each channel
|
||||||
void calcLevels() {
|
void calcLevels() {
|
||||||
uint8_t r,g,b,w,c,bri;
|
uint8_t r,g,b,w,c,briRGB,briCT;
|
||||||
bri = _state->getActualRGBCW(&r,&g,&b,&w,&c);
|
_state->getActualRGBWC(&r,&g,&b,&w,&c);
|
||||||
uint8_t wBri = _state->getWhiteBri();
|
briRGB = _state->getBriRGB();
|
||||||
|
briCT = _state->getBriCT();
|
||||||
|
|
||||||
light_current_color[0] = light_current_color[1] = light_current_color[2] = 0;
|
light_current_color[0] = light_current_color[1] = light_current_color[2] = 0;
|
||||||
light_current_color[3] = light_current_color[4] = 0;
|
light_current_color[3] = light_current_color[4] = 0;
|
||||||
if (PHILIPS == my_module_type) {
|
switch (light_subtype) {
|
||||||
// Xiaomi Philips bulbs follow a different scheme:
|
case LST_NONE:
|
||||||
// channel 0=intensity, channel2=temperature
|
light_current_color[0] = 255;
|
||||||
light_current_color[0] = bri; // set brightness from r (white)
|
break;
|
||||||
light_current_color[1] = c;
|
case LST_SINGLE:
|
||||||
} else {
|
light_current_color[0] = briRGB;
|
||||||
switch (light_subtype) {
|
break;
|
||||||
case LST_NONE:
|
case LST_COLDWARM:
|
||||||
light_current_color[0] = 255;
|
light_current_color[0] = w;
|
||||||
break;
|
light_current_color[1] = c;
|
||||||
case LST_SINGLE:
|
break;
|
||||||
light_current_color[0] = bri;
|
case LST_RGBW:
|
||||||
break;
|
case LST_RGBWC:
|
||||||
case LST_COLDWARM:
|
if (LST_RGBWC == light_subtype) {
|
||||||
light_current_color[0] = w;
|
light_current_color[3] = w;
|
||||||
light_current_color[1] = c;
|
light_current_color[4] = c;
|
||||||
break;
|
} else {
|
||||||
case LST_RGB:
|
light_current_color[3] = briCT;
|
||||||
case LST_RGBW:
|
}
|
||||||
case LST_RGBWC:
|
// continue
|
||||||
light_current_color[0] = r;
|
case LST_RGB:
|
||||||
light_current_color[1] = g;
|
light_current_color[0] = r;
|
||||||
light_current_color[2] = b;
|
light_current_color[1] = g;
|
||||||
if (c || w) { // if we have CT set
|
light_current_color[2] = b;
|
||||||
if (LST_RGBWC == light_subtype) {
|
break;
|
||||||
light_current_color[3] = w;
|
|
||||||
light_current_color[4] = c;
|
|
||||||
} else if (LST_RGBW == light_subtype) {
|
|
||||||
light_current_color[3] = wBri;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeHS(uint16_t hue, uint8_t sat) {
|
void changeHSB(uint16_t hue, uint8_t sat, uint8_t briRGB) {
|
||||||
_state->setHS(hue, sat);
|
_state->setHS(hue, sat);
|
||||||
|
_state->setBriRGB(briRGB);
|
||||||
|
if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } // try to force RGB
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
calcLevels();
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the current light state to Settings.
|
// save the current light state to Settings.
|
||||||
void saveSettings() {
|
void saveSettings() {
|
||||||
_state->getRGB(&Settings.light_color[0], &Settings.light_color[1], &Settings.light_color[2]);
|
memset(&Settings.light_color[0], 0, sizeof(Settings.light_color));
|
||||||
_state->getCW(&Settings.light_color[3], &Settings.light_color[4]);
|
if (_state->getBriRGB() > 0) {
|
||||||
|
_state->getRGB(&Settings.light_color[0], &Settings.light_color[1], &Settings.light_color[2]);
|
||||||
|
}
|
||||||
|
if (_state->getBriCT() > 0) {
|
||||||
|
_state->getCW(&Settings.light_color[3], &Settings.light_color[4]);
|
||||||
|
}
|
||||||
Settings.light_dimmer = _state->getDimmer();
|
Settings.light_dimmer = _state->getDimmer();
|
||||||
#ifdef DEBUG_LIGHT
|
#ifdef DEBUG_LIGHT
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::saveSettings Settings.light_color (%d %d %d %d %d - %d)",
|
AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::saveSettings Settings.light_color (%d %d %d %d %d - %d)",
|
||||||
|
@ -1221,6 +1323,7 @@ void LightInit(void)
|
||||||
light_device = devices_present;
|
light_device = devices_present;
|
||||||
light_subtype = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); // Always 0 - LST_MAX (5)
|
light_subtype = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); // Always 0 - LST_MAX (5)
|
||||||
|
|
||||||
|
light_controller.setSubType(light_subtype);
|
||||||
light_controller.loadSettings();
|
light_controller.loadSettings();
|
||||||
|
|
||||||
if (LST_SINGLE == light_subtype) {
|
if (LST_SINGLE == light_subtype) {
|
||||||
|
@ -1339,7 +1442,7 @@ void LightUpdateColorMapping(void)
|
||||||
|
|
||||||
// do not allow independant RGV and WC colors
|
// do not allow independant RGV and WC colors
|
||||||
bool ct_rgb_linked = !(Settings.param[P_RGB_REMAP] & 128);
|
bool ct_rgb_linked = !(Settings.param[P_RGB_REMAP] & 128);
|
||||||
light_state.setCTRGBLinked(ct_rgb_linked);
|
light_controller.setCTRGBLinked(ct_rgb_linked);
|
||||||
|
|
||||||
light_update = 1;
|
light_update = 1;
|
||||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%d colors: %d %d %d %d %d") ,Settings.param[P_RGB_REMAP], light_color_remap[0],light_color_remap[1],light_color_remap[2],light_color_remap[3],light_color_remap[4]);
|
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%d colors: %d %d %d %d %d") ,Settings.param[P_RGB_REMAP], light_color_remap[0],light_color_remap[1],light_color_remap[2],light_color_remap[3],light_color_remap[4]);
|
||||||
|
@ -1356,7 +1459,7 @@ void LightSetColorTemp(uint16_t ct)
|
||||||
if ((LST_COLDWARM != light_subtype) && (LST_RGBWC != light_subtype)) {
|
if ((LST_COLDWARM != light_subtype) && (LST_RGBWC != light_subtype)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
light_controller.changeCT(ct);
|
light_controller.changeCTB(ct, light_state.getBriCT());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t LightGetColorTemp(void)
|
uint16_t LightGetColorTemp(void)
|
||||||
|
@ -1365,7 +1468,7 @@ uint16_t LightGetColorTemp(void)
|
||||||
if ((LST_COLDWARM != light_subtype) && (LST_RGBWC != light_subtype)) {
|
if ((LST_COLDWARM != light_subtype) && (LST_RGBWC != light_subtype)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return light_state.getCT();
|
return (light_state.getColorMode() & LCM_CT) ? light_state.getCT() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value)
|
void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value)
|
||||||
|
@ -1376,7 +1479,7 @@ void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value)
|
||||||
if (Settings.flag.light_signal) {
|
if (Settings.flag.light_signal) {
|
||||||
uint16_t signal = changeUIntScale(value, lo, hi, 0, 255); // 0..255
|
uint16_t signal = changeUIntScale(value, lo, hi, 0, 255); // 0..255
|
||||||
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Light signal %d"), signal);
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Light signal %d"), signal);
|
||||||
light_controller.changeRGB(signal, 255 - signal, 0);
|
light_controller.changeRGB(signal, 255 - signal, 0, true); // keep bri
|
||||||
Settings.light_scheme = 0;
|
Settings.light_scheme = 0;
|
||||||
if (0 == light_state.getBri()) {
|
if (0 == light_state.getBri()) {
|
||||||
light_controller.changeBri(50);
|
light_controller.changeBri(50);
|
||||||
|
@ -1669,42 +1772,60 @@ void LightAnimate(void)
|
||||||
cur_col_10bits[i] = changeUIntScale(cur_col[i], 0, 255, 0, 1023);
|
cur_col_10bits[i] = changeUIntScale(cur_col[i], 0, 255, 0, 1023);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply gamma correction for 8 and 10 bits resolutions, if needed
|
|
||||||
if (Settings.light_correction) {
|
if (PHILIPS == my_module_type) {
|
||||||
// first apply gamma correction to all channels independently, from 8 bits value
|
// TODO
|
||||||
for (uint8_t i = 0; i < LST_MAX; i++) {
|
// Xiaomi Philips bulbs follow a different scheme:
|
||||||
cur_col_10bits[i] = ledGamma(cur_col[i], 10);
|
// channel 0=intensity, channel2=temperature
|
||||||
|
uint16_t pxBri = cur_col[0] + cur_col[1];
|
||||||
|
if (pxBri > 255) { pxBri = 255; }
|
||||||
|
//cur_col[1] = cur_col[0]; // get 8 bits CT from WC -- not really used
|
||||||
|
cur_col_10bits[1] = changeUIntScale(cur_col[0], 0, pxBri, 0, 1023); // get 10 bits CT from WC / (WC+WW)
|
||||||
|
if (Settings.light_correction) { // gamma correction
|
||||||
|
cur_col_10bits[0] = ledGamma(pxBri, 10); // 10 bits gamma correction
|
||||||
|
} else {
|
||||||
|
cur_col_10bits[0] = changeUIntScale(pxBri, 0, 255, 0, 1023); // no gamma, extend to 10 bits
|
||||||
}
|
}
|
||||||
// then apply a different correction for CW white channels
|
} else {
|
||||||
if ((LST_COLDWARM == light_subtype) || (LST_RGBWC == light_subtype)) {
|
// Apply gamma correction for 8 and 10 bits resolutions, if needed
|
||||||
uint8_t w_idx[2] = {0, 1}; // if LST_COLDWARM, channels 0 and 1
|
if (Settings.light_correction) {
|
||||||
if (LST_RGBWC == light_subtype) { // if LST_RGBWC, channels 3 and 4
|
// first apply gamma correction to all channels independently, from 8 bits value
|
||||||
w_idx[0] = 3;
|
for (uint8_t i = 0; i < LST_MAX; i++) {
|
||||||
w_idx[1] = 4;
|
cur_col_10bits[i] = ledGamma(cur_col[i], 10);
|
||||||
}
|
}
|
||||||
uint16_t white_bri = cur_col[w_idx[0]] + cur_col[w_idx[1]];
|
// then apply a different correction for CW white channels
|
||||||
// if sum of both channels is > 255, then channels are probablu uncorrelated
|
if ((LST_COLDWARM == light_subtype) || (LST_RGBWC == light_subtype)) {
|
||||||
if (white_bri <= 255) {
|
uint8_t w_idx[2] = {0, 1}; // if LST_COLDWARM, channels 0 and 1
|
||||||
// we calculate the gamma corrected sum of CW + WW
|
if (LST_RGBWC == light_subtype) { // if LST_RGBWC, channels 3 and 4
|
||||||
uint16_t white_bri_10bits = ledGamma(white_bri, 10);
|
w_idx[0] = 3;
|
||||||
// then we split the total energy among the cold and warm leds
|
w_idx[1] = 4;
|
||||||
cur_col_10bits[w_idx[0]] = changeUIntScale(cur_col[w_idx[0]], 0, white_bri, 0, white_bri_10bits);
|
}
|
||||||
cur_col_10bits[w_idx[1]] = changeUIntScale(cur_col[w_idx[1]], 0, white_bri, 0, white_bri_10bits);
|
uint16_t white_bri = cur_col[w_idx[0]] + cur_col[w_idx[1]];
|
||||||
|
// if sum of both channels is > 255, then channels are probablu uncorrelated
|
||||||
|
if (white_bri <= 255) {
|
||||||
|
// we calculate the gamma corrected sum of CW + WW
|
||||||
|
uint16_t white_bri_10bits = ledGamma(white_bri, 10);
|
||||||
|
// then we split the total energy among the cold and warm leds
|
||||||
|
cur_col_10bits[w_idx[0]] = changeUIntScale(cur_col[w_idx[0]], 0, white_bri, 0, white_bri_10bits);
|
||||||
|
cur_col_10bits[w_idx[1]] = changeUIntScale(cur_col[w_idx[1]], 0, white_bri, 0, white_bri_10bits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// still keep an 8 bits gamma corrected version
|
||||||
|
for (uint8_t i = 0; i < LST_MAX; i++) {
|
||||||
|
cur_col[i] = ledGamma(cur_col[i]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// still keep an 8 bits gamma corrected version
|
|
||||||
for (uint8_t i = 0; i < LST_MAX; i++) {
|
|
||||||
cur_col[i] = ledGamma(cur_col[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// final adjusments for PMW, post-gamma correction
|
// final adjusments for PMW, post-gamma correction
|
||||||
for (uint8_t i = 0; i < LST_MAX; i++) {
|
for (uint8_t i = 0; i < LST_MAX; i++) {
|
||||||
|
#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2)
|
||||||
// Fix unwanted blinking and PWM watchdog errors for values close to pwm_range (H801, Arilux and BN-SZ01)
|
// Fix unwanted blinking and PWM watchdog errors for values close to pwm_range (H801, Arilux and BN-SZ01)
|
||||||
// but keep value 1023 if full range (PWM will be deactivated in this case)
|
// but keep value 1023 if full range (PWM will be deactivated in this case)
|
||||||
if ((cur_col_10bits[i] > 1008) && (cur_col_10bits[i] < 1023)) {
|
if ((cur_col_10bits[i] > 1008) && (cur_col_10bits[i] < 1023)) {
|
||||||
cur_col_10bits[i] = 1008;
|
cur_col_10bits[i] = 1008;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
// scale from 0..1023 to 0..pwm_range, but keep any non-zero value to at least 1
|
// scale from 0..1023 to 0..pwm_range, but keep any non-zero value to at least 1
|
||||||
cur_col_10bits[i] = (cur_col_10bits[i] > 0) ? changeUIntScale(cur_col_10bits[i], 1, 1023, 1, Settings.pwm_range) : 0;
|
cur_col_10bits[i] = (cur_col_10bits[i] > 0) ? changeUIntScale(cur_col_10bits[i], 1, 1023, 1, Settings.pwm_range) : 0;
|
||||||
}
|
}
|
||||||
|
@ -1904,7 +2025,7 @@ bool LightCommand(void)
|
||||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
|
||||||
light_current_color[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload,0,100,0,255);
|
light_current_color[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload,0,100,0,255);
|
||||||
// if we change channels 1,2,3 then turn off CT mode (unless non-linked)
|
// if we change channels 1,2,3 then turn off CT mode (unless non-linked)
|
||||||
if ((XdrvMailbox.index <= 3) && (light_state.isCTRGBLinked())) {
|
if ((XdrvMailbox.index <= 3) && (light_controller.isCTRGBLinked())) {
|
||||||
light_current_color[3] = light_current_color[4] = 0;
|
light_current_color[3] = light_current_color[4] = 0;
|
||||||
}
|
}
|
||||||
light_controller.changeChannels(light_current_color);
|
light_controller.changeChannels(light_current_color);
|
||||||
|
@ -1936,11 +2057,11 @@ bool LightCommand(void)
|
||||||
}
|
}
|
||||||
} else { // Command with only 1 parameter, Hue (0<H<360), Saturation (0<S<100) OR Brightness (0<B<100)
|
} else { // Command with only 1 parameter, Hue (0<H<360), Saturation (0<S<100) OR Brightness (0<B<100)
|
||||||
uint16_t c_hue;
|
uint16_t c_hue;
|
||||||
uint8_t c_sat, c_bri;
|
uint8_t c_sat;
|
||||||
light_state.getHSB(&c_hue, &c_sat, &c_bri);
|
light_state.getHSB(&c_hue, &c_sat, nullptr);
|
||||||
HSB[0] = c_hue;
|
HSB[0] = c_hue;
|
||||||
HSB[1] = c_sat;
|
HSB[1] = c_sat;
|
||||||
HSB[2] = c_bri;
|
HSB[2] = light_state.getBri();
|
||||||
|
|
||||||
if (1 == XdrvMailbox.index) {
|
if (1 == XdrvMailbox.index) {
|
||||||
HSB[0] = XdrvMailbox.payload;
|
HSB[0] = XdrvMailbox.payload;
|
||||||
|
@ -1951,8 +2072,7 @@ bool LightCommand(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (validHSB) {
|
if (validHSB) {
|
||||||
light_controller.changeHS(HSB[0], HSB[1]);
|
light_controller.changeHSB(HSB[0], HSB[1], HSB[2]);
|
||||||
light_controller.changeBri(HSB[2]);
|
|
||||||
LightPreparePower();
|
LightPreparePower();
|
||||||
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_COLOR));
|
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_COLOR));
|
||||||
}
|
}
|
||||||
|
@ -2049,7 +2169,7 @@ bool LightCommand(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((XdrvMailbox.payload >= 153) && (XdrvMailbox.payload <= 500)) { // https://developers.meethue.com/documentation/core-concepts
|
if ((XdrvMailbox.payload >= 153) && (XdrvMailbox.payload <= 500)) { // https://developers.meethue.com/documentation/core-concepts
|
||||||
light_controller.changeCT(XdrvMailbox.payload);
|
light_controller.changeCTB(XdrvMailbox.payload, light_state.getBri());
|
||||||
coldim = true;
|
coldim = true;
|
||||||
} else {
|
} else {
|
||||||
Response_P(S_JSON_COMMAND_NVALUE, command, ct);
|
Response_P(S_JSON_COMMAND_NVALUE, command, ct);
|
||||||
|
|
|
@ -577,6 +577,7 @@ char prev_y_str[24] = "\0";
|
||||||
void HueLightStatus1(uint8_t device, String *response)
|
void HueLightStatus1(uint8_t device, String *response)
|
||||||
{
|
{
|
||||||
uint16_t ct = 0;
|
uint16_t ct = 0;
|
||||||
|
uint8_t color_mode;
|
||||||
String light_status = "";
|
String light_status = "";
|
||||||
uint16_t hue = 0;
|
uint16_t hue = 0;
|
||||||
uint8_t sat = 0;
|
uint8_t sat = 0;
|
||||||
|
@ -584,7 +585,8 @@ void HueLightStatus1(uint8_t device, String *response)
|
||||||
|
|
||||||
|
|
||||||
if (light_type) {
|
if (light_type) {
|
||||||
light_state.getHSB(&hue, &sat, &bri);
|
light_state.getHSB(&hue, &sat, nullptr);
|
||||||
|
bri = light_state.getBri();
|
||||||
|
|
||||||
if (bri > 254) bri = 254; // Philips Hue bri is between 1 and 254
|
if (bri > 254) bri = 254; // Philips Hue bri is between 1 and 254
|
||||||
if (bri < 1) bri = 1;
|
if (bri < 1) bri = 1;
|
||||||
|
@ -605,21 +607,11 @@ void HueLightStatus1(uint8_t device, String *response)
|
||||||
prev_x_str[0] = prev_y_str[0] = 0;
|
prev_x_str[0] = prev_y_str[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
color_mode = light_state.getColorMode();
|
||||||
ct = light_state.getCT();
|
ct = light_state.getCT();
|
||||||
// compute whether we're in CT mode
|
if (LCM_RGB == color_mode) { g_gotct = false; }
|
||||||
if (LST_RGBW <= light_subtype) {
|
if (LCM_CT == color_mode) { g_gotct = true; }
|
||||||
if (light_state.isCTRGBLinked()) {
|
// If LCM_BOTH == color_mode, leave g_gotct unchanged
|
||||||
// normal case, CT mode if we have a CT channel
|
|
||||||
g_gotct = (ct > 0 ? true : false);
|
|
||||||
}
|
|
||||||
// else leave g_gotct unchanged, otherwise it's getting messy
|
|
||||||
} else if (LST_COLDWARM == light_subtype) {
|
|
||||||
// force ct mode for LST_COLDWARM
|
|
||||||
g_gotct = true;
|
|
||||||
} else {
|
|
||||||
// for all others, no ct
|
|
||||||
g_gotct = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// re-adjust ct if close to command value
|
// re-adjust ct if close to command value
|
||||||
if ((ct > prev_ct ? ct - prev_ct : prev_ct - ct) < 1)
|
if ((ct > prev_ct ? ct - prev_ct : prev_ct - ct) < 1)
|
||||||
|
@ -638,7 +630,6 @@ void HueLightStatus1(uint8_t device, String *response)
|
||||||
light_status += ",";
|
light_status += ",";
|
||||||
//}
|
//}
|
||||||
if (LST_COLDWARM <= light_subtype) {
|
if (LST_COLDWARM <= light_subtype) {
|
||||||
//light_status += "\"colormode\":\"" + String(g_gotct ? "ct" : "hs") + "\",";
|
|
||||||
light_status += F("\"colormode\":\"");
|
light_status += F("\"colormode\":\"");
|
||||||
light_status += (g_gotct ? "ct" : "hs");
|
light_status += (g_gotct ? "ct" : "hs");
|
||||||
light_status += "\",";
|
light_status += "\",";
|
||||||
|
@ -652,7 +643,7 @@ void HueLightStatus1(uint8_t device, String *response)
|
||||||
light_status += "],";
|
light_status += "],";
|
||||||
} else {
|
} else {
|
||||||
float x, y;
|
float x, y;
|
||||||
light_state.getXY(&x, &y);
|
light_state.getXY(&x, &y);
|
||||||
light_status += "\"xy\":[";
|
light_status += "\"xy\":[";
|
||||||
light_status += String(x, 5);
|
light_status += String(x, 5);
|
||||||
light_status += ",";
|
light_status += ",";
|
||||||
|
@ -778,11 +769,13 @@ void HueLights(String *path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (light_type) {
|
if (light_type) {
|
||||||
light_state.getHSB(&hue, &sat, &bri);
|
light_state.getHSB(&hue, &sat, nullptr);
|
||||||
|
bri = light_state.getBri(); // get the combined bri for CT and RGB, not only the RGB one
|
||||||
ct = light_state.getCT();
|
ct = light_state.getCT();
|
||||||
if ((LST_COLDWARM == light_subtype) || (ct > 0)) {
|
uint8_t color_mode = light_state.getColorMode();
|
||||||
g_gotct = true;
|
if (LCM_RGB == color_mode) { g_gotct = false; }
|
||||||
}
|
if (LCM_CT == color_mode) { g_gotct = true; }
|
||||||
|
// If LCM_BOTH == color_mode, leave g_gotct unchanged
|
||||||
}
|
}
|
||||||
prev_x_str[0] = prev_y_str[0] = 0; // reset xy string
|
prev_x_str[0] = prev_y_str[0] = 0; // reset xy string
|
||||||
|
|
||||||
|
@ -876,11 +869,9 @@ void HueLights(String *path)
|
||||||
if (change) {
|
if (change) {
|
||||||
if (light_type) {
|
if (light_type) {
|
||||||
if (g_gotct) {
|
if (g_gotct) {
|
||||||
light_controller.changeCT(ct);
|
light_controller.changeCTB(ct, bri);
|
||||||
light_controller.changeBri(bri);
|
|
||||||
} else {
|
} else {
|
||||||
light_controller.changeHS(hue, sat);
|
light_controller.changeHSB(hue, sat, bri);
|
||||||
light_controller.changeBri(bri);
|
|
||||||
}
|
}
|
||||||
LightPreparePower();
|
LightPreparePower();
|
||||||
if (LST_COLDWARM <= light_subtype) {
|
if (LST_COLDWARM <= light_subtype) {
|
||||||
|
|
Loading…
Reference in New Issue