Browse Source

Use the 5th Channel as Cold White LED

rfm69
Niklas Wagner 6 years ago
parent
commit
20adc846a9
7 changed files with 2721 additions and 2641 deletions
  1. +12
    -7
      code/espurna/config/general.h
  2. BIN
      code/espurna/data/index.html.gz
  3. +77
    -25
      code/espurna/light.ino
  4. +6
    -3
      code/espurna/ntp.ino
  5. +2603
    -2599
      code/espurna/static/index.html.gz.h
  6. +9
    -1
      code/html/custom.js
  7. +14
    -6
      code/html/index.html

+ 12
- 7
code/espurna/config/general.h View File

@ -907,11 +907,8 @@ PROGMEM const char* const custom_reset_string[] = {
#define LIGHT_MAX_BRIGHTNESS 255 // Maximun brightness value
#endif
//#define LIGHT_MIN_MIREDS 153 // NOT USED (yet)! // Default to the Philips Hue value that HA has always assumed
//#define LIGHT_MAX_MIREDS 500 // NOT USED (yet)! // https://developers.meethue.com/documentation/core-concepts
#ifndef LIGHT_DEFAULT_MIREDS
#define LIGHT_DEFAULT_MIREDS 153 // Default value used by MQTT. This value is __NEVRER__ applied!
#endif
#define LIGHT_MIN_MIREDS 153 // Default to the Philips Hue value that HA also use.
#define LIGHT_MAX_MIREDS 500 // https://developers.meethue.com/documentation/core-concepts
#ifndef LIGHT_STEP
#define LIGHT_STEP 32 // Step size
@ -922,9 +919,18 @@ PROGMEM const char* const custom_reset_string[] = {
#endif
#ifndef LIGHT_USE_WHITE
#define LIGHT_USE_WHITE 0 // Use white channel whenever RGB have the same value
#define LIGHT_USE_WHITE 0 // Use the 4th channel as (Warm-)White LEDs
#endif
#ifndef LIGHT_USE_COLD_WHITE
#define LIGHT_USE_COLD_WHITE 0 // Use the 5th channel as Coldwhite LEDs, LIGHT_USE_WHITE must be 1.
#endif
// Used when LIGHT_USE_WHITE AND LIGHT_USE_COLD_WHITE is 1 - (1000000/Kelvin = MiReds)
// Warning! Don't change this yet, NOT FULLY IMPLEMENTED!
#define LIGHT_MIRED_W1 500 // Warmwhite Strip, Value must be __ABOVE__ W2!! (Default: 2000 Kelvin/500 MiRed)
#define LIGHT_MIRED_W2 153 // Coldwhite Strip, Value must be __BELOW__ W1!! (Default: 6535 Kelvin/153 MiRed)
#ifndef LIGHT_USE_GAMMA
#define LIGHT_USE_GAMMA 0 // Use gamma correction for color channels
#endif
@ -1115,7 +1121,6 @@ PROGMEM const char* const custom_reset_string[] = {
#define NTP_DST_REGION 0 // 0 for Europe, 1 for USA (defined in NtpClientLib)
#endif
// -----------------------------------------------------------------------------
// ALEXA
// -----------------------------------------------------------------------------


BIN
code/espurna/data/index.html.gz View File


+ 77
- 25
code/espurna/light.ino View File

@ -40,10 +40,11 @@ bool _light_use_transitions = false;
unsigned int _light_transition_time = LIGHT_TRANSITION_TIME;
bool _light_has_color = false;
bool _light_use_white = false;
bool _light_use_cold_white = false;
bool _light_use_gamma = false;
unsigned long _light_steps_left = 1;
unsigned char _light_brightness = LIGHT_MAX_BRIGHTNESS;
unsigned int _light_mireds = LIGHT_DEFAULT_MIREDS;
unsigned int _light_mireds = (LIGHT_MIRED_W1+LIGHT_MIRED_W2)/2;
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY92XX
#include <my92xx.h>
@ -76,40 +77,75 @@ const unsigned char _light_gamma_table[] = {
// UTILS
// -----------------------------------------------------------------------------
unsigned char getMax(unsigned char a, unsigned char b, unsigned char c) {
return std::max(a, std::max(b, c));
}
unsigned char getMax(unsigned char a, unsigned char b, unsigned char c, unsigned char d) {
return std::max(std::max(a, b), std::max(c, d));
}
unsigned char getMax(unsigned char a, unsigned char b, unsigned char c, unsigned char d, unsigned char e) {
return std::max(std::max(a, b), std::max(c, std::max(d, e)));
}
unsigned char getMin(unsigned char a, unsigned char b, unsigned char c) {
return std::min(a, std::min(b, c));
}
void _setRGBInputValue(unsigned char red, unsigned char green, unsigned char blue) {
_light_channel[0].inputValue = red;
_light_channel[1].inputValue = green;
_light_channel[2].inputValue = blue;
_light_channel[0].inputValue = constrain(red, 0, LIGHT_MAX_VALUE);
_light_channel[1].inputValue = constrain(green, 0, LIGHT_MAX_VALUE);;
_light_channel[2].inputValue = constrain(blue, 0, LIGHT_MAX_VALUE);;
}
void _generateBrightness() {
double brightness = (double) _light_brightness / LIGHT_MAX_BRIGHTNESS;
// Convert RGB to RGBW
// Convert RGB to RGBW(W)
if (_light_has_color && _light_use_white) {
// Substract the common part from RGB channels and add it to white channel. So [250,150,50] -> [200,100,0,50
unsigned char white = std::min(_light_channel[0].inputValue, std::min(_light_channel[1].inputValue, _light_channel[2].inputValue));
// Substract the common part from RGB channels and add it to white channel. So [250,150,50] -> [200,100,0,50]
unsigned char white = getMin(_light_channel[0].inputValue, _light_channel[1].inputValue, _light_channel[2].inputValue);
for (unsigned int i=0; i < 3; i++) {
_light_channel[i].value = _light_channel[i].inputValue - white;
}
_light_channel[3].value = white;
_light_channel[3].inputValue = 0;
// Split the White Value across 2 White LED Strips.
if (_light_use_cold_white) {
// This change the range from 153-500 to 0-347 so we get a value between 0 and 1 in the end.
double miredFactor = ((double) _light_mireds - (double) LIGHT_MIRED_W2)/((double) LIGHT_MIRED_W1 - (double) LIGHT_MIRED_W2);
_light_channel[3].inputValue = 0;
_light_channel[3].value = round(miredFactor * white);
_light_channel[4].inputValue = 0;
_light_channel[4].value = round(((double) 1.0 - (double) miredFactor) * white);
} else {
_light_channel[3].inputValue = 0;
_light_channel[3].value = white;
}
// Scale up to equal input values. So [250,150,50] -> [200,100,0,50] -> [250, 125, 0, 63]
unsigned char max_in = std::max(_light_channel[0].inputValue, std::max(_light_channel[1].inputValue, _light_channel[2].inputValue));
unsigned char max_out = std::max(std::max(_light_channel[0].value, _light_channel[1].value), std::max(_light_channel[2].value, _light_channel[3].value));
unsigned char max_in = getMax(_light_channel[0].inputValue, _light_channel[1].inputValue, _light_channel[2].inputValue);
unsigned char max_out = getMax(_light_channel[0].value, _light_channel[1].value, _light_channel[2].value, _light_channel[3].value, (_light_use_cold_white ? _light_channel[4].value : 0));
unsigned char channelSize = _light_use_cold_white ? 5 : 4;
double factor = (max_out > 0) ? (double) (max_in / max_out) : 0;
for (unsigned int i=0; i < 4; i++) {
for (unsigned char i=0; i < channelSize; i++) {
_light_channel[i].value = round((double) _light_channel[i].value * factor * brightness);
}
// Scale white channel to match brightness
_light_channel[3].value = constrain(_light_channel[3].value * LIGHT_WHITE_FACTOR, 0, 255);
for (unsigned char i=3; i < channelSize; i++) {
_light_channel[i].value = constrain(_light_channel[i].value * LIGHT_WHITE_FACTOR, 0, LIGHT_MAX_BRIGHTNESS);
}
// For the rest of channels, don't apply brightness, it is already in the inputValue:
for (unsigned char i=4; i < _light_channel.size(); i++) {
// For the rest of channels, don't apply brightness, it is already in the inputValue
// i should be 4 when RGBW and 5 when RGBWW
for (unsigned char i=channelSize; i < _light_channel.size(); i++) {
_light_channel[i].value = _light_channel[i].inputValue;
}
@ -261,9 +297,15 @@ void _fromKelvin(unsigned long kelvin, bool setMireds) {
if (!_light_has_color) return;
if (setMireds) {
_light_mireds = round(1000000UL / kelvin);
_light_mireds = constrain(round(1000000UL / kelvin), LIGHT_MIN_MIREDS, LIGHT_MAX_MIREDS);
}
if (_light_has_color && _light_use_cold_white) {
// _setRGBInputValue(LIGHT_MAX_VALUE, LIGHT_MAX_VALUE, LIGHT_MAX_VALUE);
return;
}
// Calculate colors
unsigned int red = (kelvin <= 66)
? LIGHT_MAX_VALUE
@ -277,11 +319,7 @@ void _fromKelvin(unsigned long kelvin, bool setMireds) {
? 0
: 138.5177312231 * log(kelvin - 10) - 305.0447927307);
_setRGBInputValue(
constrain(red, 0, LIGHT_MAX_VALUE),
constrain(green, 0, LIGHT_MAX_VALUE),
constrain(blue, 0, LIGHT_MAX_VALUE)
);
_setRGBInputValue(red, green, blue);
}
void _fromKelvin(unsigned long kelvin) {
@ -290,8 +328,13 @@ void _fromKelvin(unsigned long kelvin) {
// Color temperature is measured in mireds (kelvin = 1e6/mired)
void _fromMireds(unsigned long mireds) {
if (mireds == 0) mireds = 1;
_light_mireds = mireds;
_light_mireds = mireds = constrain(mireds, LIGHT_MIN_MIREDS, LIGHT_MAX_MIREDS);
if (_light_has_color && _light_use_cold_white) {
// _setRGBInputValue(LIGHT_MAX_VALUE, LIGHT_MAX_VALUE, LIGHT_MAX_VALUE);
return;
}
unsigned long kelvin = constrain(1000000UL / mireds, 1000, 40000) / 100;
_fromKelvin(kelvin, false);
}
@ -320,8 +363,8 @@ void _toHSV(char * hsv, size_t len) {
double g = (double) (_light_channel[1].inputValue * brightness) / 255.0;
double b = (double) (_light_channel[2].inputValue * brightness) / 255.0;
min = std::min(r, std::min(g, b));
max = std::max(r, std::max(g, b));
min = getMin(r, g, b);
max = getMax(r, g, b);
v = 100.0 * max;
if (v == 0) {
@ -441,6 +484,7 @@ void _lightColorSave() {
setSetting("ch", i, _light_channel[i].inputValue);
}
setSetting("brightness", _light_brightness);
setSetting("mireds", _light_mireds);
saveSettings();
}
@ -449,6 +493,7 @@ void _lightColorRestore() {
_light_channel[i].inputValue = getSetting("ch", i, i==0 ? 255 : 0).toInt();
}
_light_brightness = getSetting("brightness", LIGHT_MAX_BRIGHTNESS).toInt();
_light_mireds = getSetting("mireds", _light_mireds).toInt();
lightUpdate(false, false);
}
@ -744,6 +789,7 @@ void _lightWebSocketOnSend(JsonObject& root) {
root["mqttGroupColor"] = getSetting("mqttGroupColor");
root["useColor"] = _light_has_color;
root["useWhite"] = _light_use_white;
root["useColdWhite"] = _light_use_cold_white;
root["useGamma"] = _light_use_gamma;
root["useTransitions"] = _light_use_transitions;
root["lightTime"] = _light_transition_time;
@ -961,6 +1007,12 @@ void _lightConfigure() {
setSetting("useWhite", _light_use_white);
}
_light_use_cold_white = getSetting("useColdWhite", LIGHT_USE_COLD_WHITE).toInt() == 1;
if (_light_use_cold_white && ((_light_channel.size() < 5) || !_light_use_white)) {
_light_use_cold_white = false;
setSetting("useColdWhite", _light_use_cold_white);
}
_light_use_gamma = getSetting("useGamma", LIGHT_USE_GAMMA).toInt() == 1;
_light_use_transitions = getSetting("useTransitions", LIGHT_USE_TRANSITIONS).toInt() == 1;
_light_transition_time = getSetting("lightTime", LIGHT_TRANSITION_TIME).toInt();


+ 6
- 3
code/espurna/ntp.ino View File

@ -129,10 +129,8 @@ bool ntpSynced() {
return (year() > 2017);
}
String ntpDateTime() {
if (!ntpSynced()) return String();
String ntpDateTime(time_t t) {
char buffer[20];
time_t t = now();
snprintf_P(buffer, sizeof(buffer),
PSTR("%04d-%02d-%02d %02d:%02d:%02d"),
year(t), month(t), day(t), hour(t), minute(t), second(t)
@ -140,6 +138,11 @@ String ntpDateTime() {
return String(buffer);
}
String ntpDateTime() {
if (ntpSynced()) return ntpDateTime(now());
return String();
}
// -----------------------------------------------------------------------------
void ntpSetup() {


+ 2603
- 2599
code/espurna/static/index.html.gz.h
File diff suppressed because it is too large
View File


+ 9
- 1
code/html/custom.js View File

@ -13,6 +13,7 @@ var numReconnect = 0;
var numReload = 0;
var useWhite = false;
var useColdWhite = false;
var now = 0;
var ago = 0;
@ -838,6 +839,9 @@ function initChannels(num) {
max = num % 3;
if ((max > 0) & useWhite) {
max--;
if (useColdWhite) {
max--;
}
}
}
var start = num - max;
@ -1020,6 +1024,10 @@ function processData(data) {
useWhite = value;
}
if ("useColdWhite" === key) {
useColdWhite = value;
}
// ---------------------------------------------------------------------
// Sensors & Magnitudes
// ---------------------------------------------------------------------
@ -1303,7 +1311,7 @@ function initUrls(root) {
});
urls.ws.protocol = "ws";
}
function connectToURL(url) {


+ 14
- 6
code/html/index.html View File

@ -408,12 +408,20 @@
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useWhite" action="reload" tabindex="9" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">Use forth dimmable channel as white when first 3 have the same RGB value.<br />Will only work if the device has at least 4 dimmable channels.<br />Enabling this will render useless the "Channel 4" slider in the status page.<br />Reload the page to update the web interface.</div>
<div class="pure-u-1 pure-u-lg-3-4 hint">Use forth dimmable channel as (warm) white light calculated out of the RGB values.<br />Will only work if the device has at least 4 dimmable channels.<br />Enabling this will render useless the "Channel 4" slider in the status page.<br />Reload the page to update the web interface.</div>
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Use cold white channel</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useColdWhite" action="reload" tabindex="10" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">Use fifth dimmable channel as cold white light.<br />Will only work if the device has at least 5 dimmable channels and "white channel" above is also ON.<br />Enabling this will render useless the "Channel 5" slider in the status page.<br />Reload the page to update the web interface.</div>
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Use gamma correction</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useGamma" tabindex="10" /></div>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useGamma" tabindex="11" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">Use gamma correction for RGB channels.<br />Will only work if "use colorpicker" above is also ON.</div>
@ -421,7 +429,7 @@
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Use CSS style</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useCSS" tabindex="11" /></div>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useCSS" tabindex="12" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">Use CSS style to report colors to MQTT and REST API. <br />Red will be reported as "#FF0000" if ON, otherwise "255,0,0"</div>
@ -429,7 +437,7 @@
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Color transitions</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useTransitions" tabindex="12" /></div>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="useTransitions" tabindex="13" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">If enabled color changes will be smoothed.</div>
@ -437,7 +445,7 @@
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Transition time</label>
<div class="pure-u-1 pure-u-lg-1-4"><input class="pure-u-1" type="number" name="lightTime" min="10" max="5000" tabindex="13" /></div>
<div class="pure-u-1 pure-u-lg-1-4"><input class="pure-u-1" type="number" name="lightTime" min="10" max="5000" tabindex="14" /></div>
<div class="pure-u-0 pure-u-lg-1-2"></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">Time in millisecons to transition from one color to another.</div>
@ -445,7 +453,7 @@
<div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>MQTT group</label></div>
<div class="pure-u-1 pure-u-lg-3-4"><input name="mqttGroupColor" class="pure-u-1" tabindex="14" action="reconnect" /></div>
<div class="pure-u-1 pure-u-lg-3-4"><input name="mqttGroupColor" class="pure-u-1" tabindex="15" action="reconnect" /></div>
<div class="pure-u-0 pure-u-lg-1-4"></div>
<div class="pure-u-1 pure-u-lg-3-4 hint">Sync color between different lights.</div>
</div>


Loading…
Cancel
Save