Browse Source

Change the way dimmer light work and a whole lot of other stuff

fastled
Xose Pérez 7 years ago
parent
commit
3cf7d32478
9 changed files with 594 additions and 375 deletions
  1. +30
    -29
      code/espurna/config/general.h
  2. +26
    -25
      code/espurna/config/hardware.h
  3. +3
    -4
      code/espurna/espurna.ino
  4. +20
    -24
      code/espurna/hardware.ino
  5. +384
    -236
      code/espurna/light.ino
  6. +52
    -42
      code/espurna/mqtt.ino
  7. +1
    -0
      code/espurna/relay.ino
  8. +67
    -9
      code/espurna/settings.ino
  9. +11
    -6
      code/espurna/web.ino

+ 30
- 29
code/espurna/config/general.h View File

@ -50,7 +50,7 @@
#define HEARTBEAT_REPORT_FREEHEAP 1 #define HEARTBEAT_REPORT_FREEHEAP 1
#define HEARTBEAT_REPORT_VCC 1 #define HEARTBEAT_REPORT_VCC 1
#define HEARTBEAT_REPORT_RELAY 1 #define HEARTBEAT_REPORT_RELAY 1
#define HEARTBEAT_REPORT_COLOR 1
#define HEARTBEAT_REPORT_LIGHT 1
#define HEARTBEAT_REPORT_HOSTNAME 1 #define HEARTBEAT_REPORT_HOSTNAME 1
#define HEARTBEAT_REPORT_APP 1 #define HEARTBEAT_REPORT_APP 1
#define HEARTBEAT_REPORT_VERSION 1 #define HEARTBEAT_REPORT_VERSION 1
@ -187,6 +187,7 @@ PROGMEM const char* const custom_reset_string[] = {
#define WEBSERVER_PORT 80 // HTTP port #define WEBSERVER_PORT 80 // HTTP port
#define DNS_PORT 53 // MDNS port #define DNS_PORT 53 // MDNS port
#define ENABLE_MDNS 1 // Enabled MDNS #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_NORMAL 0
#define WEB_MODE_PASSWORD 1 #define WEB_MODE_PASSWORD 1
@ -234,10 +235,6 @@ PROGMEM const char* const custom_reset_string[] = {
#define MQTT_TOPIC_ACTION "action" #define MQTT_TOPIC_ACTION "action"
#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_WHITE "white"
#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"
@ -253,6 +250,13 @@ PROGMEM const char* const custom_reset_string[] = {
#define MQTT_TOPIC_TIME "time" #define MQTT_TOPIC_TIME "time"
#define MQTT_TOPIC_ANALOG "analog" #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_ONLINE "1" // Value for the device ON message
#define MQTT_STATUS_OFFLINE "0" // Value for the device OFF message (will) #define MQTT_STATUS_OFFLINE "0" // Value for the device OFF message (will)
@ -282,31 +286,28 @@ PROGMEM const char* const custom_reset_string[] = {
// LIGHT // LIGHT
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#define ENABLE_GAMMA_CORRECTION 0
#define LIGHT_PROVIDER_NONE 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_DEFAULT_COLOR "#000080"
#define LIGHT_SAVE_DELAY 5
#define LIGHT_MAX_VALUE 255
#define LIGHT_MAX_BRIGHTNESS LIGHT_MAX_VALUE
// 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_PROVIDER_MY9192 1
#define LIGHT_PROVIDER_DIMMER 2
// 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
#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 // DOMOTICZ


+ 26
- 25
code/espurna/config/hardware.h View File

@ -270,6 +270,10 @@
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT #define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_MY9192 #define LIGHT_PROVIDER LIGHT_PROVIDER_MY9192
#define MY9291_DI_PIN 13
#define MY9291_DCKI_PIN 15
#define MY9291_COMMAND MY9291_COMMAND_DEFAULT
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// LED Controller // LED Controller
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -281,19 +285,17 @@
#define LED1_PIN 2 #define LED1_PIN 2
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT #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 // HUACANXING H801
@ -306,20 +308,19 @@
#define LED1_PIN 5 #define LED1_PIN 5
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT #define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_RGB2W
#undef RGBW_INVERSE_LOGIC
#undef RGBW_RED_PIN
#undef RGBW_GREEN_PIN
#undef RGBW_BLUE_PIN
#undef RGBW_WHITE_PIN
#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_PROVIDER LIGHT_PROVIDER_DIMMER
#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 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 // Jan Goedeke Wifi Relay


+ 3
- 4
code/espurna/espurna.ino View File

@ -88,10 +88,8 @@ void heartbeat() {
#if (HEARTBEAT_REPORT_RELAY) #if (HEARTBEAT_REPORT_RELAY)
relayMQTT(); relayMQTT();
#endif #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 #endif
#if (HEARTBEAT_REPORT_VCC) #if (HEARTBEAT_REPORT_VCC)
#if ENABLE_ADC_VCC #if ENABLE_ADC_VCC
@ -250,6 +248,7 @@ void setup() {
// Prepare configuration for version 2.0 // Prepare configuration for version 2.0
hwUpwardsCompatibility(); hwUpwardsCompatibility();
//settingsDump();
} }


+ 20
- 24
code/espurna/hardware.ino View File

@ -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() { void hwUpwardsCompatibility() {
unsigned int board = getSetting("board", 0).toInt(); unsigned int board = getSetting("board", 0).toInt();
@ -234,14 +223,17 @@ void hwUpwardsCompatibility() {
#ifdef LED_CONTROLLER #ifdef LED_CONTROLLER
setSetting("board", 21); setSetting("board", 21);
setSetting("relayProvider", RELAY_PROVIDER_LIGHT); setSetting("relayProvider", RELAY_PROVIDER_LIGHT);
setSetting("lightProvider", LIGHT_PROVIDER_RGB);
setSetting("lightProvider", LIGHT_PROVIDER_DIMMER);
setSetting("ledGPIO", 1, 2); setSetting("ledGPIO", 1, 2);
setSetting("ledLogic", 1, 1); 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 #endif
#ifdef ITEAD_MOTOR #ifdef ITEAD_MOTOR
@ -267,15 +259,19 @@ void hwUpwardsCompatibility() {
#ifdef H801_LED_CONTROLLER #ifdef H801_LED_CONTROLLER
setSetting("board", 24); setSetting("board", 24);
setSetting("relayProvider", RELAY_PROVIDER_LIGHT); setSetting("relayProvider", RELAY_PROVIDER_LIGHT);
setSetting("lightProvider", LIGHT_PROVIDER_RGB2W);
setSetting("lightProvider", LIGHT_PROVIDER_DIMMER);
setSetting("ledGPIO", 5, 1); setSetting("ledGPIO", 5, 1);
setSetting("ledLogic", 1, 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 #endif
saveSettings(); saveSettings();


+ 384
- 236
code/espurna/light.ino View File

@ -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 #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
#include <Ticker.h> #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; Ticker colorTicker;
bool _lightState = false; bool _lightState = false;
float brightness = 1.0;
unsigned int _lightColor[3] = {0};
unsigned int _brightness = LIGHT_MAX_BRIGHTNESS;
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192 #if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
#include <my9291.h> #include <my9291.h>
my9291 * _my9291; my9291 * _my9291;
#endif #endif
#if ENABLE_GAMMA_CORRECTION
#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
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
#if LIGHT_ENABLE_GAMMA
// 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 };
#ifndef LIGHT_PWM_RANGE
#define LIGHT_PWM_RANGE (255)
#endif #endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// UTILS // UTILS
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void _color_string2array(const char * rgb, unsigned int * array) {
void _fromRGB(const char * rgb) {
char * p = (char *) rgb; char * p = (char *) rgb;
if (strlen(p) == 0) return; 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 color begins with a # then assume HEX RGB
if (p[0] == '#') { if (p[0] == '#') {
if (!lightHasColor()) return;
++p; ++p;
unsigned long value = strtol(p, NULL, 16);
unsigned long value = strtoul(p, NULL, 16);
// RGBA values are interpreted like RGB + brightness // RGBA values are interpreted like RGB + brightness
if (strlen(p) > 7) { 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 { } 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') {
unsigned long mireds = atol(p + 1);
_fromMireds(mireds);
// it's a temperature in kelvin
} else if (p[0] == 'K') {
p[strlen(p)-1] = 0;
unsigned long temperature = atol(p);
_color_temperature2array(temperature, array);
unsigned long kelvin = atol(p + 1);
_fromKelvin(kelvin);
// otherwise assume decimal values separated by commas // otherwise assume decimal values separated by commas
} else { } else {
char * tok; char * tok;
tok = strtok(p, ",");
array[0] = atoi(tok);
tok = strtok(NULL, ",");
unsigned char count = 0;
unsigned char channels = _channels.size();
// if there are more than one value assume R,G,B
if (tok != NULL) {
array[1] = atoi(tok);
tok = strtok(p, ",");
while (tok != NULL) {
_channels[count].value = atoi(tok);
if (++count == channels) break;
tok = strtok(NULL, ","); 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 // 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 // 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 // Calculate colors
unsigned int red = (kelvin <= 66) 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); : 138.5177312231 * log(kelvin - 10) - 305.0447927307);
// Save values // 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) {
intensity = brightness * intensity;
// 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);
}
#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 #else
unsigned int pwm = intensity;
unsigned int pwm = map(value, 0, LIGHT_MAX_VALUE, 0, LIGHT_MAX_PWM);
#endif #endif
#if RGBW_INVERSE_LOGIC != 1
pwm = LIGHT_PWM_RANGE - pwm;
#endif
if (reverse) pwm = LIGHT_MAX_PWM - pwm;
return 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 // PROVIDER
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void _lightProviderSet(bool state, unsigned int red, unsigned int green, unsigned int blue, float brightness) {
unsigned int white = 0;
#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
#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)
// 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
void _shadow() {
}
bool useWhite = getSetting("useWhite", LIGHT_USE_WHITE).toInt() == 1;
// -----------------------------------------------------------------------------
// LIGHT MANAGEMENT
// -----------------------------------------------------------------------------
for (unsigned int i=0; i < _channels.size(); i++) {
_channels[i].shadow = _lightState ? _channels[i].value : 0;
}
void lightState(bool state) {
_lightState = state;
_lightProviderSet(_lightState, _lightColor[0], _lightColor[1], _lightColor[2], brightness);
}
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;
}
}
bool lightState() {
return _lightState;
}
void parseColor(const char * color) {
brightness = 1.0;
_color_string2array(color, _lightColor);
} }
void _lightProviderUpdate() {
void lightColor(bool save, bool forward) {
_shadow();
_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 LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
if ((_lightColor[0] == _lightColor[1]) & (_lightColor[1] == _lightColor[2])) {
if (_lightState) {
// 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 { } 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() { 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(); saveSettings();
} }
void _lightColorRestore() { 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 // 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) { if (type == MQTT_CONNECT_EVENT) {
mqttSubscribe(MQTT_TOPIC_BRIGHTNESS); mqttSubscribe(MQTT_TOPIC_BRIGHTNESS);
mqttSubscribe(MQTT_TOPIC_COLORTEMP);
mqttSubscribe(MQTT_TOPIC_MIRED);
mqttSubscribe(MQTT_TOPIC_KELVIN);
mqttSubscribe(MQTT_TOPIC_COLOR); 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) { if (type == MQTT_MESSAGE_EVENT) {
@ -328,86 +302,260 @@ 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);
// 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
if (t.equals(MQTT_TOPIC_COLOR)) {
parseColor(payload);
lightColor(true, mqttForward());
// Color temperature in kelvins
if (t.equals(MQTT_TOPIC_KELVIN)) {
_fromKelvin(atol(payload));
lightUpdate(true, mqttForward());
} }
// White
if (t.equals(MQTT_TOPIC_WHITE)) {
parseColor(payload);
lightColor(true, mqttForward());
// Color
if (t.equals(MQTT_TOPIC_COLOR)) {
lightColor(payload);
lightUpdate(true, mqttForward());
} }
// Brightness // Brightness
if (t.equals(MQTT_TOPIC_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());
}
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// SETUP
// API
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void lightSetup() {
unsigned char lightChannels() {
return _channels.size();
}
#if LIGHT_PROVIDER == LIGHT_PROVIDER_MY9192
_my9291 = new my9291(MY9291_DI_PIN, MY9291_DCKI_PIN, MY9291_COMMAND);
#endif
bool lightHasColor() {
return _channels.size() > 2;
}
#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
unsigned char lightWhiteChannels() {
return _channels.size() % 3;
}
_lightColorRestore();
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 _lightAPISetup() {
// API entry points (protected with apikey) // 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) { [](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, apiRegister(MQTT_TOPIC_BRIGHTNESS, MQTT_TOPIC_BRIGHTNESS,
[](char * buffer, size_t len) { [](char * buffer, size_t len) {
snprintf(buffer, len, "%d", (int) (brightness * LIGHT_MAX_BRIGHTNESS));
snprintf(buffer, len, "%d", _brightness);
}, },
[](const char * payload) { [](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);
} }


+ 52
- 42
code/espurna/mqtt.ino View File

@ -24,7 +24,10 @@ PubSubClient mqtt(mqttWiFiClient);
bool _mqttConnected = false; bool _mqttConnected = false;
#endif #endif
String mqttTopic;
String _mqttTopic;
String _mqttSetter;
String _mqttGetter;
bool _mqttForward; bool _mqttForward;
char *_mqttUser = 0; char *_mqttUser = 0;
char *_mqttPass = 0; char *_mqttPass = 0;
@ -53,30 +56,17 @@ void mqttDisconnect() {
mqtt.disconnect(); mqtt.disconnect();
} }
void buildTopics() {
// Replace identifier
mqttTopic = getSetting("mqttTopic", MQTT_TOPIC);
mqttTopic.replace("{identifier}", getSetting("hostname"));
if (!mqttTopic.endsWith("/")) mqttTopic = mqttTopic + "/";
}
bool mqttForward() { bool mqttForward() {
return _mqttForward; return _mqttForward;
} }
String mqttSubtopic(char * topic) { String mqttSubtopic(char * topic) {
String response; String response;
String t = String(topic); 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; return response;
} }
void mqttSendRaw(const char * topic, const char * message) { void mqttSendRaw(const char * topic, const char * message) {
@ -107,7 +97,7 @@ void _mqttFlush() {
String output; String output;
root.printTo(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()); mqttSendRaw(path.c_str(), output.c_str());
for (unsigned char i = 0; i < _mqtt_queue.size(); i++) { 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); _mqtt_queue.push_back(element);
mqttFlushTicker.once_ms(MQTT_USE_JSON_DELAY, _mqttFlush); mqttFlushTicker.once_ms(MQTT_USE_JSON_DELAY, _mqttFlush);
} else { } 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); mqttSendRaw(path.c_str(), message);
} }
} }
@ -161,17 +150,42 @@ void mqttSubscribeRaw(const char * topic) {
} }
void mqttSubscribe(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()); mqttSubscribeRaw(path.c_str());
} }
void mqttRegister(void (*callback)(unsigned int, const char *, const char *)) {
_mqtt_callbacks.push_back(callback);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Callbacks // 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() { void _mqttOnConnect() {
@ -183,14 +197,11 @@ void _mqttOnConnect() {
#endif #endif
// Build MQTT topics // Build MQTT topics
buildTopics();
mqttConfigure();
// Send first Heartbeat // Send first Heartbeat
heartbeat(); heartbeat();
// Subscribe to system topics
mqttSubscribe(MQTT_TOPIC_ACTION);
// Send connect event to subscribers // Send connect event to subscribers
for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) { for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) {
(*_mqtt_callbacks[i])(MQTT_CONNECT_EVENT, NULL, NULL); (*_mqtt_callbacks[i])(MQTT_CONNECT_EVENT, NULL, NULL);
@ -225,15 +236,6 @@ void _mqttOnMessage(char* topic, char* payload, unsigned int len) {
#endif #endif
DEBUG_MSG_P(PSTR("\n")); 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 // Send message event to subscribers
for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) { for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) {
(*_mqtt_callbacks[i])(MQTT_MESSAGE_EVENT, topic, message); (*_mqtt_callbacks[i])(MQTT_MESSAGE_EVENT, topic, message);
@ -274,7 +276,7 @@ void mqttConnect() {
_mqttUser = strdup(getSetting("mqttUser").c_str()); _mqttUser = strdup(getSetting("mqttUser").c_str());
_mqttPass = strdup(getSetting("mqttPassword").c_str()); _mqttPass = strdup(getSetting("mqttPassword").c_str());
if (_mqttWill) free(_mqttWill); 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); DEBUG_MSG_P(PSTR("[MQTT] Connecting to broker at %s:%d"), host, port);
mqtt.setServer(host, port); mqtt.setServer(host, port);
@ -313,14 +315,20 @@ void mqttConnect() {
free(host); 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() { void mqttSetup() {
#if MQTT_USE_ASYNC #if MQTT_USE_ASYNC
mqtt.onConnect([](bool sessionPresent) { mqtt.onConnect([](bool sessionPresent) {
@ -363,7 +371,9 @@ void mqttSetup() {
_mqttOnMessage(topic, (char *) payload, length); _mqttOnMessage(topic, (char *) payload, length);
}); });
#endif #endif
buildTopics();
mqttRegister(_mqttCallback);
} }
void mqttLoop() { void mqttLoop() {


+ 1
- 0
code/espurna/relay.ino View File

@ -52,6 +52,7 @@ void relayProviderStatus(unsigned char id, bool status) {
#if RELAY_PROVIDER == RELAY_PROVIDER_LIGHT #if RELAY_PROVIDER == RELAY_PROVIDER_LIGHT
lightState(status); lightState(status);
lightUpdate(true, true);
#endif #endif
#if RELAY_PROVIDER == RELAY_PROVIDER_RELAY #if RELAY_PROVIDER == RELAY_PROVIDER_RELAY


+ 67
- 9
code/espurna/settings.ino View File

@ -13,7 +13,11 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
#define AUTO_SAVE 1 #define AUTO_SAVE 1
#ifdef DEBUG_PORT
Embedis embedis(DEBUG_PORT);
#else
Embedis embedis(Serial); Embedis embedis(Serial);
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Settings // Settings
@ -134,15 +138,60 @@ void settingsSetup() {
}); });
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE #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 #endif
Embedis::command( F("EEPROM"), [](Embedis* e) { 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() { void settingsLoop() {
embedis.process(); embedis.process();
} }


+ 11
- 6
code/espurna/web.ino View File

@ -145,8 +145,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")) {
parseColor(root["data"]);
lightColor(true, true);
lightColor(root["data"]);
lightUpdate(true, true);
} }
#endif #endif
@ -364,7 +364,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) {
#if ENABLE_INFLUXDB #if ENABLE_INFLUXDB
influxDBConfigure(); influxDBConfigure();
#endif #endif
buildTopics();
mqttConfigure();
#if ENABLE_RF #if ENABLE_RF
rfBuildCodes(); rfBuildCodes();
@ -454,6 +454,11 @@ void _wsStart(uint32_t client_id) {
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
root["colorVisible"] = 1; root["colorVisible"] = 1;
root["color"] = lightColor(); root["color"] = lightColor();
JsonArray& channels = root.createNestedArray("channels");
for (unsigned char id=0; id < lightChannels(); id++) {
channels.add(lightChannel(id));
}
root["brightness"] = lightBrightness();
#endif #endif
root["relayMode"] = getSetting("relayMode", RELAY_MODE); root["relayMode"] = getSetting("relayMode", RELAY_MODE);
@ -715,8 +720,8 @@ ArRequestHandlerFunction _bindAPI(unsigned int apiID) {
} }
// Get response from callback // 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); char *p = ltrim(value);
// The response will be a 404 NOT FOUND if the resource is not available // The response will be a 404 NOT FOUND if the resource is not available
@ -779,7 +784,7 @@ void _onAPIs(AsyncWebServerRequest *request) {
} else { } else {
for (unsigned int i=0; i < _apis.size(); i++) { 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); request->send(200, "text/plain", output);
} }


Loading…
Cancel
Save