mirror of
https://github.com/xoseperez/espurna.git
synced 2026-03-04 15:34:19 +01:00
Change the way dimmer light work and a whole lot of other stuff
This commit is contained in:
@@ -50,7 +50,7 @@
|
||||
#define HEARTBEAT_REPORT_FREEHEAP 1
|
||||
#define HEARTBEAT_REPORT_VCC 1
|
||||
#define HEARTBEAT_REPORT_RELAY 1
|
||||
#define HEARTBEAT_REPORT_COLOR 1
|
||||
#define HEARTBEAT_REPORT_LIGHT 1
|
||||
#define HEARTBEAT_REPORT_HOSTNAME 1
|
||||
#define HEARTBEAT_REPORT_APP 1
|
||||
#define HEARTBEAT_REPORT_VERSION 1
|
||||
@@ -187,6 +187,7 @@ PROGMEM const char* const custom_reset_string[] = {
|
||||
#define WEBSERVER_PORT 80 // HTTP port
|
||||
#define DNS_PORT 53 // MDNS port
|
||||
#define ENABLE_MDNS 1 // Enabled MDNS
|
||||
#define API_BUFFER_SIZE 10 // Size of the buffer for HTTP GET API responses
|
||||
|
||||
#define WEB_MODE_NORMAL 0
|
||||
#define WEB_MODE_PASSWORD 1
|
||||
@@ -234,10 +235,6 @@ PROGMEM const char* const custom_reset_string[] = {
|
||||
#define MQTT_TOPIC_ACTION "action"
|
||||
#define MQTT_TOPIC_RELAY "relay"
|
||||
#define MQTT_TOPIC_LED "led"
|
||||
#define MQTT_TOPIC_COLOR "color"
|
||||
#define MQTT_TOPIC_WHITE "white"
|
||||
#define MQTT_TOPIC_BRIGHTNESS "brightness"
|
||||
#define MQTT_TOPIC_COLORTEMP "color/temperature"
|
||||
#define MQTT_TOPIC_BUTTON "button"
|
||||
#define MQTT_TOPIC_IP "ip"
|
||||
#define MQTT_TOPIC_VERSION "version"
|
||||
@@ -253,6 +250,13 @@ PROGMEM const char* const custom_reset_string[] = {
|
||||
#define MQTT_TOPIC_TIME "time"
|
||||
#define MQTT_TOPIC_ANALOG "analog"
|
||||
|
||||
// Lights
|
||||
#define MQTT_TOPIC_CHANNEL "channel"
|
||||
#define MQTT_TOPIC_COLOR "color"
|
||||
#define MQTT_TOPIC_BRIGHTNESS "brightness"
|
||||
#define MQTT_TOPIC_MIRED "mired"
|
||||
#define MQTT_TOPIC_KELVIN "kelvin"
|
||||
|
||||
#define MQTT_STATUS_ONLINE "1" // Value for the device ON message
|
||||
#define MQTT_STATUS_OFFLINE "0" // Value for the device OFF message (will)
|
||||
|
||||
@@ -282,31 +286,28 @@ PROGMEM const char* const custom_reset_string[] = {
|
||||
// LIGHT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define ENABLE_GAMMA_CORRECTION 0
|
||||
|
||||
#define LIGHT_PROVIDER_NONE 0
|
||||
#define LIGHT_PROVIDER_WS2812 1
|
||||
#define LIGHT_PROVIDER_RGB 2
|
||||
#define LIGHT_PROVIDER_RGBW 3
|
||||
#define LIGHT_PROVIDER_MY9192 4
|
||||
#define LIGHT_PROVIDER_RGB2W 5
|
||||
#define LIGHT_PROVIDER_MY9192 1
|
||||
#define LIGHT_PROVIDER_DIMMER 2
|
||||
|
||||
#define LIGHT_DEFAULT_COLOR "#000080"
|
||||
#define LIGHT_SAVE_DELAY 5
|
||||
#define LIGHT_MAX_VALUE 255
|
||||
#define LIGHT_MAX_BRIGHTNESS LIGHT_MAX_VALUE
|
||||
// LIGHT_PROVIDER_DIMMER can have from 1 to 5 different channels.
|
||||
// They have to be defined for each device in the hardware.h file.
|
||||
// If 3 or more channels first 3 will be considered RGB.
|
||||
// Usual configurations are:
|
||||
// 1 channels => W
|
||||
// 2 channels => WW
|
||||
// 3 channels => RGB
|
||||
// 4 channels => RGBW
|
||||
// 5 channels => RGBWW
|
||||
|
||||
// Settings for MY9291 bulbs (AI Light)
|
||||
#define MY9291_DI_PIN 13
|
||||
#define MY9291_DCKI_PIN 15
|
||||
#define MY9291_COMMAND MY9291_COMMAND_DEFAULT
|
||||
|
||||
// Shared settings between RGB and RGBW lights
|
||||
#define RGBW_INVERSE_LOGIC 1
|
||||
#define RGBW_RED_PIN 14
|
||||
#define RGBW_GREEN_PIN 5
|
||||
#define RGBW_BLUE_PIN 12
|
||||
#define RGBW_WHITE_PIN 13
|
||||
#define LIGHT_DEFAULT_COLOR "#000080" // Default start color
|
||||
#define LIGHT_SAVE_DELAY 5 // Persist color after 5 seconds to avoid wearing out
|
||||
#define LIGHT_PWM_FREQUENCY 1000 // PWM frequency
|
||||
#define LIGHT_MAX_PWM 4095 // Maximum PWM value
|
||||
#define LIGHT_MAX_VALUE 255 // Maximum light value
|
||||
#define LIGHT_MAX_BRIGHTNESS 255 // Maximun brightness value
|
||||
#define LIGHT_USE_WHITE 0 // Use white channel whenever RGB have the same value
|
||||
#define LIGHT_ENABLE_GAMMA 0 // Enable gamma correction
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// DOMOTICZ
|
||||
|
||||
@@ -270,6 +270,10 @@
|
||||
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
|
||||
#define LIGHT_PROVIDER LIGHT_PROVIDER_MY9192
|
||||
|
||||
#define MY9291_DI_PIN 13
|
||||
#define MY9291_DCKI_PIN 15
|
||||
#define MY9291_COMMAND MY9291_COMMAND_DEFAULT
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// LED Controller
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -281,19 +285,17 @@
|
||||
#define LED1_PIN 2
|
||||
#define LED1_PIN_INVERSE 1
|
||||
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
|
||||
#define LIGHT_PROVIDER LIGHT_PROVIDER_RGB
|
||||
#define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER
|
||||
|
||||
#undef RGBW_INVERSE_LOGIC
|
||||
#undef RGBW_RED_PIN
|
||||
#undef RGBW_GREEN_PIN
|
||||
#undef RGBW_BLUE_PIN
|
||||
#undef RGBW_WHITE_PIN
|
||||
#define LIGHT_CH1_PIN 14 // RED
|
||||
#define LIGHT_CH2_PIN 5 // GREEN
|
||||
#define LIGHT_CH3_PIN 12 // BLUE
|
||||
#define LIGHT_CH4_PIN 13 // WHITE
|
||||
|
||||
#define RGBW_INVERSE_LOGIC 1
|
||||
#define RGBW_RED_PIN 14
|
||||
#define RGBW_GREEN_PIN 5
|
||||
#define RGBW_BLUE_PIN 12
|
||||
#define RGBW_WHITE_PIN 13
|
||||
#define LIGHT_CH1_INVERSE 1
|
||||
#define LIGHT_CH2_INVERSE 1
|
||||
#define LIGHT_CH3_INVERSE 1
|
||||
#define LIGHT_CH4_INVERSE 1
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// HUACANXING H801
|
||||
@@ -306,20 +308,19 @@
|
||||
#define LED1_PIN 5
|
||||
#define LED1_PIN_INVERSE 1
|
||||
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
|
||||
#define LIGHT_PROVIDER LIGHT_PROVIDER_RGB2W
|
||||
#define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER
|
||||
|
||||
#undef RGBW_INVERSE_LOGIC
|
||||
#undef RGBW_RED_PIN
|
||||
#undef RGBW_GREEN_PIN
|
||||
#undef RGBW_BLUE_PIN
|
||||
#undef RGBW_WHITE_PIN
|
||||
#define LIGHT_CH1_PIN 15 // RED
|
||||
#define LIGHT_CH2_PIN 13 // GREEN
|
||||
#define LIGHT_CH3_PIN 12 // BLUE
|
||||
#define LIGHT_CH4_PIN 14 // WHITE1
|
||||
#define LIGHT_CH5_PIN 4 // WHITE2
|
||||
|
||||
#define RGBW_INVERSE_LOGIC 1
|
||||
#define RGBW_RED_PIN 15
|
||||
#define RGBW_GREEN_PIN 13
|
||||
#define RGBW_BLUE_PIN 12
|
||||
#define RGBW_WHITE_PIN 14
|
||||
#define RGBW_WHITE2_PIN 4
|
||||
#define LIGHT_CH1_INVERSE 0
|
||||
#define LIGHT_CH2_INVERSE 0
|
||||
#define LIGHT_CH3_INVERSE 0
|
||||
#define LIGHT_CH4_INVERSE 0
|
||||
#define LIGHT_CH5_INVERSE 0
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Jan Goedeke Wifi Relay
|
||||
|
||||
@@ -88,10 +88,8 @@ void heartbeat() {
|
||||
#if (HEARTBEAT_REPORT_RELAY)
|
||||
relayMQTT();
|
||||
#endif
|
||||
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
|
||||
#if (HEARTBEAT_REPORT_COLOR)
|
||||
mqttSend(MQTT_TOPIC_COLOR, lightColor().c_str());
|
||||
#endif
|
||||
#if (LIGHT_PROVIDER != LIGHT_PROVIDER_NONE) & (HEARTBEAT_REPORT_LIGHT)
|
||||
lightMQTT();
|
||||
#endif
|
||||
#if (HEARTBEAT_REPORT_VCC)
|
||||
#if ENABLE_ADC_VCC
|
||||
@@ -250,6 +248,7 @@ void setup() {
|
||||
|
||||
// Prepare configuration for version 2.0
|
||||
hwUpwardsCompatibility();
|
||||
//settingsDump();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -13,17 +13,6 @@ the migration to future version 2 will be straigh forward.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#define RELAY_PROVIDER_RELAY 0
|
||||
#define RELAY_PROVIDER_DUAL 1
|
||||
#define RELAY_PROVIDER_LIGHT 2
|
||||
|
||||
#define LIGHT_PROVIDER_NONE 0
|
||||
#define LIGHT_PROVIDER_WS2812 1
|
||||
#define LIGHT_PROVIDER_RGB 2
|
||||
#define LIGHT_PROVIDER_RGBW 3
|
||||
#define LIGHT_PROVIDER_MY9192 4
|
||||
|
||||
void hwUpwardsCompatibility() {
|
||||
|
||||
unsigned int board = getSetting("board", 0).toInt();
|
||||
@@ -234,14 +223,17 @@ void hwUpwardsCompatibility() {
|
||||
#ifdef LED_CONTROLLER
|
||||
setSetting("board", 21);
|
||||
setSetting("relayProvider", RELAY_PROVIDER_LIGHT);
|
||||
setSetting("lightProvider", LIGHT_PROVIDER_RGB);
|
||||
setSetting("lightProvider", LIGHT_PROVIDER_DIMMER);
|
||||
setSetting("ledGPIO", 1, 2);
|
||||
setSetting("ledLogic", 1, 1);
|
||||
setSetting("redGPIO", 14);
|
||||
setSetting("greenGPIO", 5);
|
||||
setSetting("blueGPIO", 12);
|
||||
setSetting("whiteGPIO", 13);
|
||||
setSetting("lightLogic", 1);
|
||||
setSetting("ch1GPIO", 14);
|
||||
setSetting("ch2GPIO", 5);
|
||||
setSetting("ch3GPIO", 12);
|
||||
setSetting("ch4GPIO", 13);
|
||||
setSetting("ch1Logic", 1);
|
||||
setSetting("ch2Logic", 1);
|
||||
setSetting("ch3Logic", 1);
|
||||
setSetting("ch4Logic", 1);
|
||||
#endif
|
||||
|
||||
#ifdef ITEAD_MOTOR
|
||||
@@ -267,15 +259,19 @@ void hwUpwardsCompatibility() {
|
||||
#ifdef H801_LED_CONTROLLER
|
||||
setSetting("board", 24);
|
||||
setSetting("relayProvider", RELAY_PROVIDER_LIGHT);
|
||||
setSetting("lightProvider", LIGHT_PROVIDER_RGB2W);
|
||||
setSetting("lightProvider", LIGHT_PROVIDER_DIMMER);
|
||||
setSetting("ledGPIO", 5, 1);
|
||||
setSetting("ledLogic", 1, 1);
|
||||
setSetting("redGPIO", 15);
|
||||
setSetting("greenGPIO", 13);
|
||||
setSetting("blueGPIO", 12);
|
||||
setSetting("whiteGPIO", 14);
|
||||
setSetting("white2GPIO", 4);
|
||||
setSetting("lightLogic", 1);
|
||||
setSetting("ch1GPIO", 15);
|
||||
setSetting("ch2GPIO", 13);
|
||||
setSetting("ch3GPIO", 12);
|
||||
setSetting("ch4GPIO", 14);
|
||||
setSetting("ch5GPIO", 4);
|
||||
setSetting("ch1Logic", 1);
|
||||
setSetting("ch2Logic", 1);
|
||||
setSetting("ch3Logic", 1);
|
||||
setSetting("ch4Logic", 1);
|
||||
setSetting("ch5Logic", 1);
|
||||
#endif
|
||||
|
||||
saveSettings();
|
||||
|
||||
@@ -9,56 +9,55 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
|
||||
|
||||
#include <Ticker.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <vector>
|
||||
|
||||
typedef struct {
|
||||
unsigned char pin;
|
||||
bool reverse;
|
||||
unsigned char value;
|
||||
unsigned char shadow;
|
||||
} channel_t;
|
||||
std::vector<channel_t> _channels;
|
||||
|
||||
Ticker colorTicker;
|
||||
bool _lightState = false;
|
||||
float brightness = 1.0;
|
||||
unsigned int _lightColor[3] = {0};
|
||||
unsigned int _brightness = LIGHT_MAX_BRIGHTNESS;
|
||||
|
||||
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
|
||||
#include <my9291.h>
|
||||
my9291 * _my9291;
|
||||
#endif
|
||||
|
||||
#if ENABLE_GAMMA_CORRECTION
|
||||
#if LIGHT_ENABLE_GAMMA
|
||||
|
||||
#define GAMMA_TABLE_SIZE (256)
|
||||
#undef LIGHT_PWM_RANGE
|
||||
#define LIGHT_PWM_RANGE (4095)
|
||||
// Gamma Correction lookup table for gamma=2.8 and 12 bit (4095) full scale
|
||||
// TODO: move to PROGMEM
|
||||
const unsigned short gamma_table[LIGHT_MAX_VALUE+1] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
|
||||
2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 8, 9, 10, 11,
|
||||
12, 13, 15, 16, 17, 18, 20, 21, 23, 25, 26, 28, 30, 32, 34, 36,
|
||||
38, 40, 43, 45, 48, 50, 53, 56, 59, 62, 65, 68, 71, 75, 78, 82,
|
||||
85, 89, 93, 97, 101, 105, 110, 114, 119, 123, 128, 133, 138, 143, 149, 154,
|
||||
159, 165, 171, 177, 183, 189, 195, 202, 208, 215, 222, 229, 236, 243, 250, 258,
|
||||
266, 273, 281, 290, 298, 306, 315, 324, 332, 341, 351, 360, 369, 379, 389, 399,
|
||||
409, 419, 430, 440, 451, 462, 473, 485, 496, 508, 520, 532, 544, 556, 569, 582,
|
||||
594, 608, 621, 634, 648, 662, 676, 690, 704, 719, 734, 749, 764, 779, 795, 811,
|
||||
827, 843, 859, 876, 893, 910, 927, 944, 962, 980, 998,1016,1034,1053,1072,1091,
|
||||
1110,1130,1150,1170,1190,1210,1231,1252,1273,1294,1316,1338,1360,1382,1404,1427,
|
||||
1450,1473,1497,1520,1544,1568,1593,1617,1642,1667,1693,1718,1744,1770,1797,1823,
|
||||
1850,1877,1905,1932,1960,1988,2017,2045,2074,2103,2133,2162,2192,2223,2253,2284,
|
||||
2315,2346,2378,2410,2442,2474,2507,2540,2573,2606,2640,2674,2708,2743,2778,2813,
|
||||
2849,2884,2920,2957,2993,3030,3067,3105,3143,3181,3219,3258,3297,3336,3376,3416,
|
||||
3456,3496,3537,3578,3619,3661,3703,3745,3788,3831,3874,3918,3962,4006,4050,4095 };
|
||||
|
||||
// Gamma Correction lookup table for gamma=2.8 and 12 bit (4095) full scale
|
||||
const unsigned short gamma_table[GAMMA_TABLE_SIZE] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
|
||||
2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 8, 9, 10, 11,
|
||||
12, 13, 15, 16, 17, 18, 20, 21, 23, 25, 26, 28, 30, 32, 34, 36,
|
||||
38, 40, 43, 45, 48, 50, 53, 56, 59, 62, 65, 68, 71, 75, 78, 82,
|
||||
85, 89, 93, 97, 101, 105, 110, 114, 119, 123, 128, 133, 138, 143, 149, 154,
|
||||
159, 165, 171, 177, 183, 189, 195, 202, 208, 215, 222, 229, 236, 243, 250, 258,
|
||||
266, 273, 281, 290, 298, 306, 315, 324, 332, 341, 351, 360, 369, 379, 389, 399,
|
||||
409, 419, 430, 440, 451, 462, 473, 485, 496, 508, 520, 532, 544, 556, 569, 582,
|
||||
594, 608, 621, 634, 648, 662, 676, 690, 704, 719, 734, 749, 764, 779, 795, 811,
|
||||
827, 843, 859, 876, 893, 910, 927, 944, 962, 980, 998,1016,1034,1053,1072,1091,
|
||||
1110,1130,1150,1170,1190,1210,1231,1252,1273,1294,1316,1338,1360,1382,1404,1427,
|
||||
1450,1473,1497,1520,1544,1568,1593,1617,1642,1667,1693,1718,1744,1770,1797,1823,
|
||||
1850,1877,1905,1932,1960,1988,2017,2045,2074,2103,2133,2162,2192,2223,2253,2284,
|
||||
2315,2346,2378,2410,2442,2474,2507,2540,2573,2606,2640,2674,2708,2743,2778,2813,
|
||||
2849,2884,2920,2957,2993,3030,3067,3105,3143,3181,3219,3258,3297,3336,3376,3416,
|
||||
3456,3496,3537,3578,3619,3661,3703,3745,3788,3831,3874,3918,3962,4006,4050,4095 };
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef LIGHT_PWM_FREQUENCY
|
||||
#define LIGHT_PWM_FREQUENCY (1000)
|
||||
#endif
|
||||
|
||||
#ifndef LIGHT_PWM_RANGE
|
||||
#define LIGHT_PWM_RANGE (255)
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// UTILS
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void _color_string2array(const char * rgb, unsigned int * array) {
|
||||
void _fromRGB(const char * rgb) {
|
||||
|
||||
char * p = (char *) rgb;
|
||||
if (strlen(p) == 0) return;
|
||||
@@ -66,70 +65,87 @@ void _color_string2array(const char * rgb, unsigned int * array) {
|
||||
// if color begins with a # then assume HEX RGB
|
||||
if (p[0] == '#') {
|
||||
|
||||
if (!lightHasColor()) return;
|
||||
|
||||
++p;
|
||||
unsigned long value = strtol(p, NULL, 16);
|
||||
unsigned long value = strtoul(p, NULL, 16);
|
||||
|
||||
// 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;
|
||||
_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;
|
||||
} else {
|
||||
array[0] = (value >> 16) & 0xFF;
|
||||
array[1] = (value >> 8) & 0xFF;
|
||||
array[2] = (value) & 0xFF;
|
||||
_channels[0].value = (value >> 16) & 0xFF;
|
||||
_channels[1].value = (value >> 8) & 0xFF;
|
||||
_channels[2].value = (value) & 0xFF;
|
||||
}
|
||||
|
||||
// it's a temperature
|
||||
} else if (p[strlen(p)-1] == 'K') {
|
||||
// it's a temperature in mireds
|
||||
} else if (p[0] == 'M') {
|
||||
|
||||
p[strlen(p)-1] = 0;
|
||||
unsigned long temperature = atol(p);
|
||||
_color_temperature2array(temperature, array);
|
||||
unsigned long mireds = atol(p + 1);
|
||||
_fromMireds(mireds);
|
||||
|
||||
// it's a temperature in kelvin
|
||||
} else if (p[0] == 'K') {
|
||||
|
||||
unsigned long kelvin = atol(p + 1);
|
||||
_fromKelvin(kelvin);
|
||||
|
||||
// otherwise assume decimal values separated by commas
|
||||
} else {
|
||||
|
||||
char * tok;
|
||||
unsigned char count = 0;
|
||||
unsigned char channels = _channels.size();
|
||||
|
||||
tok = strtok(p, ",");
|
||||
array[0] = atoi(tok);
|
||||
tok = strtok(NULL, ",");
|
||||
|
||||
// if there are more than one value assume R,G,B
|
||||
if (tok != NULL) {
|
||||
array[1] = atoi(tok);
|
||||
while (tok != NULL) {
|
||||
_channels[count].value = atoi(tok);
|
||||
if (++count == channels) break;
|
||||
tok = strtok(NULL, ",");
|
||||
if (tok != NULL) {
|
||||
array[2] = atoi(tok);
|
||||
} else {
|
||||
array[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// only one value set red, green and blue to the same value
|
||||
} else {
|
||||
array[2] = array[1] = array[0];
|
||||
// RGB but less than 3 values received
|
||||
if (channels > 2 & count < 3) {
|
||||
_channels[1].value = _channels[0].value;
|
||||
_channels[2].value = _channels[0].value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
void _toRGB(char * rgb, size_t len, bool applyBrightness) {
|
||||
|
||||
if (!lightHasColor()) return;
|
||||
|
||||
float b = applyBrightness ? (float) _brightness / LIGHT_MAX_BRIGHTNESS : 1;
|
||||
|
||||
unsigned long value = 0;
|
||||
|
||||
value += _channels[0].value * b;
|
||||
value <<= 8;
|
||||
value += _channels[1].value * b;
|
||||
value <<= 8;
|
||||
value += _channels[2].value * b;
|
||||
|
||||
snprintf(rgb, len, "#%06X", value);
|
||||
|
||||
}
|
||||
|
||||
void _toRGB(char * rgb, size_t len) {
|
||||
_toRGB(rgb, len, false);
|
||||
}
|
||||
|
||||
// Thanks to Sacha Telgenhof for sharing this code in his AiLight library
|
||||
// Color temperature is measured in mireds (kelvin = 1e6/mired)
|
||||
// https://github.com/stelgenhof/AiLight
|
||||
void _color_temperature2array(unsigned long mireds, unsigned int * array) {
|
||||
void _fromKelvin(unsigned long kelvin) {
|
||||
|
||||
// Force boundaries and conversion
|
||||
if (mireds == 0) mireds = 1;
|
||||
unsigned long kelvin = constrain(1000000UL / mireds, 1000, 40000) / 100;
|
||||
// Check we have RGB channels
|
||||
if (!lightHasColor()) return;
|
||||
|
||||
// Calculate colors
|
||||
unsigned int red = (kelvin <= 66)
|
||||
@@ -145,152 +161,100 @@ void _color_temperature2array(unsigned long mireds, unsigned int * array) {
|
||||
: 138.5177312231 * log(kelvin - 10) - 305.0447927307);
|
||||
|
||||
// Save values
|
||||
array[0] = constrain(red, 0, LIGHT_MAX_VALUE);
|
||||
array[1] = constrain(green, 0, LIGHT_MAX_VALUE);
|
||||
array[2] = constrain(blue, 0, LIGHT_MAX_VALUE);
|
||||
_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);
|
||||
|
||||
}
|
||||
|
||||
// Converts a color intensity value (0..255) to a pwm value
|
||||
// This takes care of positive or negative logic and brightness
|
||||
unsigned int _intensity2pwm(unsigned int intensity, float brightness) {
|
||||
// Color temperature is measured in mireds (kelvin = 1e6/mired)
|
||||
void _fromMireds(unsigned long mireds) {
|
||||
if (mireds == 0) mireds = 1;
|
||||
unsigned long kelvin = constrain(1000000UL / mireds, 1000, 40000) / 100;
|
||||
_fromKelvin(kelvin);
|
||||
}
|
||||
|
||||
intensity = brightness * intensity;
|
||||
|
||||
#if ENABLE_GAMMA_CORRECTION
|
||||
unsigned int pwm = (intensity < GAMMA_TABLE_SIZE) ? gamma_table[intensity] : LIGHT_PWM_RANGE;
|
||||
unsigned int _toPWM(unsigned long value, bool gamma, bool reverse) {
|
||||
value = constrain(value, 0, LIGHT_MAX_VALUE);
|
||||
value *= ((float) _brightness / LIGHT_MAX_BRIGHTNESS);
|
||||
#if LIGHT_ENABLE_GAMMA
|
||||
unsigned int pwm = gamma ? gamma_table[value] : map(value, 0, LIGHT_MAX_VALUE, 0, LIGHT_MAX_PWM);
|
||||
#else
|
||||
unsigned int pwm = intensity;
|
||||
unsigned int pwm = map(value, 0, LIGHT_MAX_VALUE, 0, LIGHT_MAX_PWM);
|
||||
#endif
|
||||
|
||||
#if RGBW_INVERSE_LOGIC != 1
|
||||
pwm = LIGHT_PWM_RANGE - pwm;
|
||||
#endif
|
||||
|
||||
if (reverse) pwm = LIGHT_MAX_PWM - pwm;
|
||||
return pwm;
|
||||
|
||||
}
|
||||
|
||||
unsigned int _intensity2pwm(unsigned int intensity) {
|
||||
return _intensity2pwm(intensity, LIGHT_MAX_VALUE);
|
||||
// Returns a PWM valule for the given channel ID
|
||||
unsigned int _toPWM(unsigned char id) {
|
||||
if (id < _channels.size()) {
|
||||
#if LIGHT_ENABLE_GAMMA
|
||||
bool gamma = (lightHasColor() && id < 3);
|
||||
#else
|
||||
bool gamma = false;
|
||||
#endif
|
||||
return _toPWM(_channels[id].shadow, gamma, _channels[id].reverse);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PROVIDER
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void _lightProviderSet(bool state, unsigned int red, unsigned int green, unsigned int blue, float brightness) {
|
||||
void _shadow() {
|
||||
|
||||
unsigned int white = 0;
|
||||
bool useWhite = getSetting("useWhite", LIGHT_USE_WHITE).toInt() == 1;
|
||||
|
||||
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB2W)
|
||||
// If all set to the same value use white instead
|
||||
if ((red == green) && (green == blue)) {
|
||||
white = red;
|
||||
red = green = blue = 0;
|
||||
}
|
||||
#endif
|
||||
for (unsigned int i=0; i < _channels.size(); i++) {
|
||||
_channels[i].shadow = _lightState ? _channels[i].value : 0;
|
||||
}
|
||||
|
||||
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;
|
||||
_channels[2].shadow = 0;
|
||||
_channels[1].shadow = 0;
|
||||
_channels[0].shadow = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
void _lightProviderUpdate() {
|
||||
|
||||
_shadow();
|
||||
|
||||
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
|
||||
_my9291->setState(state);
|
||||
red *= brightness;
|
||||
green *= brightness;
|
||||
blue *= brightness;
|
||||
white *= brightness;
|
||||
_my9291->setColor((my9291_color_t) { red, green, blue, white });
|
||||
#endif
|
||||
|
||||
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB2W)
|
||||
if (_lightState) {
|
||||
|
||||
// Check state
|
||||
if (!state) red = green = blue = white = 0;
|
||||
|
||||
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)
|
||||
analogWrite(RGBW_WHITE_PIN, _intensity2pwm(white, brightness));
|
||||
#endif
|
||||
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB2W)
|
||||
analogWrite(RGBW_WHITE_PIN, _intensity2pwm(white, brightness));
|
||||
analogWrite(RGBW_WHITE2_PIN, _intensity2pwm(white, brightness));
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// LIGHT MANAGEMENT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void lightState(bool state) {
|
||||
_lightState = state;
|
||||
_lightProviderSet(_lightState, _lightColor[0], _lightColor[1], _lightColor[2], brightness);
|
||||
}
|
||||
|
||||
bool lightState() {
|
||||
return _lightState;
|
||||
}
|
||||
|
||||
void parseColor(const char * color) {
|
||||
brightness = 1.0;
|
||||
_color_string2array(color, _lightColor);
|
||||
}
|
||||
|
||||
void lightColor(bool save, bool forward) {
|
||||
|
||||
_lightProviderSet(_lightState, _lightColor[0], _lightColor[1], _lightColor[2], brightness);
|
||||
|
||||
// Delay saving to EEPROM 5 seconds to avoid wearing it out unnecessarily
|
||||
if (save) colorTicker.once(LIGHT_SAVE_DELAY, _lightColorSave);
|
||||
|
||||
// Report color & brightness to MQTT broker
|
||||
if (forward) {
|
||||
|
||||
// Color
|
||||
char rgb[8];
|
||||
_color_array2rgb(_lightColor, 1.0, rgb);
|
||||
mqttSend(MQTT_TOPIC_COLOR, rgb);
|
||||
|
||||
if ((_lightColor[0] == _lightColor[1]) & (_lightColor[1] == _lightColor[2])) {
|
||||
|
||||
// White
|
||||
char buffer[5];
|
||||
sprintf(buffer, "%d", (int) _lightColor[0]);
|
||||
mqttSend(MQTT_TOPIC_WHITE, buffer);
|
||||
float ratio = (float) LIGHT_MAX_VALUE / LIGHT_MAX_PWM;
|
||||
|
||||
unsigned int red = _toPWM(0) * ratio;
|
||||
unsigned int green = _toPWM(1) * ratio;
|
||||
unsigned int blue = _toPWM(2) * ratio;
|
||||
unsigned int white = _toPWM(3) * ratio;
|
||||
_my9291->setColor((my9291_color_t) { red, green, blue, white });
|
||||
_my9291->setState(true);
|
||||
|
||||
} else {
|
||||
|
||||
// Brightness
|
||||
char buffer[5];
|
||||
sprintf(buffer, "%d", (int) (brightness * LIGHT_MAX_BRIGHTNESS));
|
||||
mqttSend(MQTT_TOPIC_BRIGHTNESS, buffer);
|
||||
_my9291->setState(false);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
// Report color to WS clients
|
||||
{
|
||||
char rgb[8];
|
||||
_color_array2rgb(_lightColor, brightness, rgb);
|
||||
char message[64];
|
||||
sprintf(message, "{\"color\": \"%s\"}", rgb);
|
||||
wsSend(message);
|
||||
}
|
||||
#if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER
|
||||
|
||||
}
|
||||
for (unsigned int i=0; i < _channels.size(); i++) {
|
||||
analogWrite(_channels[i].pin, _toPWM(i));
|
||||
}
|
||||
|
||||
String lightColor(float b) {
|
||||
char rgb[8];
|
||||
_color_array2rgb(_lightColor, b, rgb);
|
||||
return String(rgb);
|
||||
}
|
||||
#endif
|
||||
|
||||
String lightColor() {
|
||||
return lightColor(brightness);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -298,29 +262,39 @@ String lightColor() {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void _lightColorSave() {
|
||||
setSetting("color", lightColor(1.0));
|
||||
setSetting("brightness", brightness * LIGHT_MAX_BRIGHTNESS);
|
||||
for (unsigned int i=0; i < _channels.size(); i++) {
|
||||
setSetting("ch", i, _channels[i].value);
|
||||
}
|
||||
setSetting("brightness", _brightness);
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
void _lightColorRestore() {
|
||||
String color = getSetting("color", LIGHT_DEFAULT_COLOR);
|
||||
_color_string2array(color.c_str(), _lightColor);
|
||||
brightness = getSetting("brightness", 1).toFloat() / LIGHT_MAX_BRIGHTNESS;
|
||||
for (unsigned int i=0; i < _channels.size(); i++) {
|
||||
_channels[i].value = getSetting("ch", i, 0).toInt();
|
||||
}
|
||||
_brightness = getSetting("brightness", LIGHT_MAX_BRIGHTNESS).toInt();
|
||||
lightUpdate(false, false);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// MQTT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void lightMQTTCallback(unsigned int type, const char * topic, const char * payload) {
|
||||
void _lightMQTTCallback(unsigned int type, const char * topic, const char * payload) {
|
||||
|
||||
|
||||
if (type == MQTT_CONNECT_EVENT) {
|
||||
|
||||
mqttSubscribe(MQTT_TOPIC_BRIGHTNESS);
|
||||
mqttSubscribe(MQTT_TOPIC_COLORTEMP);
|
||||
mqttSubscribe(MQTT_TOPIC_MIRED);
|
||||
mqttSubscribe(MQTT_TOPIC_KELVIN);
|
||||
mqttSubscribe(MQTT_TOPIC_COLOR);
|
||||
mqttSubscribe(MQTT_TOPIC_WHITE);
|
||||
|
||||
char buffer[strlen(MQTT_TOPIC_CHANNEL) + 3];
|
||||
sprintf(buffer, "%s/+", MQTT_TOPIC_CHANNEL);
|
||||
mqttSubscribe(buffer);
|
||||
|
||||
}
|
||||
|
||||
if (type == MQTT_MESSAGE_EVENT) {
|
||||
@@ -328,86 +302,260 @@ void lightMQTTCallback(unsigned int type, const char * topic, const char * paylo
|
||||
// Match topic
|
||||
String t = mqttSubtopic((char *) topic);
|
||||
|
||||
// Color temperature
|
||||
if (t.equals(MQTT_TOPIC_COLORTEMP)) {
|
||||
char buffer[10];
|
||||
sprintf(buffer, "%sK", payload);
|
||||
parseColor(buffer);
|
||||
lightColor(true, mqttForward());
|
||||
// Color temperature in mireds
|
||||
if (t.equals(MQTT_TOPIC_MIRED)) {
|
||||
_fromMireds(atol(payload));
|
||||
lightUpdate(true, mqttForward());
|
||||
}
|
||||
|
||||
// Color temperature in kelvins
|
||||
if (t.equals(MQTT_TOPIC_KELVIN)) {
|
||||
_fromKelvin(atol(payload));
|
||||
lightUpdate(true, mqttForward());
|
||||
}
|
||||
|
||||
// Color
|
||||
if (t.equals(MQTT_TOPIC_COLOR)) {
|
||||
parseColor(payload);
|
||||
lightColor(true, mqttForward());
|
||||
}
|
||||
|
||||
// White
|
||||
if (t.equals(MQTT_TOPIC_WHITE)) {
|
||||
parseColor(payload);
|
||||
lightColor(true, mqttForward());
|
||||
lightColor(payload);
|
||||
lightUpdate(true, mqttForward());
|
||||
}
|
||||
|
||||
// Brightness
|
||||
if (t.equals(MQTT_TOPIC_BRIGHTNESS)) {
|
||||
brightness = (float) atoi(payload) / LIGHT_MAX_BRIGHTNESS;
|
||||
lightColor(true, mqttForward());
|
||||
_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()) {
|
||||
DEBUG_MSG_P(PSTR("[LIGHT] Wrong channelID (%d)\n"), channelID);
|
||||
return;
|
||||
}
|
||||
lightChannel(channelID, atoi(payload));
|
||||
lightUpdate(true, mqttForward());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// API
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
unsigned char lightChannels() {
|
||||
return _channels.size();
|
||||
}
|
||||
|
||||
bool lightHasColor() {
|
||||
return _channels.size() > 2;
|
||||
}
|
||||
|
||||
unsigned char lightWhiteChannels() {
|
||||
return _channels.size() % 3;
|
||||
}
|
||||
|
||||
void lightMQTT() {
|
||||
|
||||
char buffer[8];
|
||||
|
||||
// Color
|
||||
if (lightHasColor()) {
|
||||
_toRGB(buffer, 8, false);
|
||||
mqttSend(MQTT_TOPIC_COLOR, buffer);
|
||||
}
|
||||
|
||||
// Channels
|
||||
for (unsigned int i=0; i < _channels.size(); i++) {
|
||||
sprintf(buffer, "%d", _channels[i].value);
|
||||
mqttSend(MQTT_TOPIC_CHANNEL, i, buffer);
|
||||
}
|
||||
|
||||
// Brightness
|
||||
sprintf(buffer, "%d", _brightness);
|
||||
mqttSend(MQTT_TOPIC_BRIGHTNESS, buffer);
|
||||
|
||||
}
|
||||
|
||||
void lightUpdate(bool save, bool forward) {
|
||||
|
||||
_lightProviderUpdate();
|
||||
|
||||
// Delay saving to EEPROM 5 seconds to avoid wearing it out unnecessarily
|
||||
if (save) colorTicker.once(LIGHT_SAVE_DELAY, _lightColorSave);
|
||||
|
||||
// Report color & brightness to MQTT broker
|
||||
if (forward) lightMQTT();
|
||||
|
||||
// Report color to WS clients (using current brightness setting)
|
||||
{
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& root = jsonBuffer.createObject();
|
||||
root["colorVisible"] = 1;
|
||||
root["color"] = lightColor();
|
||||
JsonArray& channels = root.createNestedArray("channels");
|
||||
for (unsigned char id=0; id < lightChannels(); id++) {
|
||||
channels.add(lightChannel(id));
|
||||
}
|
||||
root["brightness"] = lightBrightness();
|
||||
String output;
|
||||
root.printTo(output);
|
||||
wsSend(output.c_str());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void lightState(bool state) {
|
||||
_lightState = state;
|
||||
}
|
||||
|
||||
bool lightState() {
|
||||
return _lightState;
|
||||
}
|
||||
|
||||
void lightColor(const char * color) {
|
||||
_fromRGB(color);
|
||||
}
|
||||
|
||||
String lightColor() {
|
||||
char rgb[8];
|
||||
_toRGB(rgb, 8, false);
|
||||
return String(rgb);
|
||||
}
|
||||
|
||||
unsigned int lightChannel(unsigned char id) {
|
||||
if (id <= _channels.size()) {
|
||||
return _channels[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);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int lightBrightness() {
|
||||
return _brightness;
|
||||
}
|
||||
|
||||
void lightBrightness(unsigned int b) {
|
||||
_brightness = constrain(b, 0, LIGHT_MAX_BRIGHTNESS);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// SETUP
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void lightSetup() {
|
||||
|
||||
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
|
||||
_my9291 = new my9291(MY9291_DI_PIN, MY9291_DCKI_PIN, MY9291_COMMAND);
|
||||
#endif
|
||||
|
||||
#if (LIGHT_PROVIDER == LIGHT_PROVIDER_RGB) || (LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW)
|
||||
analogWriteRange(LIGHT_PWM_RANGE);
|
||||
analogWriteFreq(LIGHT_PWM_FREQUENCY);
|
||||
pinMode(RGBW_RED_PIN, OUTPUT);
|
||||
pinMode(RGBW_GREEN_PIN, OUTPUT);
|
||||
pinMode(RGBW_BLUE_PIN, OUTPUT);
|
||||
#if LIGHT_PROVIDER == LIGHT_PROVIDER_RGBW
|
||||
pinMode(RGBW_WHITE_PIN, OUTPUT);
|
||||
#endif
|
||||
#if LIGHT_PROVIDER == LIGHT_PROVIDER_RGB2W
|
||||
pinMode(RGBW_WHITE_PIN, OUTPUT);
|
||||
pinMode(RGBW_WHITE2_PIN, OUTPUT);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
_lightColorRestore();
|
||||
void _lightAPISetup() {
|
||||
|
||||
// API entry points (protected with apikey)
|
||||
apiRegister(MQTT_TOPIC_COLOR, MQTT_TOPIC_COLOR,
|
||||
[](char * buffer, size_t len) {
|
||||
snprintf(buffer, len, "%s", lightColor().c_str());
|
||||
},
|
||||
if (_channels.size() > 2) {
|
||||
apiRegister(MQTT_TOPIC_COLOR, MQTT_TOPIC_COLOR,
|
||||
[](char * buffer, size_t len) {
|
||||
_toRGB(buffer, len, false);
|
||||
},
|
||||
[](const char * payload) {
|
||||
lightColor(payload);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
apiRegister(MQTT_TOPIC_KELVIN, MQTT_TOPIC_KELVIN,
|
||||
[](char * buffer, size_t len) {},
|
||||
[](const char * payload) {
|
||||
parseColor(payload);
|
||||
lightColor(true, true);
|
||||
_fromKelvin(atol(payload));
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
apiRegister(MQTT_TOPIC_MIRED, MQTT_TOPIC_MIRED,
|
||||
[](char * buffer, size_t len) {},
|
||||
[](const char * payload) {
|
||||
_fromMireds(atol(payload));
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
apiRegister(MQTT_TOPIC_BRIGHTNESS, MQTT_TOPIC_BRIGHTNESS,
|
||||
[](char * buffer, size_t len) {
|
||||
snprintf(buffer, len, "%d", (int) (brightness * LIGHT_MAX_BRIGHTNESS));
|
||||
snprintf(buffer, len, "%d", _brightness);
|
||||
},
|
||||
[](const char * payload) {
|
||||
brightness = (float) atoi(payload) / LIGHT_MAX_BRIGHTNESS;
|
||||
lightColor(true, true);
|
||||
lightBrightness(atoi(payload));
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
mqttRegister(lightMQTTCallback);
|
||||
for (unsigned int id=0; id<lightChannels(); id++) {
|
||||
|
||||
char url[15];
|
||||
sprintf(url, "%s/%d", MQTT_TOPIC_CHANNEL, id);
|
||||
|
||||
char key[10];
|
||||
sprintf(key, "%s%d", MQTT_TOPIC_CHANNEL, id);
|
||||
|
||||
apiRegister(url, key,
|
||||
[id](char * buffer, size_t len) {
|
||||
snprintf(buffer, len, "%d", lightChannel(id));
|
||||
},
|
||||
[id](const char * payload) {
|
||||
lightChannel(id, atoi(payload));
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void lightSetup() {
|
||||
|
||||
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
|
||||
_my9291 = new my9291(MY9291_DI_PIN, MY9291_DCKI_PIN, MY9291_COMMAND);
|
||||
_channels.push_back((channel_t) {0, false, 0});
|
||||
_channels.push_back((channel_t) {0, false, 0});
|
||||
_channels.push_back((channel_t) {0, false, 0});
|
||||
_channels.push_back((channel_t) {0, false, 0});
|
||||
#endif
|
||||
|
||||
#if LIGHT_PROVIDER == LIGHT_PROVIDER_DIMMER
|
||||
|
||||
#ifdef LIGHT_CH1_PIN
|
||||
_channels.push_back((channel_t) {LIGHT_CH1_PIN, LIGHT_CH1_INVERSE, 0});
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT_CH2_PIN
|
||||
_channels.push_back((channel_t) {LIGHT_CH2_PIN, LIGHT_CH2_INVERSE, 0});
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT_CH3_PIN
|
||||
_channels.push_back((channel_t) {LIGHT_CH3_PIN, LIGHT_CH3_INVERSE, 0});
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT_CH4_PIN
|
||||
_channels.push_back((channel_t) {LIGHT_CH4_PIN, LIGHT_CH4_INVERSE, 0});
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT_CH5_PIN
|
||||
_channels.push_back((channel_t) {LIGHT_CH5_PIN, LIGHT_CH5_INVERSE, 0});
|
||||
#endif
|
||||
|
||||
analogWriteRange(LIGHT_MAX_PWM+1);
|
||||
analogWriteFreq(LIGHT_PWM_FREQUENCY);
|
||||
for (unsigned int i=0; i < _channels.size(); i++) {
|
||||
pinMode(_channels[i].pin, OUTPUT);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
_lightColorRestore();
|
||||
_lightAPISetup();
|
||||
mqttRegister(_lightMQTTCallback);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,10 @@ PubSubClient mqtt(mqttWiFiClient);
|
||||
bool _mqttConnected = false;
|
||||
#endif
|
||||
|
||||
String mqttTopic;
|
||||
String _mqttTopic;
|
||||
String _mqttSetter;
|
||||
String _mqttGetter;
|
||||
|
||||
bool _mqttForward;
|
||||
char *_mqttUser = 0;
|
||||
char *_mqttPass = 0;
|
||||
@@ -53,30 +56,17 @@ void mqttDisconnect() {
|
||||
mqtt.disconnect();
|
||||
}
|
||||
|
||||
void buildTopics() {
|
||||
// Replace identifier
|
||||
mqttTopic = getSetting("mqttTopic", MQTT_TOPIC);
|
||||
mqttTopic.replace("{identifier}", getSetting("hostname"));
|
||||
if (!mqttTopic.endsWith("/")) mqttTopic = mqttTopic + "/";
|
||||
}
|
||||
|
||||
bool mqttForward() {
|
||||
return _mqttForward;
|
||||
}
|
||||
|
||||
String mqttSubtopic(char * topic) {
|
||||
|
||||
String response;
|
||||
|
||||
String t = String(topic);
|
||||
String mqttSetter = getSetting("mqttSetter", MQTT_USE_SETTER);
|
||||
|
||||
if (t.startsWith(mqttTopic) && t.endsWith(mqttSetter)) {
|
||||
response = t.substring(mqttTopic.length(), t.length() - mqttSetter.length());
|
||||
if (t.startsWith(_mqttTopic) && t.endsWith(_mqttSetter)) {
|
||||
response = t.substring(_mqttTopic.length(), t.length() - _mqttSetter.length());
|
||||
}
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
|
||||
void mqttSendRaw(const char * topic, const char * message) {
|
||||
@@ -107,7 +97,7 @@ void _mqttFlush() {
|
||||
|
||||
String output;
|
||||
root.printTo(output);
|
||||
String path = mqttTopic + String(MQTT_TOPIC_JSON);
|
||||
String path = _mqttTopic + String(MQTT_TOPIC_JSON);
|
||||
mqttSendRaw(path.c_str(), output.c_str());
|
||||
|
||||
for (unsigned char i = 0; i < _mqtt_queue.size(); i++) {
|
||||
@@ -128,8 +118,7 @@ void mqttSend(const char * topic, const char * message, bool force) {
|
||||
_mqtt_queue.push_back(element);
|
||||
mqttFlushTicker.once_ms(MQTT_USE_JSON_DELAY, _mqttFlush);
|
||||
} else {
|
||||
String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER);
|
||||
String path = mqttTopic + String(topic) + mqttGetter;
|
||||
String path = _mqttTopic + String(topic) + _mqttGetter;
|
||||
mqttSendRaw(path.c_str(), message);
|
||||
}
|
||||
}
|
||||
@@ -161,17 +150,42 @@ void mqttSubscribeRaw(const char * topic) {
|
||||
}
|
||||
|
||||
void mqttSubscribe(const char * topic) {
|
||||
String mqttSetter = getSetting("mqttSetter", MQTT_USE_SETTER);
|
||||
String path = mqttTopic + String(topic) + mqttSetter;
|
||||
String path = _mqttTopic + String(topic) + _mqttSetter;
|
||||
mqttSubscribeRaw(path.c_str());
|
||||
}
|
||||
|
||||
void mqttRegister(void (*callback)(unsigned int, const char *, const char *)) {
|
||||
_mqtt_callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Callbacks
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void mqttRegister(void (*callback)(unsigned int, const char *, const char *)) {
|
||||
_mqtt_callbacks.push_back(callback);
|
||||
void _mqttCallback(unsigned int type, const char * topic, const char * payload) {
|
||||
|
||||
|
||||
if (type == MQTT_CONNECT_EVENT) {
|
||||
|
||||
mqttSubscribe(MQTT_TOPIC_ACTION);
|
||||
|
||||
}
|
||||
|
||||
if (type == MQTT_MESSAGE_EVENT) {
|
||||
|
||||
// Match topic
|
||||
String t = mqttSubtopic((char *) topic);
|
||||
|
||||
// Actions
|
||||
if (t.equals(MQTT_TOPIC_ACTION)) {
|
||||
if (strcmp(payload, MQTT_ACTION_RESET) == 0) {
|
||||
customReset(CUSTOM_RESET_MQTT);
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void _mqttOnConnect() {
|
||||
@@ -183,14 +197,11 @@ void _mqttOnConnect() {
|
||||
#endif
|
||||
|
||||
// Build MQTT topics
|
||||
buildTopics();
|
||||
mqttConfigure();
|
||||
|
||||
// Send first Heartbeat
|
||||
heartbeat();
|
||||
|
||||
// Subscribe to system topics
|
||||
mqttSubscribe(MQTT_TOPIC_ACTION);
|
||||
|
||||
// Send connect event to subscribers
|
||||
for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) {
|
||||
(*_mqtt_callbacks[i])(MQTT_CONNECT_EVENT, NULL, NULL);
|
||||
@@ -225,15 +236,6 @@ void _mqttOnMessage(char* topic, char* payload, unsigned int len) {
|
||||
#endif
|
||||
DEBUG_MSG_P(PSTR("\n"));
|
||||
|
||||
// Check system topics
|
||||
String t = mqttSubtopic((char *) topic);
|
||||
if (t.equals(MQTT_TOPIC_ACTION)) {
|
||||
if (strcmp(message, MQTT_ACTION_RESET) == 0) {
|
||||
customReset(CUSTOM_RESET_MQTT);
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
// Send message event to subscribers
|
||||
for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) {
|
||||
(*_mqtt_callbacks[i])(MQTT_MESSAGE_EVENT, topic, message);
|
||||
@@ -274,7 +276,7 @@ void mqttConnect() {
|
||||
_mqttUser = strdup(getSetting("mqttUser").c_str());
|
||||
_mqttPass = strdup(getSetting("mqttPassword").c_str());
|
||||
if (_mqttWill) free(_mqttWill);
|
||||
_mqttWill = strdup((mqttTopic + MQTT_TOPIC_STATUS).c_str());
|
||||
_mqttWill = strdup((_mqttTopic + MQTT_TOPIC_STATUS).c_str());
|
||||
|
||||
DEBUG_MSG_P(PSTR("[MQTT] Connecting to broker at %s:%d"), host, port);
|
||||
mqtt.setServer(host, port);
|
||||
@@ -313,14 +315,20 @@ void mqttConnect() {
|
||||
|
||||
free(host);
|
||||
|
||||
String mqttSetter = getSetting("mqttSetter", MQTT_USE_SETTER);
|
||||
String mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER);
|
||||
_mqttForward = !mqttGetter.equals(mqttSetter);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void mqttConfigure() {
|
||||
// Replace identifier
|
||||
_mqttTopic = getSetting("mqttTopic", MQTT_TOPIC);
|
||||
_mqttTopic.replace("{identifier}", getSetting("hostname"));
|
||||
if (!_mqttTopic.endsWith("/")) _mqttTopic = _mqttTopic + "/";
|
||||
_mqttSetter = getSetting("mqttSetter", MQTT_USE_SETTER);
|
||||
_mqttGetter = getSetting("mqttGetter", MQTT_USE_GETTER);
|
||||
_mqttForward = !_mqttGetter.equals(_mqttSetter);
|
||||
}
|
||||
|
||||
void mqttSetup() {
|
||||
#if MQTT_USE_ASYNC
|
||||
mqtt.onConnect([](bool sessionPresent) {
|
||||
@@ -363,7 +371,9 @@ void mqttSetup() {
|
||||
_mqttOnMessage(topic, (char *) payload, length);
|
||||
});
|
||||
#endif
|
||||
buildTopics();
|
||||
|
||||
mqttRegister(_mqttCallback);
|
||||
|
||||
}
|
||||
|
||||
void mqttLoop() {
|
||||
|
||||
@@ -52,6 +52,7 @@ void relayProviderStatus(unsigned char id, bool status) {
|
||||
|
||||
#if RELAY_PROVIDER == RELAY_PROVIDER_LIGHT
|
||||
lightState(status);
|
||||
lightUpdate(true, true);
|
||||
#endif
|
||||
|
||||
#if RELAY_PROVIDER == RELAY_PROVIDER_RELAY
|
||||
|
||||
@@ -13,7 +13,11 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#define AUTO_SAVE 1
|
||||
|
||||
#ifdef DEBUG_PORT
|
||||
Embedis embedis(DEBUG_PORT);
|
||||
#else
|
||||
Embedis embedis(Serial);
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Settings
|
||||
@@ -134,15 +138,60 @@ void settingsSetup() {
|
||||
});
|
||||
|
||||
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
|
||||
Embedis::command( F("COLOR"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
String color = String(e->argv[1]);
|
||||
parseColor(color.c_str());
|
||||
lightColor(true, true);
|
||||
}
|
||||
e->stream->printf("Color: %s\n", lightColor().c_str());
|
||||
e->response(Embedis::OK);
|
||||
});
|
||||
|
||||
Embedis::command( F("COLOR"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
String color = String(e->argv[1]);
|
||||
lightColor(color.c_str());
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
e->stream->printf("Color: %s\n", lightColor().c_str());
|
||||
e->response(Embedis::OK);
|
||||
});
|
||||
|
||||
Embedis::command( F("MIRED"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
String color = String("M") + String(e->argv[1]);
|
||||
lightColor(color.c_str());
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
e->stream->printf("Color: %s\n", lightColor().c_str());
|
||||
e->response(Embedis::OK);
|
||||
});
|
||||
|
||||
Embedis::command( F("KELVIN"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
String color = String("K") + String(e->argv[1]);
|
||||
lightColor(color.c_str());
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
e->stream->printf("Color: %s\n", lightColor().c_str());
|
||||
e->response(Embedis::OK);
|
||||
});
|
||||
|
||||
Embedis::command( F("BRIGHTNESS"), [](Embedis* e) {
|
||||
if (e->argc > 1) {
|
||||
lightBrightness(String(e->argv[1]).toInt());
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
e->stream->printf("Brightness: %d\n", lightBrightness());
|
||||
e->response(Embedis::OK);
|
||||
});
|
||||
|
||||
Embedis::command( F("CHANNEL"), [](Embedis* e) {
|
||||
if (e->argc < 2) {
|
||||
return e->response(Embedis::ARGS_ERROR);
|
||||
}
|
||||
int id = String(e->argv[1]).toInt();
|
||||
if (e->argc > 2) {
|
||||
int value = String(e->argv[2]).toInt();
|
||||
lightChannel(id, value);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
e->stream->printf("Channel #%d: %d\n", id, lightChannel(id));
|
||||
e->response(Embedis::OK);
|
||||
});
|
||||
|
||||
#endif
|
||||
|
||||
Embedis::command( F("EEPROM"), [](Embedis* e) {
|
||||
@@ -176,6 +225,15 @@ void settingsSetup() {
|
||||
|
||||
}
|
||||
|
||||
void settingsDump() {
|
||||
unsigned int size = settingsKeyCount();
|
||||
for (unsigned int i=0; i<size; i++) {
|
||||
String key = settingsKeyName(i);
|
||||
String value = getSetting(key);
|
||||
DEBUG_MSG_P(PSTR("%s => %s\n"), key.c_str(), value.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void settingsLoop() {
|
||||
embedis.process();
|
||||
}
|
||||
|
||||
@@ -145,8 +145,8 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
|
||||
|
||||
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
|
||||
if (action.equals("color") && root.containsKey("data")) {
|
||||
parseColor(root["data"]);
|
||||
lightColor(true, true);
|
||||
lightColor(root["data"]);
|
||||
lightUpdate(true, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -364,7 +364,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
|
||||
#if ENABLE_INFLUXDB
|
||||
influxDBConfigure();
|
||||
#endif
|
||||
buildTopics();
|
||||
mqttConfigure();
|
||||
|
||||
#if ENABLE_RF
|
||||
rfBuildCodes();
|
||||
@@ -454,6 +454,11 @@ void _wsStart(uint32_t client_id) {
|
||||
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
|
||||
root["colorVisible"] = 1;
|
||||
root["color"] = lightColor();
|
||||
JsonArray& channels = root.createNestedArray("channels");
|
||||
for (unsigned char id=0; id < lightChannels(); id++) {
|
||||
channels.add(lightChannel(id));
|
||||
}
|
||||
root["brightness"] = lightBrightness();
|
||||
#endif
|
||||
|
||||
root["relayMode"] = getSetting("relayMode", RELAY_MODE);
|
||||
@@ -715,8 +720,8 @@ ArRequestHandlerFunction _bindAPI(unsigned int apiID) {
|
||||
}
|
||||
|
||||
// Get response from callback
|
||||
char value[10];
|
||||
(api.getFn)(value, 10);
|
||||
char value[API_BUFFER_SIZE];
|
||||
(api.getFn)(value, API_BUFFER_SIZE);
|
||||
char *p = ltrim(value);
|
||||
|
||||
// The response will be a 404 NOT FOUND if the resource is not available
|
||||
@@ -779,7 +784,7 @@ void _onAPIs(AsyncWebServerRequest *request) {
|
||||
|
||||
} else {
|
||||
for (unsigned int i=0; i < _apis.size(); i++) {
|
||||
output += _apis[i].key + String(" -> ") + _apis[i].url + String("\n<br />");
|
||||
output += _apis[i].key + String(" -> ") + _apis[i].url + String("\n");
|
||||
}
|
||||
request->send(200, "text/plain", output);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user