From 97c52ed1a00da71bb08d4a5c70d76a4b23e08c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xose=20P=C3=A9rez?= Date: Sun, 26 Nov 2017 22:42:19 +0100 Subject: [PATCH] Color transitions --- code/espurna/config/general.h | 4 + code/espurna/data/index.html.gz | Bin 60986 -> 60984 bytes code/espurna/light.ino | 352 +- code/espurna/static/index.html.gz.h | 5046 +++++++++++++-------------- 4 files changed, 2714 insertions(+), 2688 deletions(-) diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index fe9048cb..b8ea90b9 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -546,6 +546,10 @@ PROGMEM const char* const custom_reset_string[] = { #define LIGHT_USE_CSS 1 // Use CSS style to report colors (1=> "#FF0000", 0=> "255,0,0") #define LIGHT_USE_RGB 0 // Use RGB color selector (1=> RGB, 0=> HSV) +#define LIGHT_USE_TRANSITIONS 1 // Transitions between colors +#define LIGHT_TRANSITION_STEP 10 // Time in millis between each transtion step +#define LIGHT_TRANSITION_STEPS 50 // Number of steps to acomplish transition + // ----------------------------------------------------------------------------- // POWER METERING // ----------------------------------------------------------------------------- diff --git a/code/espurna/data/index.html.gz b/code/espurna/data/index.html.gz index b27b359464b3ce753ed266c20d110b92b10be4ba..ffba6668b927dfdfbaefb8851295f1dd4d0598dc 100644 GIT binary patch delta 14368 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 diff --git a/code/espurna/light.ino b/code/espurna/light.ino index 569cca6c..ec5ecf66 100644 --- a/code/espurna/light.ino +++ b/code/espurna/light.ino @@ -19,25 +19,36 @@ extern "C" { } #endif -Ticker colorTicker; +// ----------------------------------------------------------------------------- + +Ticker _light_save_ticker; +Ticker _light_transition_ticker; + typedef struct { unsigned char pin; bool reverse; - unsigned char value; - unsigned char shadow; + unsigned char value; // target or nominal value + unsigned char shadow; // represented value + double current; // transition value } channel_t; -std::vector _channels; -bool _lightState = false; -unsigned int _brightness = LIGHT_MAX_BRIGHTNESS; +std::vector _light_channel; + +bool _light_state = false; +bool _light_has_color = false; +bool _light_use_white = false; +bool _light_use_gamma = false; +unsigned long _light_steps_left = 0; +unsigned int _light_brightness = LIGHT_MAX_BRIGHTNESS; #if LIGHT_PROVIDER == LIGHT_PROVIDER_MY92XX #include my92xx * _my92xx; +ARRAYINIT(unsigned char, _light_channel_map, MY92XX_MAPPING); #endif // Gamma Correction lookup table (8 bit) // TODO: move to PROGMEM -const unsigned char gamma_table[] = { +const unsigned char _light_gamma_table[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, @@ -63,14 +74,14 @@ const unsigned char gamma_table[] = { void _fromLong(unsigned long value, bool brightness) { if (brightness) { - _channels[0].value = (value >> 24) & 0xFF; - _channels[1].value = (value >> 16) & 0xFF; - _channels[2].value = (value >> 8) & 0xFF; - _brightness = (value & 0xFF) * LIGHT_MAX_BRIGHTNESS / 255; + _light_channel[0].value = (value >> 24) & 0xFF; + _light_channel[1].value = (value >> 16) & 0xFF; + _light_channel[2].value = (value >> 8) & 0xFF; + _light_brightness = (value & 0xFF) * LIGHT_MAX_BRIGHTNESS / 255; } else { - _channels[0].value = (value >> 16) & 0xFF; - _channels[1].value = (value >> 8) & 0xFF; - _channels[2].value = (value) & 0xFF; + _light_channel[0].value = (value >> 16) & 0xFF; + _light_channel[1].value = (value >> 8) & 0xFF; + _light_channel[2].value = (value) & 0xFF; } } @@ -83,7 +94,7 @@ void _fromRGB(const char * rgb) { // if color begins with a # then assume HEX RGB if (p[0] == '#') { - if (lightHasColor()) { + if (_light_has_color) { ++p; unsigned long value = strtoul(p, NULL, 16); @@ -96,7 +107,7 @@ void _fromRGB(const char * rgb) { // it's a temperature in mireds } else if (p[0] == 'M') { - if (lightHasColor()) { + if (_light_has_color) { unsigned long mireds = atol(p + 1); _fromMireds(mireds); } @@ -104,7 +115,7 @@ void _fromRGB(const char * rgb) { // it's a temperature in kelvin } else if (p[0] == 'K') { - if (lightHasColor()) { + if (_light_has_color) { unsigned long kelvin = atol(p + 1); _fromKelvin(kelvin); } @@ -114,19 +125,19 @@ void _fromRGB(const char * rgb) { char * tok; unsigned char count = 0; - unsigned char channels = _channels.size(); + unsigned char channels = _light_channel.size(); tok = strtok(p, ","); while (tok != NULL) { - _channels[count].value = atoi(tok); + _light_channel[count].value = atoi(tok); if (++count == channels) break; tok = strtok(NULL, ","); } // RGB but less than 3 values received - if (lightHasColor() && (count < 3)) { - _channels[1].value = _channels[0].value; - _channels[2].value = _channels[0].value; + if (_light_has_color && (count < 3)) { + _light_channel[1].value = _light_channel[0].value; + _light_channel[2].value = _light_channel[0].value; } } @@ -135,17 +146,17 @@ void _fromRGB(const char * rgb) { void _toRGB(char * rgb, size_t len, bool applyBrightness) { - if (!lightHasColor()) return; + if (!_light_has_color) return; - float b = applyBrightness ? (float) _brightness / LIGHT_MAX_BRIGHTNESS : 1; + float b = applyBrightness ? (float) _light_brightness / LIGHT_MAX_BRIGHTNESS : 1; unsigned long value = 0; - value += _channels[0].value * b; + value += _light_channel[0].value * b; value <<= 8; - value += _channels[1].value * b; + value += _light_channel[1].value * b; value <<= 8; - value += _channels[2].value * b; + value += _light_channel[2].value * b; snprintf_P(rgb, len, PSTR("#%06X"), value); @@ -163,7 +174,7 @@ void _fromHSV(const char * hsv) { char * ptr = (char *) hsv; if (strlen(ptr) == 0) return; - if (!lightHasColor()) return; + if (!_light_has_color) return; char * tok; unsigned char count = 0; @@ -189,56 +200,56 @@ void _fromHSV(const char * hsv) { switch (int(h)) { case 0: - _channels[0].value = v; - _channels[1].value = t; - _channels[2].value = p; + _light_channel[0].value = v; + _light_channel[1].value = t; + _light_channel[2].value = p; break; case 1: - _channels[0].value = q; - _channels[1].value = v; - _channels[2].value = p; + _light_channel[0].value = q; + _light_channel[1].value = v; + _light_channel[2].value = p; break; case 2: - _channels[0].value = p; - _channels[1].value = v; - _channels[2].value = t; + _light_channel[0].value = p; + _light_channel[1].value = v; + _light_channel[2].value = t; break; case 3: - _channels[0].value = p; - _channels[1].value = q; - _channels[2].value = v; + _light_channel[0].value = p; + _light_channel[1].value = q; + _light_channel[2].value = v; break; case 4: - _channels[0].value = t; - _channels[1].value = p; - _channels[2].value = v; + _light_channel[0].value = t; + _light_channel[1].value = p; + _light_channel[2].value = v; break; case 5: - _channels[0].value = v; - _channels[1].value = p; - _channels[2].value = q; + _light_channel[0].value = v; + _light_channel[1].value = p; + _light_channel[2].value = q; break; default: - _channels[0].value = 0; - _channels[1].value = 0; - _channels[2].value = 0; + _light_channel[0].value = 0; + _light_channel[1].value = 0; + _light_channel[2].value = 0; break; } - _brightness = LIGHT_MAX_BRIGHTNESS; + _light_brightness = LIGHT_MAX_BRIGHTNESS; } void _toHSV(char * hsv, size_t len) { - if (!lightHasColor()) return; + if (!_light_has_color) return; double min, max; double h, s, v; - double r = (double) _channels[0].value / 255.0; - double g = (double) _channels[1].value / 255.0; - double b = (double) _channels[2].value / 255.0; + double r = (double) _light_channel[0].value / 255.0; + double g = (double) _light_channel[1].value / 255.0; + double b = (double) _light_channel[2].value / 255.0; min = (r < g) ? r : g; min = (min < b) ? min : b; @@ -281,14 +292,14 @@ void _toHSV(char * hsv, size_t len) { void _toLong(char * color, size_t len, bool applyBrightness) { - if (!lightHasColor()) return; + if (!_light_has_color) return; - float b = applyBrightness ? (float) _brightness / LIGHT_MAX_BRIGHTNESS : 1; + float b = applyBrightness ? (float) _light_brightness / LIGHT_MAX_BRIGHTNESS : 1; snprintf_P(color, len, PSTR("%d,%d,%d"), - (int) (_channels[0].value * b), - (int) (_channels[1].value * b), - (int) (_channels[2].value * b) + (int) (_light_channel[0].value * b), + (int) (_light_channel[1].value * b), + (int) (_light_channel[2].value * b) ); } @@ -302,7 +313,7 @@ void _toLong(char * color, size_t len) { void _fromKelvin(unsigned long kelvin) { // Check we have RGB channels - if (!lightHasColor()) return; + if (!_light_has_color) return; // Calculate colors unsigned int red = (kelvin <= 66) @@ -318,9 +329,9 @@ void _fromKelvin(unsigned long kelvin) { : 138.5177312231 * log(kelvin - 10) - 305.0447927307); // Save values - _channels[0].value = constrain(red, 0, LIGHT_MAX_VALUE); - _channels[1].value = constrain(green, 0, LIGHT_MAX_VALUE); - _channels[2].value = constrain(blue, 0, LIGHT_MAX_VALUE); + _light_channel[0].value = constrain(red, 0, LIGHT_MAX_VALUE); + _light_channel[1].value = constrain(green, 0, LIGHT_MAX_VALUE); + _light_channel[2].value = constrain(blue, 0, LIGHT_MAX_VALUE); } @@ -333,8 +344,8 @@ void _fromMireds(unsigned long mireds) { unsigned int _toPWM(unsigned long value, bool bright, bool gamma, bool reverse) { value = constrain(value, 0, LIGHT_MAX_VALUE); - if (bright) value *= ((float) _brightness / LIGHT_MAX_BRIGHTNESS); - if (gamma) value = gamma_table[value]; + if (bright) value *= ((float) _light_brightness / LIGHT_MAX_BRIGHTNESS); + if (gamma) value = _light_gamma_table[value]; if (LIGHT_MAX_VALUE != LIGHT_LIMIT_PWM) value = map(value, 0, LIGHT_MAX_VALUE, 0, LIGHT_LIMIT_PWM); if (reverse) value = LIGHT_LIMIT_PWM - value; return value; @@ -342,11 +353,11 @@ unsigned int _toPWM(unsigned long value, bool bright, bool gamma, bool reverse) // Returns a PWM valule for the given channel ID unsigned int _toPWM(unsigned char id) { - if (id < _channels.size()) { - bool isColor = lightHasColor() && (id < 3); + if (id < _light_channel.size()) { + bool isColor = _light_has_color && (id < 3); bool bright = isColor; - bool gamma = isColor & (getSetting("useGamma", LIGHT_USE_GAMMA).toInt() == 1); - return _toPWM(_channels[id].shadow, bright, gamma, _channels[id].reverse); + bool gamma = isColor & _light_use_gamma; + return _toPWM(_light_channel[id].shadow, bright, gamma, _light_channel[id].reverse); } return 0; } @@ -357,23 +368,34 @@ unsigned int _toPWM(unsigned char id) { void _shadow() { - for (unsigned int i=0; i < _channels.size(); i++) { - _channels[i].shadow = _lightState ? _channels[i].value : 0; + // Transitions + unsigned char target; + for (unsigned int i=0; i < _light_channel.size(); i++) { + target = _light_state ? _light_channel[i].value : 0; + if (_light_steps_left == 0) { + _light_channel[i].current = target; + } else { + double difference = (double) (target - _light_channel[i].current) / (_light_steps_left + 1); + _light_channel[i].current = _light_channel[i].current + difference; + } + _light_channel[i].shadow = _light_channel[i].current; } - if (lightHasColor()) { - - bool useWhite = getSetting("useWhite", LIGHT_USE_WHITE).toInt() == 1; + // Update transition ticker + if (_light_steps_left == 0) { + _light_transition_ticker.detach(); + } else { + _light_steps_left--; + } - if (_lightState && useWhite && (_channels.size() > 3)) { - if (_channels[0].shadow == _channels[1].shadow && _channels[1].shadow == _channels[2].shadow ) { - _channels[3].shadow = _channels[0].shadow * ((float) _brightness / LIGHT_MAX_BRIGHTNESS); - _channels[2].shadow = 0; - _channels[1].shadow = 0; - _channels[0].shadow = 0; - } + // Use white channel for same RGB + if (_light_use_white && _light_has_color) { + if (_light_channel[0].shadow == _light_channel[1].shadow && _light_channel[1].shadow == _light_channel[2].shadow ) { + _light_channel[3].shadow = _light_channel[0].shadow * ((float) _light_brightness / LIGHT_MAX_BRIGHTNESS); + _light_channel[2].shadow = 0; + _light_channel[1].shadow = 0; + _light_channel[0].shadow = 0; } - } } @@ -383,24 +405,22 @@ void _lightProviderUpdate() { _shadow(); #ifdef LIGHT_ENABLE_PIN - digitalWrite(LIGHT_ENABLE_PIN, _lightState); + digitalWrite(LIGHT_ENABLE_PIN, _light_state); #endif #if LIGHT_PROVIDER == LIGHT_PROVIDER_MY92XX - ARRAYINIT(unsigned char, channels, MY92XX_MAPPING); - - for (unsigned char i=0; i<_channels.size(); i++) { - _my92xx->setChannel(channels[i], _toPWM(i)); + for (unsigned char i=0; i<_light_channel.size(); i++) { + _my92xx->setChannel(_light_channel_map[i], _toPWM(i)); } - _my92xx->setState(_lightState); + _my92xx->setState(true); _my92xx->update(); #endif #if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER - for (unsigned int i=0; i < _channels.size(); i++) { + for (unsigned int i=0; i < _light_channel.size(); i++) { pwm_set_duty(_toPWM(i), i); } pwm_start(); @@ -414,18 +434,18 @@ void _lightProviderUpdate() { // ----------------------------------------------------------------------------- void _lightColorSave() { - for (unsigned int i=0; i < _channels.size(); i++) { - setSetting("ch", i, _channels[i].value); + for (unsigned int i=0; i < _light_channel.size(); i++) { + setSetting("ch", i, _light_channel[i].value); } - setSetting("brightness", _brightness); + setSetting("brightness", _light_brightness); saveSettings(); } void _lightColorRestore() { - for (unsigned int i=0; i < _channels.size(); i++) { - _channels[i].value = getSetting("ch", i, i==0 ? 255 : 0).toInt(); + for (unsigned int i=0; i < _light_channel.size(); i++) { + _light_channel[i].value = getSetting("ch", i, i==0 ? 255 : 0).toInt(); } - _brightness = getSetting("brightness", LIGHT_MAX_BRIGHTNESS).toInt(); + _light_brightness = getSetting("brightness", LIGHT_MAX_BRIGHTNESS).toInt(); lightUpdate(false, false); } @@ -439,7 +459,7 @@ void _lightMQTTCallback(unsigned int type, const char * topic, const char * payl if (type == MQTT_CONNECT_EVENT) { - if (lightHasColor()) { + if (_light_has_color) { mqttSubscribe(MQTT_TOPIC_BRIGHTNESS); mqttSubscribe(MQTT_TOPIC_MIRED); mqttSubscribe(MQTT_TOPIC_KELVIN); @@ -483,14 +503,14 @@ void _lightMQTTCallback(unsigned int type, const char * topic, const char * payl // Brightness if (t.equals(MQTT_TOPIC_BRIGHTNESS)) { - _brightness = constrain(atoi(payload), 0, LIGHT_MAX_BRIGHTNESS); + _light_brightness = constrain(atoi(payload), 0, LIGHT_MAX_BRIGHTNESS); lightUpdate(true, mqttForward()); } // Channel if (t.startsWith(MQTT_TOPIC_CHANNEL)) { unsigned int channelID = t.substring(strlen(MQTT_TOPIC_CHANNEL)+1).toInt(); - if (channelID >= _channels.size()) { + if (channelID >= _light_channel.size()) { DEBUG_MSG_P(PSTR("[LIGHT] Wrong channelID (%d)\n"), channelID); return; } @@ -506,7 +526,7 @@ void lightMQTT() { char buffer[12]; - if (lightHasColor()) { + if (_light_has_color) { // Color if (getSetting("useCSS", LIGHT_USE_CSS).toInt() == 1) { @@ -520,14 +540,14 @@ void lightMQTT() { mqttSend(MQTT_TOPIC_COLOR_HSV, buffer); // Brightness - snprintf_P(buffer, sizeof(buffer), PSTR("%d"), _brightness); + snprintf_P(buffer, sizeof(buffer), PSTR("%d"), _light_brightness); mqttSend(MQTT_TOPIC_BRIGHTNESS, buffer); } // Channels - for (unsigned int i=0; i < _channels.size(); i++) { - snprintf_P(buffer, sizeof(buffer), PSTR("%d"), _channels[i].value); + for (unsigned int i=0; i < _light_channel.size(); i++) { + snprintf_P(buffer, sizeof(buffer), PSTR("%d"), _light_channel[i].value); mqttSend(MQTT_TOPIC_CHANNEL, i, buffer); } @@ -540,21 +560,26 @@ void lightMQTT() { // ----------------------------------------------------------------------------- unsigned char lightChannels() { - return _channels.size(); + return _light_channel.size(); } bool lightHasColor() { - bool useColor = getSetting("useColor", LIGHT_USE_COLOR).toInt() == 1; - return useColor && (_channels.size() > 2); + return _light_has_color; } unsigned char lightWhiteChannels() { - return _channels.size() % 3; + return _light_channel.size() % 3; } void lightUpdate(bool save, bool forward) { - _lightProviderUpdate(); + #if LIGHT_USE_TRANSITIONS + _light_steps_left = LIGHT_TRANSITION_STEPS; + _light_transition_ticker.attach_ms(LIGHT_TRANSITION_STEP, _lightProviderUpdate); + #else + _light_steps_left = 0; + _lightProviderUpdate(); + #endif // Report color & brightness to MQTT broker #if MQTT_SUPPORT @@ -563,35 +588,12 @@ void lightUpdate(bool save, bool forward) { // Report color to WS clients (using current brightness setting) #if WEB_SUPPORT - { - DynamicJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - root["colorVisible"] = 1; - root["useColor"] = getSetting("useColor", LIGHT_USE_COLOR).toInt() == 1; - root["useWhite"] = getSetting("useWhite", LIGHT_USE_WHITE).toInt() == 1; - root["useGamma"] = getSetting("useGamma", LIGHT_USE_GAMMA).toInt() == 1; - if (lightHasColor()) { - bool useRGB = getSetting("useRGB", LIGHT_USE_RGB).toInt() == 1; - if (useRGB) { - root["rgb"] = lightColor(true); - root["brightness"] = lightBrightness(); - } else { - root["hsv"] = lightColor(false); - } - } - JsonArray& channels = root.createNestedArray("channels"); - for (unsigned char id=0; id < lightChannels(); id++) { - channels.add(lightChannel(id)); - } - String output; - root.printTo(output); - wsSend(output.c_str()); - } + wsSend(_lightWebSocketOnSend); #endif #if LIGHT_SAVE_ENABLED // Delay saving to EEPROM 5 seconds to avoid wearing it out unnecessarily - if (save) colorTicker.once(LIGHT_SAVE_DELAY, _lightColorSave); + if (save) _light_save_ticker.once(LIGHT_SAVE_DELAY, _lightColorSave); #endif }; @@ -603,11 +605,11 @@ void lightSave() { #endif void lightState(bool state) { - _lightState = state; + _light_state = state; } bool lightState() { - return _lightState; + return _light_state; } void lightColor(const char * color, bool rgb) { @@ -642,28 +644,28 @@ String lightColor() { } unsigned int lightChannel(unsigned char id) { - if (id <= _channels.size()) { - return _channels[id].value; + if (id <= _light_channel.size()) { + return _light_channel[id].value; } return 0; } void lightChannel(unsigned char id, unsigned int value) { - if (id <= _channels.size()) { - _channels[id].value = constrain(value, 0, LIGHT_MAX_VALUE); + if (id <= _light_channel.size()) { + _light_channel[id].value = constrain(value, 0, LIGHT_MAX_VALUE); } } unsigned int lightBrightness() { - return _brightness; + return _light_brightness; } void lightBrightness(int b) { - _brightness = constrain(b, 0, LIGHT_MAX_BRIGHTNESS); + _light_brightness = constrain(b, 0, LIGHT_MAX_BRIGHTNESS); } void lightBrightnessStep(int steps) { - lightBrightness(_brightness + steps * LIGHT_STEP); + lightBrightness(_light_brightness + steps * LIGHT_STEP); } // ----------------------------------------------------------------------------- @@ -674,13 +676,13 @@ void lightBrightnessStep(int steps) { void _lightWebSocketOnSend(JsonObject& root) { root["colorVisible"] = 1; - root["useColor"] = getSetting("useColor", LIGHT_USE_COLOR).toInt() == 1; - root["useWhite"] = getSetting("useWhite", LIGHT_USE_WHITE).toInt() == 1; - root["useGamma"] = getSetting("useGamma", LIGHT_USE_GAMMA).toInt() == 1; + root["useColor"] = _light_has_color; + root["useWhite"] = _light_use_white; + root["useGamma"] = _light_use_gamma; root["useCSS"] = getSetting("useCSS", LIGHT_USE_CSS).toInt() == 1; bool useRGB = getSetting("useRGB", LIGHT_USE_RGB).toInt() == 1; root["useRGB"] = useRGB; - if (lightHasColor()) { + if (_light_has_color) { if (useRGB) { root["rgb"] = lightColor(true); root["brightness"] = lightBrightness(); @@ -696,11 +698,10 @@ void _lightWebSocketOnSend(JsonObject& root) { void _lightWebSocketOnAction(const char * action, JsonObject& data) { - if (lightHasColor()) { + if (_light_has_color) { if (strcmp(action, "color") == 0) { if (data.containsKey("rgb")) { - Serial.printf(data["rgb"]); lightColor(data["rgb"], true); lightUpdate(true, true); } @@ -728,7 +729,7 @@ void _lightWebSocketOnAction(const char * action, JsonObject& data) { void _lightAPISetup() { // API entry points (protected with apikey) - if (lightHasColor()) { + if (_light_has_color) { // DEPRECATE apiRegister(MQTT_TOPIC_COLOR, MQTT_TOPIC_COLOR, @@ -771,7 +772,7 @@ void _lightAPISetup() { apiRegister(MQTT_TOPIC_BRIGHTNESS, MQTT_TOPIC_BRIGHTNESS, [](char * buffer, size_t len) { - snprintf_P(buffer, len, PSTR("%d"), _brightness); + snprintf_P(buffer, len, PSTR("%d"), _light_brightness); }, [](const char * payload) { lightBrightness(atoi(payload)); @@ -846,6 +847,24 @@ unsigned long getIOFunc(unsigned long gpio) { #endif +void _lightConfigure() { + + _light_has_color = getSetting("useColor", LIGHT_USE_COLOR).toInt() == 1; + if (_light_has_color && (_light_channel.size() < 3)) { + _light_has_color = false; + setSetting("useColor", _light_has_color); + } + + _light_use_white = getSetting("useWhite", LIGHT_USE_WHITE).toInt() == 1; + if (_light_use_white && (_light_channel.size() < 4)) { + _light_use_white = false; + setSetting("useWhite", _light_use_white); + } + + _light_use_gamma = getSetting("useGamma", LIGHT_USE_GAMMA).toInt() == 1; + +} + void lightSetup() { #ifdef LIGHT_ENABLE_PIN @@ -856,7 +875,7 @@ void lightSetup() { _my92xx = new my92xx(MY92XX_MODEL, MY92XX_CHIPS, MY92XX_DI_PIN, MY92XX_DCKI_PIN, MY92XX_COMMAND); for (unsigned char i=0; i