Browse Source

Support for brightness and color/temperature MQTT topics

fastled
Xose Pérez 7 years ago
parent
commit
f9f7b6419e
4 changed files with 107 additions and 45 deletions
  1. +5
    -2
      code/espurna/config/general.h
  2. +97
    -40
      code/espurna/light.ino
  3. +2
    -1
      code/espurna/settings.ino
  4. +3
    -2
      code/espurna/web.ino

+ 5
- 2
code/espurna/config/general.h View File

@ -20,8 +20,8 @@
// To receive the message son the destination computer use nc: // To receive the message son the destination computer use nc:
// nc -ul 8111 // nc -ul 8111
//#define DEBUG_UDP_IP IPAddress(192, 168, 1, 100)
//#define DEBUG_UDP_PORT 8111
#define DEBUG_UDP_IP IPAddress(192, 168, 1, 100)
#define DEBUG_UDP_PORT 8113
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// EEPROM // EEPROM
@ -205,6 +205,8 @@ PROGMEM const char* const custom_reset_string[] = {
#define MQTT_TOPIC_RELAY "relay" #define MQTT_TOPIC_RELAY "relay"
#define MQTT_TOPIC_LED "led" #define MQTT_TOPIC_LED "led"
#define MQTT_TOPIC_COLOR "color" #define MQTT_TOPIC_COLOR "color"
#define MQTT_TOPIC_BRIGHTNESS "brightness"
#define MQTT_TOPIC_COLORTEMP "color/temperature"
#define MQTT_TOPIC_BUTTON "button" #define MQTT_TOPIC_BUTTON "button"
#define MQTT_TOPIC_IP "ip" #define MQTT_TOPIC_IP "ip"
#define MQTT_TOPIC_VERSION "version" #define MQTT_TOPIC_VERSION "version"
@ -274,6 +276,7 @@ PROGMEM const char* const custom_reset_string[] = {
#define LIGHT_DEFAULT_COLOR "#000080" #define LIGHT_DEFAULT_COLOR "#000080"
#define LIGHT_SAVE_DELAY 5 #define LIGHT_SAVE_DELAY 5
#define LIGHT_MAX_VALUE 255 #define LIGHT_MAX_VALUE 255
#define LIGHT_MAX_BRIGHTNESS LIGHT_MAX_VALUE
#define MY9291_DI_PIN 13 #define MY9291_DI_PIN 13
#define MY9291_DCKI_PIN 15 #define MY9291_DCKI_PIN 15


+ 97
- 40
code/espurna/light.ino View File

@ -11,6 +11,7 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
#include <Ticker.h> #include <Ticker.h>
Ticker colorTicker; Ticker colorTicker;
bool _lightState = false; bool _lightState = false;
float brightness = 1.0;
unsigned int _lightColor[3] = {0}; unsigned int _lightColor[3] = {0};
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192 #if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
@ -57,7 +58,7 @@ my9291 * _my9291;
// UTILS // UTILS
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void color_string2array(const char * rgb, unsigned int * array) {
void _color_string2array(const char * rgb, unsigned int * array) {
char * p = (char *) rgb; char * p = (char *) rgb;
if (strlen(p) == 0) return; if (strlen(p) == 0) return;
@ -67,16 +68,25 @@ void color_string2array(const char * rgb, unsigned int * array) {
++p; ++p;
unsigned long value = strtol(p, NULL, 16); unsigned long value = strtol(p, NULL, 16);
array[0] = (value >> 16) & 0xFF;
array[1] = (value >> 8) & 0xFF;
array[2] = (value) & 0xFF;
// RGBA values are interpreted like RGB + brightness
if (strlen(p) > 7) {
array[0] = (value >> 24) & 0xFF;
array[1] = (value >> 16) & 0xFF;
array[2] = (value >> 8) & 0xFF;
brightness =float(value & 0xFF) / 255;
} else {
array[0] = (value >> 16) & 0xFF;
array[1] = (value >> 8) & 0xFF;
array[2] = (value) & 0xFF;
}
// it's a temperature // it's a temperature
} else if (p[strlen(p)-1] == 'K') { } else if (p[strlen(p)-1] == 'K') {
p[strlen(p)-1] = 0; p[strlen(p)-1] = 0;
unsigned int temperature = atoi(p); unsigned int temperature = atoi(p);
color_temperature2array(temperature, array);
_color_temperature2array(temperature, array);
// otherwise assume decimal values separated by commas // otherwise assume decimal values separated by commas
} else { } else {
@ -105,16 +115,16 @@ void color_string2array(const char * rgb, unsigned int * array) {
} }
void color_array2rgb(unsigned int * array, char * rgb) {
unsigned long value = array[0];
value = (value << 8) + array[1];
value = (value << 8) + array[2];
void _color_array2rgb(unsigned int * array, float brightness, char * rgb) {
unsigned long value = array[0] * brightness;
value = (value << 8) + array[1] * brightness;
value = (value << 8) + array[2] * brightness;
sprintf(rgb, "#%06X", value); sprintf(rgb, "#%06X", value);
} }
// Thanks to Sacha Telgenhof for sharing this code in his AiLight library // Thanks to Sacha Telgenhof for sharing this code in his AiLight library
// https://github.com/stelgenhof/AiLight // https://github.com/stelgenhof/AiLight
void color_temperature2array(unsigned int temperature, unsigned int * array) {
void _color_temperature2array(unsigned int temperature, unsigned int * array) {
// Force boundaries and conversion // Force boundaries and conversion
temperature = constrain(temperature, 1000, 40000) / 100; temperature = constrain(temperature, 1000, 40000) / 100;
@ -136,19 +146,19 @@ void color_temperature2array(unsigned int temperature, unsigned int * array) {
array[0] = constrain(red, 0, LIGHT_MAX_VALUE); array[0] = constrain(red, 0, LIGHT_MAX_VALUE);
array[1] = constrain(green, 0, LIGHT_MAX_VALUE); array[1] = constrain(green, 0, LIGHT_MAX_VALUE);
array[2] = constrain(blue, 0, LIGHT_MAX_VALUE); array[2] = constrain(blue, 0, LIGHT_MAX_VALUE);
} }
// Converts a color intensity value (0..255) to a pwm value // Converts a color intensity value (0..255) to a pwm value
// This takes care of positive or negative logic
unsigned int _intensity2pwm(unsigned int intensity) {
unsigned int pwm;
// This takes care of positive or negative logic and brightness
unsigned int _intensity2pwm(unsigned int intensity, float brightness) {
intensity = brightness * intensity;
#if ENABLE_GAMMA_CORRECTION #if ENABLE_GAMMA_CORRECTION
pwm = (intensity < GAMMA_TABLE_SIZE) ? gamma_table[intensity] : LIGHT_PWM_RANGE;
unsigned int pwm = (intensity < GAMMA_TABLE_SIZE) ? gamma_table[intensity] : LIGHT_PWM_RANGE;
#else #else
// Support integer multiples of 256 (-1) for the LIGHT_PWM_RANGE
// The divide should happen at compile time
pwm = intensity * ( (LIGHT_PWM_RANGE+1) / (LIGHT_MAX_VALUE+1) );
unsigned int pwm = intensity;
#endif #endif
#if RGBW_INVERSE_LOGIC != 1 #if RGBW_INVERSE_LOGIC != 1
@ -156,13 +166,19 @@ unsigned int _intensity2pwm(unsigned int intensity) {
#endif #endif
return pwm; return pwm;
} }
unsigned int _intensity2pwm(unsigned int intensity) {
return _intensity2pwm(intensity, LIGHT_MAX_VALUE);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// PROVIDER // PROVIDER
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void _lightProviderSet(bool state, unsigned int red, unsigned int green, unsigned int blue) {
void _lightProviderSet(bool state, unsigned int red, unsigned int green, unsigned int blue, float brightness) {
unsigned int white = 0; unsigned int white = 0;
@ -176,7 +192,7 @@ void _lightProviderSet(bool state, unsigned int red, unsigned int green, unsigne
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192 #if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
_my9291->setState(state); _my9291->setState(state);
_my9291->setColor((my9291_color_t) { red, green, blue, white });
_my9291->setColor((my9291_color_t) { red * brightness, green * brightness, blue * brightness, white * brightness });
#endif #endif
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB2W) #if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB2W)
@ -184,16 +200,17 @@ void _lightProviderSet(bool state, unsigned int red, unsigned int green, unsigne
// Check state // Check state
if (!state) red = green = blue = white = 0; if (!state) red = green = blue = white = 0;
analogWrite(RGBW_RED_PIN, _intensity2pwm(red));
analogWrite(RGBW_GREEN_PIN, _intensity2pwm(green));
analogWrite(RGBW_BLUE_PIN, _intensity2pwm(blue));
analogWrite(RGBW_RED_PIN, _intensity2pwm(red, brightness));
analogWrite(RGBW_GREEN_PIN, _intensity2pwm(green, brightness));
analogWrite(RGBW_BLUE_PIN, _intensity2pwm(blue, brightness));
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW) #if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
analogWrite(RGBW_WHITE_PIN, _intensity2pwm(white));
analogWrite(RGBW_WHITE_PIN, _intensity2pwm(white, brightness));
#endif #endif
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB2W) #if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB2W)
analogWrite(RGBW_WHITE_PIN, _intensity2pwm(white));
analogWrite(RGBW_WHITE2_PIN, _intensity2pwm(white));
analogWrite(RGBW_WHITE_PIN, _intensity2pwm(white, brightness));
analogWrite(RGBW_WHITE2_PIN, _intensity2pwm(white,brightness));
#endif #endif
#endif #endif
} }
@ -204,52 +221,64 @@ void _lightProviderSet(bool state, unsigned int red, unsigned int green, unsigne
void lightState(bool state) { void lightState(bool state) {
_lightState = state; _lightState = state;
_lightProviderSet(_lightState, _lightColor[0], _lightColor[1], _lightColor[2]);
_lightProviderSet(_lightState, _lightColor[0], _lightColor[1], _lightColor[2], brightness);
} }
bool lightState() { bool lightState() {
return _lightState; return _lightState;
} }
void lightColor(const char * color, bool save, bool forward) {
void parseColor(const char * color) {
brightness = 1.0;
_color_string2array(color, _lightColor);
}
void lightColor(bool save, bool forward) {
color_string2array(color, _lightColor);
_lightProviderSet(_lightState, _lightColor[0], _lightColor[1], _lightColor[2]);
_lightProviderSet(_lightState, _lightColor[0], _lightColor[1], _lightColor[2], brightness);
char rgb[8]; char rgb[8];
color_array2rgb(_lightColor, rgb);
_color_array2rgb(_lightColor, brightness, rgb);
// Delay saving to EEPROM 5 seconds to avoid wearing it out unnecessarily // Delay saving to EEPROM 5 seconds to avoid wearing it out unnecessarily
if (save) colorTicker.once(LIGHT_SAVE_DELAY, _lightColorSave); if (save) colorTicker.once(LIGHT_SAVE_DELAY, _lightColorSave);
// Report color to MQTT broker // Report color to MQTT broker
if (forward) mqttSend(MQTT_TOPIC_COLOR, rgb);
if (forward) {
mqttSend(MQTT_TOPIC_COLOR, rgb);
}
// Report color to WS clients // Report color to WS clients
char message[20];
sprintf(message, "{\"color\": \"%s\"}", rgb);
char message[64];
sprintf(message, "{\"color\": \"%s\", \"brightness\": %d}", rgb, (int) (brightness * LIGHT_MAX_BRIGHTNESS));
wsSend(message); wsSend(message);
} }
String lightColor() {
String lightColor(float b) {
char rgb[8]; char rgb[8];
color_array2rgb(_lightColor, rgb);
_color_array2rgb(_lightColor, b, rgb);
return String(rgb); return String(rgb);
} }
String lightColor() {
return lightColor(brightness);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// PERSISTANCE // PERSISTANCE
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void _lightColorSave() { void _lightColorSave() {
setSetting("color", lightColor());
setSetting("color", lightColor(1.0));
setSetting("brightness", brightness * LIGHT_MAX_BRIGHTNESS);
saveSettings(); saveSettings();
} }
void _lightColorRestore() { void _lightColorRestore() {
String color = getSetting("color", LIGHT_DEFAULT_COLOR); String color = getSetting("color", LIGHT_DEFAULT_COLOR);
color_string2array(color.c_str(), _lightColor);
_color_string2array(color.c_str(), _lightColor);
brightness = getSetting("brightness", 1).toFloat() / LIGHT_MAX_BRIGHTNESS;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -260,6 +289,8 @@ void lightMQTTCallback(unsigned int type, const char * topic, const char * paylo
if (type == MQTT_CONNECT_EVENT) { if (type == MQTT_CONNECT_EVENT) {
mqttSubscribe(MQTT_TOPIC_BRIGHTNESS);
mqttSubscribe(MQTT_TOPIC_COLORTEMP);
mqttSubscribe(MQTT_TOPIC_COLOR); mqttSubscribe(MQTT_TOPIC_COLOR);
} }
@ -267,9 +298,27 @@ void lightMQTTCallback(unsigned int type, const char * topic, const char * paylo
// Match topic // Match topic
String t = mqttSubtopic((char *) topic); String t = mqttSubtopic((char *) topic);
if (!t.equals(MQTT_TOPIC_COLOR)) return;
lightColor(payload, true, mqttForward());
// Color temperature
if (t.equals(MQTT_TOPIC_COLORTEMP)) {
char buffer[10];
sprintf(buffer, "%sK", payload);
parseColor(buffer);
lightColor(true, mqttForward());
}
// Color
if (t.equals(MQTT_TOPIC_COLOR)) {
parseColor(payload);
lightColor(true, mqttForward());
}
// Brightness
if (t.equals(MQTT_TOPIC_BRIGHTNESS)) {
brightness = (float) atoi(payload) / LIGHT_MAX_BRIGHTNESS;
lightColor(true, mqttForward());
}
} }
@ -308,7 +357,15 @@ void lightSetup() {
snprintf(buffer, len, "%s", lightColor().c_str()); snprintf(buffer, len, "%s", lightColor().c_str());
}, },
[](const char * payload) { [](const char * payload) {
lightColor(payload, true, mqttForward());
parseColor(payload);
lightColor(true, mqttForward());
}
);
apiRegister(MQTT_TOPIC_BRIGHTNESS, MQTT_TOPIC_BRIGHTNESS,
NULL,
[](const char * payload) {
lightColor(true, mqttForward());
} }
); );


+ 2
- 1
code/espurna/settings.ino View File

@ -131,7 +131,8 @@ void settingsSetup() {
Embedis::command( F("COLOR"), [](Embedis* e) { Embedis::command( F("COLOR"), [](Embedis* e) {
if (e->argc > 1) { if (e->argc > 1) {
String color = String(e->argv[1]); String color = String(e->argv[1]);
lightColor(color.c_str(), true, true);
parseColor(color.c_str());
lightColor(true, true);
} }
e->stream->printf("Color: %s\n", lightColor().c_str()); e->stream->printf("Color: %s\n", lightColor().c_str());
e->response(Embedis::OK); e->response(Embedis::OK);


+ 3
- 2
code/espurna/web.ino View File

@ -143,7 +143,8 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
if (action.equals("color") && root.containsKey("data")) { if (action.equals("color") && root.containsKey("data")) {
lightColor(root["data"], true, true);
parseColor(root["data"]);
lightColor(true, true);
} }
#endif #endif
@ -780,7 +781,7 @@ void _onRPC(AsyncWebServerRequest *request) {
if (action.equals("reset")) { if (action.equals("reset")) {
response = 200; response = 200;
deferred.once_ms(100, []() {
deferred.once_ms(100, []() {
customReset(CUSTOM_RESET_RPC); customReset(CUSTOM_RESET_RPC);
ESP.restart(); ESP.restart();
}); });


Loading…
Cancel
Save