+1
-1
@@ -27,7 +27,7 @@ lib_deps_external =
|
||||
#[email protected]
|
||||
#webserver
|
||||
[email protected]
|
||||
NeoPixelBus@2.3.4
|
||||
NeoPixelBus@2.4.1
|
||||
#[email protected]
|
||||
#[email protected]
|
||||
#[email protected]
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||

|
||||

|
||||
|
||||
## Welcome to my project WLED! (v0.8.2)
|
||||
[](https://github.com/Aircoookie/WLED/releases)
|
||||
[](https://discord.gg/KuqP7NE)
|
||||
[](https://github.com/Aircoookie/WLED/wiki)
|
||||
[](https://github.com/Aircoookie/WLED-App)
|
||||
|
||||
## Welcome to my project WLED!
|
||||
|
||||
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B) LEDs!
|
||||
|
||||
### Features:
|
||||
- WS2812FX library integrated for 75 special effects
|
||||
- WS2812FX library integrated for 80 special effects
|
||||
- FastLED noise effects and palettes
|
||||
- Customizable Mobile and desktop UI with color and effect controls
|
||||
- Settings page - configuration over network
|
||||
@@ -19,13 +24,14 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
|
||||
- Configurable Auto Brightness limit for safer operation
|
||||
|
||||
### Supported light control interfaces:
|
||||
- WLED Android app
|
||||
- HTTP request API
|
||||
- Blynk IoT
|
||||
- MQTT
|
||||
- E1.31
|
||||
- Hyperion
|
||||
- UDP realtime
|
||||
- Alexa smart device (including dimming)
|
||||
- Alexa voice control (including dimming and color)
|
||||
- Sync to Philips hue lights
|
||||
- Adalight (PC ambilight via serial)
|
||||
- Sync color of multiple WLED devices (UDP notifier)
|
||||
@@ -45,10 +51,4 @@ Uses Linearicons by Perxis!
|
||||
|
||||
Join the Discord [server](https://discord.gg/KuqP7NE) to discuss everything about WLED!
|
||||
You can also send me mails to [[email protected]](mailto:[email protected]).
|
||||
If WLED really brightens up your every day, you can [send me a small gift](https://paypal.me/aircoookie)!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
If WLED really brightens up your every day, you can [](https://paypal.me/aircoookie)
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
#ifndef NpbWrapper_h
|
||||
#define NpbWrapper_h
|
||||
|
||||
#define WORKAROUND_ESP32_BITBANG
|
||||
//#define WORKAROUND_ESP32_BITBANG
|
||||
//see https://github.com/Aircoookie/WLED/issues/2 for flicker free ESP32 support
|
||||
|
||||
//PIN CONFIGURATION
|
||||
@@ -19,12 +19,12 @@
|
||||
#define PIXELMETHOD NeoEsp32BitBangWs2813Method
|
||||
#pragma message "Software BitBang is used because of your NeoPixelBus version. Look in NpbWrapper.h for instructions on how to mitigate flickering."
|
||||
#else
|
||||
#define PIXELMETHOD NeoEsp32RmtWS2813_V3Method
|
||||
#define PIXELMETHOD NeoWs2813Method
|
||||
#endif
|
||||
#else //esp8266
|
||||
//autoselect the right method depending on strip pin
|
||||
#if LEDPIN == 2
|
||||
#define PIXELMETHOD NeoEsp8266Uart800KbpsMethod
|
||||
#define PIXELMETHOD NeoEsp8266UartWs2813Method //if you get an error here, try to change to NeoEsp8266Uart1Ws2813Method or use Neopixelbus v2.3.5
|
||||
#elif LEDPIN == 3
|
||||
#define PIXELMETHOD NeoEsp8266Dma800KbpsMethod
|
||||
#else
|
||||
|
||||
+290
-969
File diff suppressed because it is too large
Load Diff
+168
-39
@@ -1,15 +1,7 @@
|
||||
//pixelmethod now in NpbWrapper.h
|
||||
|
||||
/*
|
||||
WS2812FX.h - Library for WS2812 LED effects.
|
||||
Harm Aldick - 2016
|
||||
www.aldick.org
|
||||
FEATURES
|
||||
* A lot of blinken modes and counting
|
||||
* WS2812FX can be used as drop-in replacement for Adafruit NeoPixel Library
|
||||
NOTES
|
||||
* Uses the Adafruit NeoPixel library. Get it here:
|
||||
https://github.com/adafruit/Adafruit_NeoPixel
|
||||
LICENSE
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2016 Harm Aldick
|
||||
@@ -28,11 +20,7 @@
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
CHANGELOG
|
||||
2016-05-28 Initial beta release
|
||||
2016-06-03 Code cleanup, minor improvements, new modes
|
||||
2016-06-04 2 new fx, fixed setColor (now also resets _mode_color)
|
||||
2017-02-02 added external trigger functionality (e.g. for sound-to-light)
|
||||
|
||||
Modified for WLED
|
||||
*/
|
||||
|
||||
@@ -85,7 +73,7 @@
|
||||
#define REVERSE (uint8_t)0x80
|
||||
#define IS_REVERSE ((SEGMENT.options & REVERSE) == REVERSE)
|
||||
|
||||
#define MODE_COUNT 79
|
||||
#define MODE_COUNT 80
|
||||
|
||||
#define FX_MODE_STATIC 0
|
||||
#define FX_MODE_BLINK 1
|
||||
@@ -103,10 +91,10 @@
|
||||
#define FX_MODE_THEATER_CHASE 13
|
||||
#define FX_MODE_THEATER_CHASE_RAINBOW 14
|
||||
#define FX_MODE_RUNNING_LIGHTS 15
|
||||
#define FX_MODE_TWINKLE 16
|
||||
#define FX_MODE_TWINKLE_RANDOM 17
|
||||
#define FX_MODE_TWINKLE_FADE 18
|
||||
#define FX_MODE_TWINKLE_FADE_RANDOM 19
|
||||
#define FX_MODE_SAW 16
|
||||
#define FX_MODE_TWINKLE 17
|
||||
#define FX_MODE_DISSOLVE 18
|
||||
#define FX_MODE_DISSOLVE_RANDOM 19
|
||||
#define FX_MODE_SPARKLE 20
|
||||
#define FX_MODE_FLASH_SPARKLE 21
|
||||
#define FX_MODE_HYPER_SPARKLE 22
|
||||
@@ -130,7 +118,7 @@
|
||||
#define FX_MODE_LARSON_SCANNER 40
|
||||
#define FX_MODE_COMET 41
|
||||
#define FX_MODE_FIREWORKS 42
|
||||
#define FX_MODE_FIREWORKS_RANDOM 43
|
||||
#define FX_MODE_RAIN 43
|
||||
#define FX_MODE_MERRY_CHRISTMAS 44
|
||||
#define FX_MODE_FIRE_FLICKER 45
|
||||
#define FX_MODE_GRADIENT 46
|
||||
@@ -167,6 +155,7 @@
|
||||
#define FX_MODE_METEOR 76
|
||||
#define FX_MODE_METEOR_SMOOTH 77
|
||||
#define FX_MODE_RAILWAY 78
|
||||
#define FX_MODE_RIPPLE 79
|
||||
|
||||
|
||||
class WS2812FX {
|
||||
@@ -210,10 +199,10 @@ class WS2812FX {
|
||||
_mode[FX_MODE_FADE] = &WS2812FX::mode_fade;
|
||||
_mode[FX_MODE_THEATER_CHASE] = &WS2812FX::mode_theater_chase;
|
||||
_mode[FX_MODE_THEATER_CHASE_RAINBOW] = &WS2812FX::mode_theater_chase_rainbow;
|
||||
_mode[FX_MODE_SAW] = &WS2812FX::mode_saw;
|
||||
_mode[FX_MODE_TWINKLE] = &WS2812FX::mode_twinkle;
|
||||
_mode[FX_MODE_TWINKLE_RANDOM] = &WS2812FX::mode_twinkle_random;
|
||||
_mode[FX_MODE_TWINKLE_FADE] = &WS2812FX::mode_twinkle_fade;
|
||||
_mode[FX_MODE_TWINKLE_FADE_RANDOM] = &WS2812FX::mode_twinkle_fade_random;
|
||||
_mode[FX_MODE_DISSOLVE] = &WS2812FX::mode_dissolve;
|
||||
_mode[FX_MODE_DISSOLVE_RANDOM] = &WS2812FX::mode_dissolve_random;
|
||||
_mode[FX_MODE_SPARKLE] = &WS2812FX::mode_sparkle;
|
||||
_mode[FX_MODE_FLASH_SPARKLE] = &WS2812FX::mode_flash_sparkle;
|
||||
_mode[FX_MODE_HYPER_SPARKLE] = &WS2812FX::mode_hyper_sparkle;
|
||||
@@ -237,7 +226,7 @@ class WS2812FX {
|
||||
_mode[FX_MODE_LARSON_SCANNER] = &WS2812FX::mode_larson_scanner;
|
||||
_mode[FX_MODE_COMET] = &WS2812FX::mode_comet;
|
||||
_mode[FX_MODE_FIREWORKS] = &WS2812FX::mode_fireworks;
|
||||
_mode[FX_MODE_FIREWORKS_RANDOM] = &WS2812FX::mode_fireworks_random;
|
||||
_mode[FX_MODE_RAIN] = &WS2812FX::mode_rain;
|
||||
_mode[FX_MODE_MERRY_CHRISTMAS] = &WS2812FX::mode_merry_christmas;
|
||||
_mode[FX_MODE_FIRE_FLICKER] = &WS2812FX::mode_fire_flicker;
|
||||
_mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient;
|
||||
@@ -275,15 +264,16 @@ class WS2812FX {
|
||||
_mode[FX_MODE_METEOR] = &WS2812FX::mode_meteor;
|
||||
_mode[FX_MODE_METEOR_SMOOTH] = &WS2812FX::mode_meteor_smooth;
|
||||
_mode[FX_MODE_RAILWAY] = &WS2812FX::mode_railway;
|
||||
|
||||
_mode[FX_MODE_RIPPLE] = &WS2812FX::mode_ripple;
|
||||
|
||||
_brightness = DEFAULT_BRIGHTNESS;
|
||||
_running = false;
|
||||
_num_segments = 1;
|
||||
_segments[0].mode = DEFAULT_MODE;
|
||||
_segments[0].colors[0] = DEFAULT_COLOR;
|
||||
_segments[0].start = 0;
|
||||
_segments[0].speed = DEFAULT_SPEED;
|
||||
currentPalette = CRGBPalette16(CRGB::Black);
|
||||
targetPalette = CloudColors_p;
|
||||
_reverseMode = false;
|
||||
_skipFirstMode = false;
|
||||
colorOrder = 0;
|
||||
@@ -292,6 +282,7 @@ class WS2812FX {
|
||||
ablMilliampsMax = 750;
|
||||
currentMilliamps = 0;
|
||||
_locked = NULL;
|
||||
_modeUsesLock = false;
|
||||
_cronixieDigits = new byte[6];
|
||||
bus = new NeoPixelWrapper();
|
||||
RESET_RUNTIME;
|
||||
@@ -300,8 +291,7 @@ class WS2812FX {
|
||||
void
|
||||
init(bool supportWhite, uint16_t countPixels, bool skipFirst),
|
||||
service(void),
|
||||
clear(void),
|
||||
strip_off(void),
|
||||
blur(uint8_t),
|
||||
fade_out(uint8_t r),
|
||||
setMode(uint8_t m),
|
||||
setSpeed(uint8_t s),
|
||||
@@ -371,12 +361,12 @@ class WS2812FX {
|
||||
color_wipe(uint32_t, uint32_t, bool , bool),
|
||||
scan(bool),
|
||||
theater_chase(uint32_t, uint32_t, bool),
|
||||
twinkle(uint32_t),
|
||||
twinkle_fade(uint32_t),
|
||||
chase(uint32_t, uint32_t, uint32_t, uint8_t),
|
||||
running_base(bool),
|
||||
dissolve(uint32_t),
|
||||
chase(uint32_t, uint32_t, uint32_t, bool),
|
||||
gradient_base(bool),
|
||||
running(uint32_t, uint32_t),
|
||||
fireworks(uint32_t),
|
||||
tricolor_chase(uint32_t, uint32_t, uint32_t);
|
||||
tricolor_chase(uint32_t, uint32_t);
|
||||
|
||||
// builtin modes
|
||||
uint16_t
|
||||
@@ -400,10 +390,10 @@ class WS2812FX {
|
||||
mode_rainbow(void),
|
||||
mode_rainbow_cycle(void),
|
||||
mode_running_lights(void),
|
||||
mode_saw(void),
|
||||
mode_twinkle(void),
|
||||
mode_twinkle_random(void),
|
||||
mode_twinkle_fade(void),
|
||||
mode_twinkle_fade_random(void),
|
||||
mode_dissolve(void),
|
||||
mode_dissolve_random(void),
|
||||
mode_sparkle(void),
|
||||
mode_flash_sparkle(void),
|
||||
mode_hyper_sparkle(void),
|
||||
@@ -423,7 +413,7 @@ class WS2812FX {
|
||||
mode_larson_scanner(void),
|
||||
mode_comet(void),
|
||||
mode_fireworks(void),
|
||||
mode_fireworks_random(void),
|
||||
mode_rain(void),
|
||||
mode_merry_christmas(void),
|
||||
mode_halloween(void),
|
||||
mode_fire_flicker(void),
|
||||
@@ -438,6 +428,7 @@ class WS2812FX {
|
||||
mode_tricolor_chase(void),
|
||||
mode_tricolor_wipe(void),
|
||||
mode_tricolor_fade(void),
|
||||
mode_lightning(void),
|
||||
mode_icu(void),
|
||||
mode_multi_comet(void),
|
||||
mode_dual_larson_scanner(void),
|
||||
@@ -459,22 +450,25 @@ class WS2812FX {
|
||||
mode_meteor(void),
|
||||
mode_meteor_smooth(void),
|
||||
mode_railway(void),
|
||||
mode_lightning(void);
|
||||
mode_ripple(void);
|
||||
|
||||
private:
|
||||
NeoPixelWrapper *bus;
|
||||
|
||||
CRGB fastled_from_col(uint32_t);
|
||||
CRGBPalette16 currentPalette;
|
||||
CRGBPalette16 targetPalette;
|
||||
|
||||
uint16_t _length;
|
||||
uint16_t _rand16seed;
|
||||
uint8_t _brightness;
|
||||
|
||||
void handle_palette(void);
|
||||
void fill(uint32_t);
|
||||
bool modeUsesLock(uint8_t);
|
||||
|
||||
boolean
|
||||
_running,
|
||||
bool
|
||||
_modeUsesLock,
|
||||
_rgbwMode,
|
||||
_reverseMode,
|
||||
_cronixieMode,
|
||||
@@ -500,4 +494,139 @@ class WS2812FX {
|
||||
segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 17 bytes per element
|
||||
};
|
||||
|
||||
|
||||
const char JSON_mode_names[] PROGMEM = R"=====({"effects":[
|
||||
"Solid",
|
||||
"Blink",
|
||||
"Breathe",
|
||||
"Wipe",
|
||||
"Wipe Random",
|
||||
"Random Colors",
|
||||
"Sweep",
|
||||
"Dynamic",
|
||||
"Colorloop",
|
||||
"Rainbow",
|
||||
"Scan",
|
||||
"Dual Scan",
|
||||
"Fade",
|
||||
"Chase",
|
||||
"Chase Rainbow",
|
||||
"Running",
|
||||
"Saw",
|
||||
"Twinkle",
|
||||
"Dissolve",
|
||||
"Dissolve Rnd",
|
||||
"Sparkle",
|
||||
"Dark Sparkle",
|
||||
"Sparkle+",
|
||||
"Strobe",
|
||||
"Strobe Rainbow",
|
||||
"Mega Strobe",
|
||||
"Blink Rainbow",
|
||||
"Android",
|
||||
"Chase",
|
||||
"Chase Random",
|
||||
"Chase Rainbow",
|
||||
"Chase Flash",
|
||||
"Chase Flash Rnd",
|
||||
"Rainbow Runner",
|
||||
"Colorful",
|
||||
"Traffic Light",
|
||||
"Sweep Random",
|
||||
"Running 2",
|
||||
"Red & Blue",
|
||||
"Stream",
|
||||
"Scanner",
|
||||
"Lighthouse",
|
||||
"Fireworks",
|
||||
"Rain",
|
||||
"Merry Christmas",
|
||||
"Fire Flicker",
|
||||
"Gradient",
|
||||
"Loading",
|
||||
"In Out",
|
||||
"In In",
|
||||
"Out Out",
|
||||
"Out In",
|
||||
"Circus",
|
||||
"Halloween",
|
||||
"Tri Chase",
|
||||
"Tri Wipe",
|
||||
"Tri Fade",
|
||||
"Lightning",
|
||||
"ICU",
|
||||
"Multi Comet",
|
||||
"Dual Scanner",
|
||||
"Stream 2",
|
||||
"Oscillate",
|
||||
"Pride 2015",
|
||||
"Juggle",
|
||||
"Palette",
|
||||
"Fire 2012",
|
||||
"Colorwaves",
|
||||
"BPM",
|
||||
"Fill Noise",
|
||||
"Noise 1",
|
||||
"Noise 2",
|
||||
"Noise 3",
|
||||
"Noise 4",
|
||||
"Colortwinkle",
|
||||
"Lake",
|
||||
"Meteor",
|
||||
"Smooth Meteor",
|
||||
"Railway",
|
||||
"Ripple"
|
||||
]})=====";
|
||||
|
||||
|
||||
const char JSON_palette_names[] PROGMEM = R"=====({"palettes":[
|
||||
"Default",
|
||||
"Random Cycle",
|
||||
"Primary Color",
|
||||
"Based on Primary",
|
||||
"Set Colors",
|
||||
"Based on Set",
|
||||
"Party",
|
||||
"Cloud",
|
||||
"Lava",
|
||||
"Ocean",
|
||||
"Forest",
|
||||
"Rainbow",
|
||||
"Rainbow Bands",
|
||||
"Sunset",
|
||||
"Rivendell",
|
||||
"Breeze",
|
||||
"Red & Blue",
|
||||
"Yellowout",
|
||||
"Analogous",
|
||||
"Splash",
|
||||
"Pastel",
|
||||
"Sunset 2",
|
||||
"Beech",
|
||||
"Vintage",
|
||||
"Departure",
|
||||
"Landscape",
|
||||
"Beach",
|
||||
"Sherbet",
|
||||
"Hult",
|
||||
"Hult 64",
|
||||
"Drywet",
|
||||
"Jul",
|
||||
"Grintage",
|
||||
"Rewhi",
|
||||
"Tertiary",
|
||||
"Fire",
|
||||
"Icefire",
|
||||
"Cyane",
|
||||
"Light Pink",
|
||||
"Autumn",
|
||||
"Magenta",
|
||||
"Magred",
|
||||
"Yelmag",
|
||||
"Yelblu",
|
||||
"Orange & Teal",
|
||||
"Tiamat",
|
||||
"April Night"
|
||||
]})=====";
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,717 @@
|
||||
/*
|
||||
WS2812FX_fcn.cpp contains all utility functions
|
||||
Harm Aldick - 2016
|
||||
www.aldick.org
|
||||
LICENSE
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2016 Harm Aldick
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Modified heavily for WLED
|
||||
*/
|
||||
|
||||
#include "WS2812FX.h"
|
||||
#include "palettes.h"
|
||||
|
||||
#define LED_SKIP_AMOUNT 1
|
||||
|
||||
void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst)
|
||||
{
|
||||
if (supportWhite == _rgbwMode && countPixels == _length && _locked != NULL) return;
|
||||
RESET_RUNTIME;
|
||||
_rgbwMode = supportWhite;
|
||||
_skipFirstMode = skipFirst;
|
||||
_length = countPixels;
|
||||
|
||||
uint8_t ty = 1;
|
||||
if (supportWhite) ty =2;
|
||||
uint16_t lengthRaw = _length;
|
||||
if (_skipFirstMode) lengthRaw += LED_SKIP_AMOUNT;
|
||||
bus->Begin((NeoPixelType)ty, lengthRaw);
|
||||
|
||||
if (_locked != NULL) delete _locked;
|
||||
_locked = new byte[_length];
|
||||
|
||||
_segments[0].start = 0;
|
||||
_segments[0].stop = _length -1;
|
||||
|
||||
unlockAll();
|
||||
setBrightness(_brightness);
|
||||
}
|
||||
|
||||
void WS2812FX::service() {
|
||||
unsigned long now = millis(); // Be aware, millis() rolls over every 49 days
|
||||
bool doShow = false;
|
||||
for(uint8_t i=0; i < _num_segments; i++)
|
||||
{
|
||||
_segment_index = i;
|
||||
if(now > SEGMENT_RUNTIME.next_time || _triggered)
|
||||
{
|
||||
doShow = true;
|
||||
handle_palette();
|
||||
uint16_t delay = (this->*_mode[SEGMENT.mode])();
|
||||
SEGMENT_RUNTIME.next_time = now + max(delay, 5);
|
||||
SEGMENT_RUNTIME.counter_mode_call++;
|
||||
}
|
||||
}
|
||||
if(doShow) {
|
||||
show();
|
||||
}
|
||||
_triggered = false;
|
||||
}
|
||||
|
||||
bool WS2812FX::modeUsesLock(uint8_t m)
|
||||
{
|
||||
if (m == FX_MODE_FIRE_2012 || m == FX_MODE_COLORTWINKLE ||
|
||||
m == FX_MODE_METEOR || m == FX_MODE_METEOR_SMOOTH ||
|
||||
m == FX_MODE_RIPPLE) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void WS2812FX::setPixelColor(uint16_t n, uint32_t c) {
|
||||
uint8_t w = (c >> 24) & 0xFF;
|
||||
uint8_t r = (c >> 16) & 0xFF;
|
||||
uint8_t g = (c >> 8) & 0xFF;
|
||||
uint8_t b = c & 0xFF;
|
||||
setPixelColor(n, r, g, b, w);
|
||||
}
|
||||
|
||||
void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
{
|
||||
if (_locked[i] && !_modeUsesLock) return;
|
||||
if (_reverseMode) i = _length - 1 -i;
|
||||
if (IS_REVERSE) i = SEGMENT.stop - (i - SEGMENT.start); //reverse just individual segment
|
||||
byte tmpg = g;
|
||||
switch (colorOrder) //0 = Grb, default
|
||||
{
|
||||
case 0: break; //0 = Grb, default
|
||||
case 1: g = r; r = tmpg; break; //1 = Rgb, common for WS2811
|
||||
case 2: g = b; b = tmpg; break; //2 = Brg
|
||||
case 3: g = b; b = r; r = tmpg; //3 = Rbg
|
||||
}
|
||||
if (!_cronixieMode)
|
||||
{
|
||||
if (_skipFirstMode)
|
||||
{
|
||||
if (i < LED_SKIP_AMOUNT) bus->SetPixelColor(i, RgbwColor(0,0,0,0));
|
||||
i += LED_SKIP_AMOUNT;
|
||||
}
|
||||
|
||||
bus->SetPixelColor(i, RgbwColor(r,g,b,w));
|
||||
} else {
|
||||
if(i>6)return;
|
||||
byte o = 10*i;
|
||||
if (_cronixieBacklightEnabled && _cronixieDigits[i] <11)
|
||||
{
|
||||
byte r2 = (_segments[0].colors[1] >>16) & 0xFF;
|
||||
byte g2 = (_segments[0].colors[1] >> 8) & 0xFF;
|
||||
byte b2 = (_segments[0].colors[1] ) & 0xFF;
|
||||
byte w2 = (_segments[0].colors[1] >>24) & 0xFF;
|
||||
for (int j=o; j< o+19; j++)
|
||||
{
|
||||
bus->SetPixelColor(j, RgbwColor(r2,g2,b2,w2));
|
||||
}
|
||||
} else
|
||||
{
|
||||
for (int j=o; j< o+19; j++)
|
||||
{
|
||||
bus->SetPixelColor(j, RgbwColor(0,0,0,0));
|
||||
}
|
||||
}
|
||||
if (_skipFirstMode) o += LED_SKIP_AMOUNT;
|
||||
switch(_cronixieDigits[i])
|
||||
{
|
||||
case 0: bus->SetPixelColor(o+5, RgbwColor(r,g,b,w)); break;
|
||||
case 1: bus->SetPixelColor(o+0, RgbwColor(r,g,b,w)); break;
|
||||
case 2: bus->SetPixelColor(o+6, RgbwColor(r,g,b,w)); break;
|
||||
case 3: bus->SetPixelColor(o+1, RgbwColor(r,g,b,w)); break;
|
||||
case 4: bus->SetPixelColor(o+7, RgbwColor(r,g,b,w)); break;
|
||||
case 5: bus->SetPixelColor(o+2, RgbwColor(r,g,b,w)); break;
|
||||
case 6: bus->SetPixelColor(o+8, RgbwColor(r,g,b,w)); break;
|
||||
case 7: bus->SetPixelColor(o+3, RgbwColor(r,g,b,w)); break;
|
||||
case 8: bus->SetPixelColor(o+9, RgbwColor(r,g,b,w)); break;
|
||||
case 9: bus->SetPixelColor(o+4, RgbwColor(r,g,b,w)); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::setReverseMode(bool b)
|
||||
{
|
||||
_reverseMode = b;
|
||||
}
|
||||
|
||||
void WS2812FX::driverModeCronixie(bool b)
|
||||
{
|
||||
_cronixieMode = b;
|
||||
_segments[0].stop = (b) ? 5 : _length-1;
|
||||
}
|
||||
|
||||
void WS2812FX::setCronixieBacklight(bool b)
|
||||
{
|
||||
_cronixieBacklightEnabled = b;
|
||||
}
|
||||
|
||||
void WS2812FX::setCronixieDigits(byte d[])
|
||||
{
|
||||
for (int i = 0; i<6; i++)
|
||||
{
|
||||
_cronixieDigits[i] = d[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//DISCLAIMER
|
||||
//The following function attemps to calculate the current LED power usage,
|
||||
//and will limit the brightness to stay below a set amperage threshold.
|
||||
//It is NOT a measurement and NOT guaranteed to stay within the ablMilliampsMax margin.
|
||||
//Stay safe with high amperage and have a reasonable safety margin!
|
||||
//I am NOT to be held liable for burned down garages!
|
||||
|
||||
//fine tune power estimation constants for your setup
|
||||
#define PU_PER_MA 3600 //power units per milliamperere for accurate power estimation
|
||||
//formula: 195075 divided by mA per fully lit LED, here ~54mA)
|
||||
//lowering the value increases the estimated usage and therefore makes the ABL more aggressive
|
||||
|
||||
#define MA_FOR_ESP 100 //how much mA does the ESP use (Wemos D1 about 80mA, ESP32 about 120mA)
|
||||
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
|
||||
|
||||
void WS2812FX::show(void) {
|
||||
//power limit calculation
|
||||
//each LED can draw up 195075 "power units" (approx. 53mA)
|
||||
//one PU is the power it takes to have 1 channel 1 step brighter per brightness step
|
||||
//so A=2,R=255,G=0,B=0 would use 510 PU per LED (1mA is about 3700 PU)
|
||||
|
||||
if (ablMilliampsMax > 149 && ablMilliampsMax < 65000) //lower numbers and 65000 turn off calculation
|
||||
{
|
||||
uint32_t powerBudget = (ablMilliampsMax - MA_FOR_ESP) * PU_PER_MA; //100mA for ESP power
|
||||
if (powerBudget > PU_PER_MA * _length) //each LED uses about 1mA in standby, exclude that from power budget
|
||||
{
|
||||
powerBudget -= PU_PER_MA * _length;
|
||||
} else
|
||||
{
|
||||
powerBudget = 0;
|
||||
}
|
||||
|
||||
uint32_t powerSum = 0;
|
||||
|
||||
for (uint16_t i = 0; i < _length; i++) //sum up the usage of each LED
|
||||
{
|
||||
RgbwColor c = bus->GetPixelColorRgbw(i);
|
||||
powerSum += (c.R + c.G + c.B + c.W);
|
||||
}
|
||||
|
||||
if (_rgbwMode) //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
|
||||
{
|
||||
powerSum *= 3;
|
||||
powerSum >> 2; //same as /= 4
|
||||
}
|
||||
|
||||
uint32_t powerSum0 = powerSum;
|
||||
powerSum *= _brightness;
|
||||
|
||||
if (powerSum > powerBudget) //scale brightness down to stay in current limit
|
||||
{
|
||||
float scale = (float)powerBudget / (float)powerSum;
|
||||
uint16_t scaleI = scale * 255;
|
||||
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
|
||||
uint8_t newBri = scale8(_brightness, scaleB);
|
||||
bus->SetBrightness(newBri);
|
||||
currentMilliamps = (powerSum0 * newBri) / PU_PER_MA;
|
||||
} else
|
||||
{
|
||||
currentMilliamps = powerSum / PU_PER_MA;
|
||||
bus->SetBrightness(_brightness);
|
||||
}
|
||||
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
|
||||
currentMilliamps += _length; //add standby power back to estimate
|
||||
} else {
|
||||
currentMilliamps = 0;
|
||||
}
|
||||
|
||||
bus->Show();
|
||||
}
|
||||
|
||||
void WS2812FX::trigger() {
|
||||
_triggered = true;
|
||||
}
|
||||
|
||||
void WS2812FX::setMode(uint8_t m) {
|
||||
RESET_RUNTIME;
|
||||
bool ua = modeUsesLock(_segments[0].mode) && !modeUsesLock(m);
|
||||
if (m > MODE_COUNT - 1) m = MODE_COUNT - 1;
|
||||
_segments[0].mode = m;
|
||||
if (ua) unlockAll();
|
||||
_modeUsesLock = modeUsesLock(_segments[0].mode);
|
||||
setBrightness(_brightness);
|
||||
}
|
||||
|
||||
//TODO transitions
|
||||
|
||||
void WS2812FX::setSpeed(uint8_t s) {
|
||||
_segments[0].speed = s;
|
||||
}
|
||||
|
||||
void WS2812FX::setIntensity(uint8_t in) {
|
||||
_segments[0].intensity = in;
|
||||
}
|
||||
|
||||
void WS2812FX::setPalette(uint8_t p) {
|
||||
_segments[0].palette = p;
|
||||
}
|
||||
|
||||
bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t p) {
|
||||
bool changed = false;
|
||||
m = constrain(m, 0, MODE_COUNT - 1);
|
||||
if (m != _segments[0].mode) { setMode(m); changed = true; }
|
||||
if (s != _segments[0].speed) { setSpeed(s); changed = true; }
|
||||
if (i != _segments[0].intensity) { setIntensity(i); changed = true; }
|
||||
if (p != _segments[0].palette) { setPalette(p); changed = true; }
|
||||
return changed;
|
||||
}
|
||||
|
||||
void WS2812FX::setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||
setColor(((uint32_t)w << 24) |((uint32_t)r << 16) | ((uint32_t)g << 8) | b);
|
||||
}
|
||||
|
||||
void WS2812FX::setSecondaryColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||
setSecondaryColor(((uint32_t)w << 24) |((uint32_t)r << 16) | ((uint32_t)g << 8) | b);
|
||||
}
|
||||
|
||||
void WS2812FX::setColor(uint32_t c) {
|
||||
_segments[0].colors[0] = c;
|
||||
}
|
||||
|
||||
void WS2812FX::setSecondaryColor(uint32_t c) {
|
||||
_segments[0].colors[1] = c;
|
||||
}
|
||||
|
||||
void WS2812FX::setBrightness(uint8_t b) {
|
||||
if (_brightness == b) return;
|
||||
_brightness = b;
|
||||
bus->SetBrightness(_brightness);
|
||||
show();
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getMode(void) {
|
||||
return _segments[0].mode;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getSpeed(void) {
|
||||
return _segments[0].speed;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getBrightness(void) {
|
||||
return _brightness;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getNumSegments(void) {
|
||||
return _num_segments;
|
||||
}
|
||||
|
||||
void WS2812FX::setNumSegments(uint8_t n) {
|
||||
_num_segments = n;
|
||||
}
|
||||
|
||||
uint32_t WS2812FX::getColor(void) {
|
||||
return _segments[0].colors[0];
|
||||
}
|
||||
|
||||
uint32_t WS2812FX::getPixelColor(uint16_t i)
|
||||
{
|
||||
if (_reverseMode) i = _length- 1 -i;
|
||||
if (_skipFirstMode) i += LED_SKIP_AMOUNT;
|
||||
if (_cronixieMode)
|
||||
{
|
||||
if(i>6)return 0;
|
||||
byte o = 10*i;
|
||||
switch(_cronixieDigits[i])
|
||||
{
|
||||
case 0: i=o+5; break;
|
||||
case 1: i=o+0; break;
|
||||
case 2: i=o+6; break;
|
||||
case 3: i=o+1; break;
|
||||
case 4: i=o+7; break;
|
||||
case 5: i=o+2; break;
|
||||
case 6: i=o+8; break;
|
||||
case 7: i=o+3; break;
|
||||
case 8: i=o+9; break;
|
||||
case 9: i=o+4; break;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
RgbwColor lColor = bus->GetPixelColorRgbw(i);
|
||||
byte r = lColor.R, g = lColor.G, b = lColor.B;
|
||||
switch (colorOrder)
|
||||
{
|
||||
case 0: break; //0 = Grb
|
||||
case 1: r = lColor.G; g = lColor.R; break; //1 = Rgb, common for WS2811
|
||||
case 2: g = lColor.B; b = lColor.G; break; //2 = Brg
|
||||
case 3: r = lColor.B; g = lColor.R; b = lColor.G; //3 = Rbg
|
||||
}
|
||||
return ( (lColor.W << 24) | (r << 16) | (g << 8) | (b) );
|
||||
}
|
||||
|
||||
WS2812FX::Segment WS2812FX::getSegment(void) {
|
||||
return SEGMENT;
|
||||
}
|
||||
|
||||
WS2812FX::Segment_runtime WS2812FX::getSegmentRuntime(void) {
|
||||
return SEGMENT_RUNTIME;
|
||||
}
|
||||
|
||||
WS2812FX::Segment* WS2812FX::getSegments(void) {
|
||||
return _segments;
|
||||
}
|
||||
|
||||
void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint8_t speed, uint8_t intensity, bool reverse) {
|
||||
uint32_t colors[] = {color, 0, 0};
|
||||
setSegment(n, start, stop, mode, colors, speed, intensity, reverse);
|
||||
}
|
||||
|
||||
void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint8_t speed, uint8_t intensity, bool reverse) {
|
||||
setSegment(n, start, stop, mode, colors, speed, intensity, (uint8_t)(reverse ? REVERSE : NO_OPTIONS));
|
||||
}
|
||||
|
||||
void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint8_t speed, uint8_t intensity, uint8_t options) {
|
||||
if(n < (sizeof(_segments) / sizeof(_segments[0]))) {
|
||||
if(n + 1 > _num_segments) _num_segments = n + 1;
|
||||
_segments[n].start = start;
|
||||
_segments[n].stop = stop;
|
||||
_segments[n].mode = mode;
|
||||
_segments[n].speed = speed;
|
||||
_segments[n].intensity = intensity;
|
||||
_segments[n].options = options;
|
||||
|
||||
for(uint8_t i=0; i<NUM_COLORS; i++) {
|
||||
_segments[n].colors[i] = colors[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::resetSegments() {
|
||||
memset(_segments, 0, sizeof(_segments));
|
||||
memset(_segment_runtimes, 0, sizeof(_segment_runtimes));
|
||||
_segment_index = 0;
|
||||
_num_segments = 1;
|
||||
setSegment(0, 0, 7, FX_MODE_STATIC, (const uint32_t[]){DEFAULT_COLOR, 0, 0}, DEFAULT_SPEED, 128, NO_OPTIONS);
|
||||
}
|
||||
|
||||
void WS2812FX::setIndividual(uint16_t i, uint32_t col)
|
||||
{
|
||||
if (modeUsesLock(SEGMENT.mode)) return;
|
||||
if (i >= 0 && i < _length)
|
||||
{
|
||||
_locked[i] = false;
|
||||
setPixelColor(i, col);
|
||||
_locked[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
|
||||
{
|
||||
if (i2 >= i)
|
||||
{
|
||||
for (uint16_t x = i; x <= i2; x++) setIndividual(x,col);
|
||||
} else
|
||||
{
|
||||
for (uint16_t x = i2; x <= i; x++) setIndividual(x,col);
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::lock(uint16_t i)
|
||||
{
|
||||
if (modeUsesLock(SEGMENT.mode)) return;
|
||||
if (i >= 0 && i < _length) _locked[i] = true;
|
||||
}
|
||||
|
||||
void WS2812FX::lockRange(uint16_t i, uint16_t i2)
|
||||
{
|
||||
if (modeUsesLock(SEGMENT.mode)) return;
|
||||
for (uint16_t x = i; x <= i2; x++)
|
||||
{
|
||||
if (i >= 0 && i < _length) _locked[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::unlock(uint16_t i)
|
||||
{
|
||||
if (modeUsesLock(SEGMENT.mode)) return;
|
||||
if (i >= 0 && i < _length) _locked[i] = false;
|
||||
}
|
||||
|
||||
void WS2812FX::unlockRange(uint16_t i, uint16_t i2)
|
||||
{
|
||||
if (modeUsesLock(SEGMENT.mode)) return;
|
||||
for (uint16_t x = i; x < i2; x++)
|
||||
{
|
||||
if (x >= 0 && x < _length) _locked[x] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::unlockAll()
|
||||
{
|
||||
for (int i=0; i < _length; i++) _locked[i] = false;
|
||||
}
|
||||
|
||||
void WS2812FX::setTransitionMode(bool t)
|
||||
{
|
||||
SEGMENT_RUNTIME.trans_act = (t) ? 1:2;
|
||||
if (!t) return;
|
||||
unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled
|
||||
if (SEGMENT.mode == FX_MODE_STATIC && SEGMENT_RUNTIME.next_time > waitMax) SEGMENT_RUNTIME.next_time = waitMax;
|
||||
}
|
||||
|
||||
/*
|
||||
* color blend function
|
||||
*/
|
||||
uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint8_t blend) {
|
||||
if(blend == 0) return color1;
|
||||
if(blend == 255) return color2;
|
||||
|
||||
int w1 = (color1 >> 24) & 0xff;
|
||||
int r1 = (color1 >> 16) & 0xff;
|
||||
int g1 = (color1 >> 8) & 0xff;
|
||||
int b1 = color1 & 0xff;
|
||||
|
||||
int w2 = (color2 >> 24) & 0xff;
|
||||
int r2 = (color2 >> 16) & 0xff;
|
||||
int g2 = (color2 >> 8) & 0xff;
|
||||
int b2 = color2 & 0xff;
|
||||
|
||||
uint32_t w3 = ((w2 * blend) + (w1 * (255 - blend))) >> 8;
|
||||
uint32_t r3 = ((r2 * blend) + (r1 * (255 - blend))) >> 8;
|
||||
uint32_t g3 = ((g2 * blend) + (g1 * (255 - blend))) >> 8;
|
||||
uint32_t b3 = ((b2 * blend) + (b1 * (255 - blend))) >> 8;
|
||||
|
||||
return ((w3 << 24) | (r3 << 16) | (g3 << 8) | (b3));
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills segment with color
|
||||
*/
|
||||
void WS2812FX::fill(uint32_t c) {
|
||||
for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) {
|
||||
setPixelColor(i, c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fade out function, higher rate = quicker fade
|
||||
*/
|
||||
void WS2812FX::fade_out(uint8_t rate) {
|
||||
rate = (255-rate) >> 1;
|
||||
float mappedRate = float(rate) +1.1;
|
||||
|
||||
uint32_t color = SEGMENT.colors[1]; // target color
|
||||
int w2 = (color >> 24) & 0xff;
|
||||
int r2 = (color >> 16) & 0xff;
|
||||
int g2 = (color >> 8) & 0xff;
|
||||
int b2 = color & 0xff;
|
||||
|
||||
for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) {
|
||||
color = getPixelColor(i);
|
||||
int w1 = (color >> 24) & 0xff;
|
||||
int r1 = (color >> 16) & 0xff;
|
||||
int g1 = (color >> 8) & 0xff;
|
||||
int b1 = color & 0xff;
|
||||
|
||||
int wdelta = (w2 - w1) / mappedRate;
|
||||
int rdelta = (r2 - r1) / mappedRate;
|
||||
int gdelta = (g2 - g1) / mappedRate;
|
||||
int bdelta = (b2 - b1) / mappedRate;
|
||||
|
||||
// if fade isn't complete, make sure delta is at least 1 (fixes rounding issues)
|
||||
wdelta += (w2 == w1) ? 0 : (w2 > w1) ? 1 : -1;
|
||||
rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1;
|
||||
gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1;
|
||||
bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1;
|
||||
|
||||
setPixelColor(i, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* blurs segment content, source: FastLED colorutils.cpp
|
||||
*/
|
||||
void WS2812FX::blur(uint8_t blur_amount)
|
||||
{
|
||||
uint8_t keep = 255 - blur_amount;
|
||||
uint8_t seep = blur_amount >> 1;
|
||||
CRGB carryover = CRGB::Black;
|
||||
for(uint16_t i = SEGMENT.start; i <= SEGMENT.stop; i++)
|
||||
{
|
||||
CRGB cur = fastled_from_col(getPixelColor(i));
|
||||
CRGB part = cur;
|
||||
part.nscale8(seep);
|
||||
cur.nscale8(keep);
|
||||
cur += carryover;
|
||||
if(i > SEGMENT.start) {
|
||||
uint32_t c = getPixelColor(i-1);
|
||||
uint8_t r = (c >> 16 & 0xFF);
|
||||
uint8_t g = (c >> 8 & 0xFF);
|
||||
uint8_t b = (c & 0xFF);
|
||||
setPixelColor(i-1, qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue));
|
||||
}
|
||||
setPixelColor(i,cur.red, cur.green, cur.blue);
|
||||
carryover = part;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Put a value 0 to 255 in to get a color value.
|
||||
* The colours are a transition r -> g -> b -> back to r
|
||||
* Inspired by the Adafruit examples.
|
||||
*/
|
||||
uint32_t WS2812FX::color_wheel(uint8_t pos) {
|
||||
if (SEGMENT.palette) return color_from_palette(pos, false, true, 0);
|
||||
pos = 255 - pos;
|
||||
if(pos < 85) {
|
||||
return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3);
|
||||
} else if(pos < 170) {
|
||||
pos -= 85;
|
||||
return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3);
|
||||
} else {
|
||||
pos -= 170;
|
||||
return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a new, random wheel index with a minimum distance of 42 from pos.
|
||||
*/
|
||||
uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) {
|
||||
uint8_t r = 0, x = 0, y = 0, d = 0;
|
||||
|
||||
while(d < 42) {
|
||||
r = random8();
|
||||
x = abs(pos - r);
|
||||
y = 255 - x;
|
||||
d = min(x, y);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
CRGB WS2812FX::fastled_from_col(uint32_t color)
|
||||
{
|
||||
CRGB fastled_col;
|
||||
fastled_col.red = (color >> 16 & 0xFF);
|
||||
fastled_col.green = (color >> 8 & 0xFF);
|
||||
fastled_col.blue = (color & 0xFF);
|
||||
return fastled_col;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* FastLED palette modes helper function. Limitation: Due to memory reasons, multiple active segments with FastLED will disable the Palette transitions
|
||||
*/
|
||||
void WS2812FX::handle_palette(void)
|
||||
{
|
||||
bool singleSegmentMode = (_segment_index == _segment_index_palette_last);
|
||||
_segment_index_palette_last = _segment_index;
|
||||
|
||||
byte paletteIndex = SEGMENT.palette;
|
||||
if ((SEGMENT.mode >= FX_MODE_METEOR) && SEGMENT.palette == 0) paletteIndex = 4;
|
||||
|
||||
switch (paletteIndex)
|
||||
{
|
||||
case 0: {//default palette. Differs depending on effect
|
||||
switch (SEGMENT.mode)
|
||||
{
|
||||
case FX_MODE_FIRE_2012 : targetPalette = gGradientPalettes[22]; break;//heat palette
|
||||
case FX_MODE_COLORWAVES : targetPalette = gGradientPalettes[13]; break;//landscape 33
|
||||
case FX_MODE_FILLNOISE8 : targetPalette = OceanColors_p; break;
|
||||
case FX_MODE_NOISE16_1 : targetPalette = gGradientPalettes[17]; break;//Drywet
|
||||
case FX_MODE_NOISE16_2 : targetPalette = gGradientPalettes[30]; break;//Blue cyan yellow
|
||||
case FX_MODE_NOISE16_3 : targetPalette = gGradientPalettes[22]; break;//heat palette
|
||||
case FX_MODE_NOISE16_4 : targetPalette = gGradientPalettes[13]; break;//landscape 33
|
||||
|
||||
default: targetPalette = PartyColors_p; break;//palette, bpm
|
||||
}
|
||||
break;}
|
||||
case 1: {//periodically replace palette with a random one. Doesn't work with multiple FastLED segments
|
||||
if (!singleSegmentMode)
|
||||
{
|
||||
targetPalette = PartyColors_p; break; //fallback
|
||||
}
|
||||
if (millis() - _lastPaletteChange > 1000 + ((uint32_t)(255-SEGMENT.intensity))*100)
|
||||
{
|
||||
targetPalette = CRGBPalette16(
|
||||
CHSV(random8(), 255, random8(128, 255)),
|
||||
CHSV(random8(), 255, random8(128, 255)),
|
||||
CHSV(random8(), 192, random8(128, 255)),
|
||||
CHSV(random8(), 255, random8(128, 255)));
|
||||
_lastPaletteChange = millis();
|
||||
} break;}
|
||||
case 2: {//primary color only
|
||||
CRGB prim = fastled_from_col(SEGMENT.colors[0]);
|
||||
targetPalette = CRGBPalette16(prim); break;}
|
||||
case 3: {//based on primary
|
||||
//considering performance implications
|
||||
CRGB prim = fastled_from_col(SEGMENT.colors[0]);
|
||||
CHSV prim_hsv = rgb2hsv_approximate(prim);
|
||||
targetPalette = CRGBPalette16(
|
||||
CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v), //color itself
|
||||
CHSV(prim_hsv.h, max(prim_hsv.s - 50,0), prim_hsv.v), //less saturated
|
||||
CHSV(prim_hsv.h, prim_hsv.s, max(prim_hsv.v - 50,0)), //darker
|
||||
CHSV(prim_hsv.h, prim_hsv.s, prim_hsv.v)); //color itself
|
||||
break;}
|
||||
case 4: {//primary + secondary
|
||||
CRGB prim = fastled_from_col(SEGMENT.colors[0]);
|
||||
CRGB sec = fastled_from_col(SEGMENT.colors[1]);
|
||||
targetPalette = CRGBPalette16(sec,prim); break;}
|
||||
case 5: {//based on primary + secondary
|
||||
CRGB prim = fastled_from_col(SEGMENT.colors[0]);
|
||||
CRGB sec = fastled_from_col(SEGMENT.colors[1]);
|
||||
targetPalette = CRGBPalette16(sec,prim,CRGB::White); break;}
|
||||
case 6: //Party colors
|
||||
targetPalette = PartyColors_p; break;
|
||||
case 7: //Cloud colors
|
||||
targetPalette = CloudColors_p; break;
|
||||
case 8: //Lava colors
|
||||
targetPalette = LavaColors_p; break;
|
||||
case 9: //Ocean colors
|
||||
targetPalette = OceanColors_p; break;
|
||||
case 10: //Forest colors
|
||||
targetPalette = ForestColors_p; break;
|
||||
case 11: //Rainbow colors
|
||||
targetPalette = RainbowColors_p; break;
|
||||
case 12: //Rainbow stripe colors
|
||||
targetPalette = RainbowStripeColors_p; break;
|
||||
default: //progmem palettes
|
||||
targetPalette = gGradientPalettes[constrain(SEGMENT.palette -13, 0, gGradientPaletteCount -1)];
|
||||
}
|
||||
|
||||
if (singleSegmentMode && paletteFade) //only blend if just one segment uses FastLED mode
|
||||
{
|
||||
nblendPaletteTowardPalette(currentPalette, targetPalette, 48);
|
||||
} else
|
||||
{
|
||||
currentPalette = targetPalette;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri)
|
||||
{
|
||||
if (SEGMENT.palette == 0 && mcol < 3) return SEGMENT.colors[mcol]; //WS2812FX default
|
||||
uint8_t paletteIndex = i;
|
||||
if (mapping) paletteIndex = map(i,SEGMENT.start,SEGMENT.stop,0,255);
|
||||
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
|
||||
CRGB fastled_col;
|
||||
fastled_col = ColorFromPalette( currentPalette, paletteIndex, pbri, (paletteBlend == 3)? NOBLEND:LINEARBLEND);
|
||||
return fastled_col.r*65536 + fastled_col.g*256 + fastled_col.b;
|
||||
}
|
||||
+45
-134
@@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head><meta charset="utf-8"><meta name="theme-color" content="#fff">
|
||||
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico'/>
|
||||
<title>WLED 0.8.2</title>
|
||||
<title>WLED 0.8.3</title>
|
||||
<script>
|
||||
var d=document;
|
||||
var w=window.getComputedStyle(d.querySelector("html"));
|
||||
@@ -18,6 +18,9 @@
|
||||
var nState = 0;
|
||||
var cv=0;
|
||||
var lm=0;
|
||||
var fxi=0;
|
||||
var fpi=0;
|
||||
var fxn=1;
|
||||
aC="";
|
||||
bC="";
|
||||
dC="";
|
||||
@@ -38,8 +41,10 @@
|
||||
var w=window.getComputedStyle(d.querySelector("html"));
|
||||
UCol();
|
||||
CV(0);
|
||||
setInterval('GIO()', 5000);
|
||||
setInterval(GIO, 5000);
|
||||
GIO();
|
||||
setTimeout(function(){fillfx(0);}, 500);
|
||||
setTimeout(function(){fillfx(1);}, 750);
|
||||
}
|
||||
function GIO()
|
||||
{
|
||||
@@ -60,8 +65,8 @@
|
||||
} else {
|
||||
uwv = false;
|
||||
}
|
||||
Cf.TX.selectedIndex = this.responseXML.getElementsByTagName('fx')[0].childNodes[0].nodeValue;
|
||||
Cf.FP.selectedIndex = this.responseXML.getElementsByTagName('fp')[0].childNodes[0].nodeValue;
|
||||
fxi = this.responseXML.getElementsByTagName('fx')[0].childNodes[0].nodeValue;
|
||||
fpi = this.responseXML.getElementsByTagName('fp')[0].childNodes[0].nodeValue;
|
||||
d.Cf.SX.value = this.responseXML.getElementsByTagName('sx')[0].childNodes[0].nodeValue;
|
||||
d.Cf.IX.value = this.responseXML.getElementsByTagName('ix')[0].childNodes[0].nodeValue;
|
||||
nla = (this.responseXML.getElementsByTagName('nl')[0].innerHTML)!=0?true:false;
|
||||
@@ -73,12 +78,16 @@
|
||||
d.documentElement.style.setProperty("--bCol",this.responseXML.getElementsByTagName('cb')[0].childNodes[0].nodeValue);
|
||||
d.documentElement.style.setProperty("--cCol",this.responseXML.getElementsByTagName('cc')[0].childNodes[0].nodeValue);
|
||||
d.documentElement.style.setProperty("--dCol",this.responseXML.getElementsByTagName('cd')[0].childNodes[0].nodeValue);
|
||||
d.documentElement.style.setProperty("--sCol",this.responseXML.getElementsByTagName('cs')[0].childNodes[0].nodeValue);
|
||||
d.documentElement.style.setProperty("--sCol",this.responseXML.getElementsByTagName('cu')[0].childNodes[0].nodeValue);
|
||||
d.documentElement.style.setProperty("--tCol",this.responseXML.getElementsByTagName('ct')[0].childNodes[0].nodeValue);
|
||||
d.documentElement.style.setProperty("--cFn",this.responseXML.getElementsByTagName('cf')[0].childNodes[0].nodeValue);
|
||||
UCol();
|
||||
CV(parseInt(this.responseXML.getElementsByTagName('md')[0].childNodes[0].nodeValue));
|
||||
}firstload=false;
|
||||
}else{
|
||||
Cf.TX.selectedIndex = fxi;
|
||||
Cf.IX.selectedIndex = fpi;
|
||||
}
|
||||
firstload=false;
|
||||
nState = 0;
|
||||
nState = (this.responseXML.getElementsByTagName('nr')[0].innerHTML)!=0?1:0;
|
||||
nState += (this.responseXML.getElementsByTagName('ns')[0].innerHTML)!=0?2:0;
|
||||
@@ -204,10 +213,10 @@
|
||||
function SwFX(s)
|
||||
{
|
||||
var n=Cf.TX.selectedIndex+s;
|
||||
if (n==-1||n==79) return;
|
||||
if (n==-1||n==fxn) return;
|
||||
Cf.TX.selectedIndex =n;
|
||||
if (n < 0) Cf.TX.selectedIndex = 0;
|
||||
if (n > 78) Cf.TX.selectedIndex = 65;
|
||||
if (n > fxn) Cf.TX.selectedIndex = Math.min(65,fxn-1);
|
||||
GX();
|
||||
}
|
||||
function TgHSB()
|
||||
@@ -356,6 +365,32 @@
|
||||
{
|
||||
resp+="&PL=0";GIO();
|
||||
}
|
||||
function fillfx(fp)
|
||||
{
|
||||
e="<option>Error loading list!</option>";
|
||||
el = fp?Cf.FP:Cf.TX;
|
||||
fetch(fp?'/json/palettes':'/json/effects')
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
el.innerHTML=e;
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(json => {
|
||||
var x="";
|
||||
var l=fp?json.palettes:json.effects;
|
||||
for (i in l) {
|
||||
x += "<option value=\""+i+"\">"+l[i]+" ("+i+")</option>";
|
||||
}
|
||||
el.innerHTML=x;
|
||||
el.selectedIndex=fp?fpi:fxi;
|
||||
if(!fp)fxn=l.length;
|
||||
UV();
|
||||
})
|
||||
.catch(function () {
|
||||
el.innerHTML=e;
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
:root {
|
||||
@@ -597,85 +632,7 @@
|
||||
<svg id="fmf" onclick="SwFX(1)"><use xlink:href="#lnr-arrow-right-circle"></use></svg>
|
||||
<svg id="fof" onclick="SwFX(99)"><use xlink:href="#lnr-rocket"></use></svg><br><br>
|
||||
<select name="TX" onchange="GX()">
|
||||
<option value="0" selected>Solid (0)</option>
|
||||
<option value="1">Blink (1)</option>
|
||||
<option value="2">Breath (2)</option>
|
||||
<option value="3">Wipe (3)</option>
|
||||
<option value="4">Wipe Random (4)</option>
|
||||
<option value="5">Color R (5)</option>
|
||||
<option value="6">Sweep (6)</option>
|
||||
<option value="7">Dynamic (7)</option>
|
||||
<option value="8">Colorloop (8)</option>
|
||||
<option value="9">Rainbow (9)</option>
|
||||
<option value="10">Scan (10)</option>
|
||||
<option value="11">Scan x2 (11)</option>
|
||||
<option value="12">Fade (12)</option>
|
||||
<option value="13">Chase (13)</option>
|
||||
<option value="14">Chase Cl (14)</option>
|
||||
<option value="15">Running (15)</option>
|
||||
<option value="16">Twinkle (16)</option>
|
||||
<option value="17">Twinkle R (17)</option>
|
||||
<option value="18">Twinkle Fade (18)</option>
|
||||
<option value="19">Twinkle RF (19)</option>
|
||||
<option value="20">Sparkle (20)</option>
|
||||
<option value="21">Sparkle Inv (21)</option>
|
||||
<option value="22">Sparkle Inv+ (22)</option>
|
||||
<option value="23">Strobe (23)</option>
|
||||
<option value="24">Strobe Cl (24)</option>
|
||||
<option value="25">Strobe + (25)</option>
|
||||
<option value="26">Blink Cl (26)</option>
|
||||
<option value="27">Android (27)</option>
|
||||
<option value="28">Chase (28)</option>
|
||||
<option value="29">Chase R (29)</option>
|
||||
<option value="30">Chase Rainbow (30)</option>
|
||||
<option value="31">Chase Flash (31)</option>
|
||||
<option value="32">Chase RF (32)</option>
|
||||
<option value="33">Chase Cl Inv (33)</option>
|
||||
<option value="34">Colorful (34)</option>
|
||||
<option value="35">Traffic Light (35)</option>
|
||||
<option value="36">Sweep R(36)</option>
|
||||
<option value="37">Running 2 (37)</option>
|
||||
<option value="38">Red/Blue (38)</option>
|
||||
<option value="39">Running R (39)</option>
|
||||
<option value="40">Scanner (40)</option>
|
||||
<option value="41">Lighthouse (41)</option>
|
||||
<option value="42">Fireworks (42)</option>
|
||||
<option value="43">Fireworks R (43)</option>
|
||||
<option value="44">Christmas (44)</option>
|
||||
<option value="45">Fire Flicker (45)</option>
|
||||
<option value="46">Gradient (46)</option>
|
||||
<option value="47">Loading (47)</option>
|
||||
<option value="48">Wipe IO (48)</option>
|
||||
<option value="49">Wipe II (49)</option>
|
||||
<option value="50">Wipe OO (50)</option>
|
||||
<option value="51">Wipe OI (51)</option>
|
||||
<option value="52">Circus (52)</option>
|
||||
<option value="53">Halloween (53)</option>
|
||||
<option value="54">Tricolor Chase (54)</option>
|
||||
<option value="55">Tricolor Wipe (55)</option>
|
||||
<option value="56">Tricolor Fade (56)</option>
|
||||
<option value="57">Lighting (57)</option>
|
||||
<option value="58">ICU (58)</option>
|
||||
<option value="59">Multi Comet (59)</option>
|
||||
<option value="60">Scanner x2 (60)</option>
|
||||
<option value="61">Random Chase (61)</option>
|
||||
<option value="62">Oscillate (62)</option>
|
||||
<option value="63">Pride 2015 (63)</option>
|
||||
<option value="64">Juggle (64)</option>
|
||||
<option value="65">Palette (65)</option>
|
||||
<option value="66">Fire 2012 (66)</option>
|
||||
<option value="67">Colorwaves (67)</option>
|
||||
<option value="68">BPM (68)</option>
|
||||
<option value="69">Fill Noise 8 (69)</option>
|
||||
<option value="70">Noise 16 1 (70)</option>
|
||||
<option value="71">Noise 16 2 (71)</option>
|
||||
<option value="72">Noise 16 3 (72)</option>
|
||||
<option value="73">Noise 16 4 (73)</option>
|
||||
<option value="74">Colortwinkle (74)</option>
|
||||
<option value="75">Lake (75)</option>
|
||||
<option value="76">Meteor (76)</option>
|
||||
<option value="77">Smooth Meteor (77)</option>
|
||||
<option value="78">Railway (78)</option>
|
||||
<option>Loading...</option>
|
||||
</select><br><br>
|
||||
Set secondary color to
|
||||
<button type="button" onclick="CS(0)">White</button>
|
||||
@@ -687,53 +644,7 @@
|
||||
<div id="fpX">
|
||||
<br>FastLED Palette<br><br>
|
||||
<select name="FP" onchange="GP()">
|
||||
<option value="0" selected>Default</option>
|
||||
<option value="1">Random Cycle</option>
|
||||
<option value="2">Primary Color Only</option>
|
||||
<option value="3">Based on Primary</option>
|
||||
<option value="4">Set Colors Only</option>
|
||||
<option value="5">Based on Set Colors</option>
|
||||
<option value="6">Party</option>
|
||||
<option value="7">Cloud</option>
|
||||
<option value="8">Lava</option>
|
||||
<option value="9">Ocean</option>
|
||||
<option value="10">Forest</option>
|
||||
<option value="11">Rainbow</option>
|
||||
<option value="12">Rainbow Stripe</option>
|
||||
<option value="13">Sunset</option>
|
||||
<option value="14">Rivendell</option>
|
||||
<option value="15">Breeze</option>
|
||||
<option value="16">Red & Blue</option>
|
||||
<option value="17">Yellowout</option>
|
||||
<option value="18">Analogous</option>
|
||||
<option value="19">Splash</option>
|
||||
<option value="20">Pastel</option>
|
||||
<option value="21">Sunset2</option>
|
||||
<option value="22">Beech</option>
|
||||
<option value="23">Vintage</option>
|
||||
<option value="24">Departure</option>
|
||||
<option value="25">Landscape</option>
|
||||
<option value="26">Beach</option>
|
||||
<option value="27">Sherbet</option>
|
||||
<option value="28">Hult</option>
|
||||
<option value="29">Hult64</option>
|
||||
<option value="30">Drywet</option>
|
||||
<option value="31">Jul</option>
|
||||
<option value="32">Grintage</option>
|
||||
<option value="33">Rewhi</option>
|
||||
<option value="34">Tertiary</option>
|
||||
<option value="35">Fire</option>
|
||||
<option value="36">Icefire</option>
|
||||
<option value="37">Cyane</option>
|
||||
<option value="38">Light Pink</option>
|
||||
<option value="39">Autumn</option>
|
||||
<option value="40">Magenta</option>
|
||||
<option value="41">Magred</option>
|
||||
<option value="42">Yelmag</option>
|
||||
<option value="43">Yelblu</option>
|
||||
<option value="44">Orange & Teal</option>
|
||||
<option value="43">Tiamat</option>
|
||||
<option value="44">April Night</option>
|
||||
<option>Loading...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="slX" class="sl">
|
||||
|
||||
+70
-177
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
+643
-696
File diff suppressed because it is too large
Load Diff
+1206
-1261
File diff suppressed because it is too large
Load Diff
+12
-23
File diff suppressed because one or more lines are too long
+40
-81
@@ -3,19 +3,14 @@
|
||||
*/
|
||||
|
||||
//common CSS of settings pages
|
||||
const char PAGE_settingsCss[] PROGMEM = R"=====(
|
||||
body{font-family:var(--cFn),sans-serif;text-align:center;background:var(--cCol);color:var(--tCol);line-height:200%;margin:0;background-attachment:fixed}hr{border-color:var(--dCol);filter:drop-shadow(-5px -5px 5px var(--sCol))}button{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:20px;margin:8px;margin-top:12px}.helpB{text-align:left;position:absolute;width:60px}input{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.5ch solid var(--bCol);filter:drop-shadow(-5px -5px 5px var(--sCol))}input[type=number]{width:4em}select{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:0.5ch solid var(--bCol);filter:drop-shadow( -5px -5px 5px var(--sCol) );}</style>
|
||||
)=====";
|
||||
const char PAGE_settingsCss[] PROGMEM = R"=====(body{font-family:var(--cFn),sans-serif;text-align:center;background:var(--cCol);color:var(--tCol);line-height:200%;margin:0;background-attachment:fixed}hr{border-color:var(--dCol);filter:drop-shadow(-5px -5px 5px var(--sCol))}button{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:20px;margin:8px;margin-top:12px}.helpB{text-align:left;position:absolute;width:60px}input{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.5ch solid var(--bCol);filter:drop-shadow(-5px -5px 5px var(--sCol))}input[type=number]{width:4em}select{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:0.5ch solid var(--bCol);filter:drop-shadow( -5px -5px 5px var(--sCol) );}td{padding:2px;}</style>)=====";
|
||||
|
||||
|
||||
//settings menu
|
||||
const char PAGE_settings0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
<html><head><title>WLED Settings</title>
|
||||
)=====";
|
||||
const char PAGE_settings0[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><title>WLED Settings</title>)=====";
|
||||
|
||||
const char PAGE_settings1[] PROGMEM = R"=====(
|
||||
body{text-align:center;background:var(--cCol);height:100%;margin:0;background-attachment:fixed}html{--h:11.55vh}button{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),Helvetica,sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:8vmin;height:var(--h);width:95%;margin-top:2.4vh}</style>
|
||||
const char PAGE_settings1[] PROGMEM = R"=====(body{text-align:center;background:var(--cCol);height:100%;margin:0;background-attachment:fixed}html{--h:11.55vh}button{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),Helvetica,sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:8vmin;height:var(--h);width:95%;margin-top:2.4vh}</style>
|
||||
<script>function BB(){if(window.frameElement){document.getElementById("b").style.display="none";document.documentElement.style.setProperty("--h","13.86vh")}};</script>
|
||||
</head>
|
||||
<body onload=BB()>
|
||||
@@ -27,19 +22,15 @@ body{text-align:center;background:var(--cCol);height:100%;margin:0;background-at
|
||||
<form action=/settings/time><button type=submit>Time & Macros</button></form>
|
||||
<form action=/settings/sec><button type=submit>Security & Updates</button></form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
|
||||
|
||||
//wifi settings
|
||||
const char PAGE_settings_wifi0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
const char PAGE_settings_wifi0[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500">
|
||||
<title>WiFi Settings</title><script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#wifi-settings");}function B(){window.history.back();}function GetV(){var d = document;
|
||||
)=====";
|
||||
<title>WiFi Settings</title><script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#wifi-settings");}function B(){window.history.back();}function GetV(){var d = document;)=====";
|
||||
|
||||
const char PAGE_settings_wifi1[] PROGMEM = R"=====(
|
||||
</head>
|
||||
const char PAGE_settings_wifi1[] PROGMEM = R"=====(</head>
|
||||
<body onload="GetV()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
@@ -76,13 +67,11 @@ AP IP: <span class="sip"> Not active </span><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
|
||||
|
||||
//LED settings
|
||||
const char PAGE_settings_leds0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
const char PAGE_settings_leds0[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500">
|
||||
<title>LED Settings</title><script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#led-settings");}function B(){window.history.back();}function S(){GetV();UI();}function UI(){
|
||||
var myC=document.querySelectorAll('.wc'),l=myC.length;
|
||||
@@ -91,11 +80,9 @@ var val=Math.ceil((100+document.Sf.LC.value*55)/500)/2;
|
||||
val=(val>5)?Math.ceil(val):val;var s="";
|
||||
if (val<1.1){s="ESP 5V pin with 1A USB supply";}else{s="External 5V ";s+=val;s+="A supply connected to LEDs";}
|
||||
document.getElementById('psu').innerHTML=s;document.getElementById('ps2').innerHTML=val+"A = "+val*1000;
|
||||
}function GetV(){var d = document;
|
||||
)=====";
|
||||
}function GetV(){var d = document;)=====";
|
||||
|
||||
const char PAGE_settings_leds1[] PROGMEM = R"=====(
|
||||
</head>
|
||||
const char PAGE_settings_leds1[] PROGMEM = R"=====(</head>
|
||||
<body onload="S()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
@@ -159,25 +146,20 @@ Palette blending:
|
||||
<option value="3">None (not recommended)</option>
|
||||
</select><br>
|
||||
Reverse LED order (rotate 180): <input type="checkbox" name="RV"><br>
|
||||
Init LEDs after WiFi: <input type="checkbox" name="EI"><br>
|
||||
Skip first LED: <input type="checkbox" name="SL"><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
|
||||
|
||||
//User Interface settings
|
||||
const char PAGE_settings_ui0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
const char PAGE_settings_ui0[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500">
|
||||
<title>UI Settings</title><script>
|
||||
function gId(s){return document.getElementById(s);}function S(){GetV();Ct();}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#user-interface-settings");}function B(){window.history.back();}function Ct(){if (gId("co").selected){gId("cth").style.display="block";}else{gId("cth").style.display="none";}}function GetV(){var d = document;
|
||||
)=====";
|
||||
function gId(s){return document.getElementById(s);}function S(){GetV();Ct();}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#user-interface-settings");}function B(){window.history.back();}function Ct(){if (gId("co").selected){gId("cth").style.display="block";}else{gId("cth").style.display="none";}}function GetV(){var d = document;)=====";
|
||||
|
||||
const char PAGE_settings_ui1[] PROGMEM = R"=====(
|
||||
</head>
|
||||
const char PAGE_settings_ui1[] PROGMEM = R"=====(</head>
|
||||
<body onload="S()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
@@ -201,7 +183,7 @@ Color Theme:
|
||||
<option value="4">Electric</option>
|
||||
<option value="5">Mint</option>
|
||||
<option value="6">Amber</option>
|
||||
<option value="7">Club</option>
|
||||
<option value="7">Dark</option>
|
||||
<option value="8">Air</option>
|
||||
<option value="9">Nixie</option>
|
||||
<option value="10">Terminal</option>
|
||||
@@ -224,19 +206,15 @@ Make sure the font you use is installed on your system!<br>
|
||||
<hr><button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
|
||||
|
||||
//sync settings
|
||||
const char PAGE_settings_sync0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
const char PAGE_settings_sync0[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500"><title>Sync Settings</title>
|
||||
<script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#sync-settings");}function B(){window.open("/settings","_self");}function GetV(){var d = document;
|
||||
)=====";
|
||||
<script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#sync-settings");}function B(){window.open("/settings","_self");}function GetV(){var d = document;)=====";
|
||||
|
||||
const char PAGE_settings_sync1[] PROGMEM = R"=====(
|
||||
</head>
|
||||
const char PAGE_settings_sync1[] PROGMEM = R"=====(</head>
|
||||
<body onload="GetV()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
@@ -295,21 +273,20 @@ Hue status: <span class="hms"> Internal ESP Error! </span><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
|
||||
|
||||
//time and macro settings
|
||||
const char PAGE_settings_time0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
const char PAGE_settings_time0[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500"><title>Time Settings</title>
|
||||
<script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings");}function B(){window.open("/settings","_self");}function S(){GetV();Cs();}function gId(s){return document.getElementById(s);}function Cs(){gId("cac").style.display="none";gId("coc").style.display="block";gId("ccc").style.display="none";if (gId("ca").selected){gId("cac").style.display="block";}if (gId("cc").selected){gId("coc").style.display="none";gId("ccc").style.display="block";}if (gId("cn").selected){gId("coc").style.display="none";}}function GetV(){var d = document;
|
||||
)=====";
|
||||
<script>var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings");}function B(){window.open("/settings","_self");}function S(){BTa();GetV();Cs();FC();}function gId(s){return d.getElementById(s);}function Cs(){gId("cac").style.display="none";gId("coc").style.display="block";gId("ccc").style.display="none";if (gId("ca").selected){gId("cac").style.display="block";}if (gId("cc").selected){gId("coc").style.display="none";gId("ccc").style.display="block";}if (gId("cn").selected){gId("coc").style.display="none";}}
|
||||
function BTa(){var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Macro</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for (i=0;i<8;i++){ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" type=\"number\" min=\"0\" max=\"16\"></td>";for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>";}gId("TMT").innerHTML=ih;}
|
||||
function FC(){for(j=0;j<8;j++){for(i=0;i<8;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1;}}
|
||||
function Wd(){a=[0,0,0,0,0,0,0,0];for(i=0;i<8;i++){m=1;for(j=0;j<8;j++){a[i]+=gId("W"+i+j).checked*m;m*=2;}gId("W"+i).value=a[i];}}function GetV(){)=====";
|
||||
|
||||
const char PAGE_settings_time1[] PROGMEM = R"=====(
|
||||
</head>
|
||||
const char PAGE_settings_time1[] PROGMEM = R"=====(</head>
|
||||
<body onload="S()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<form id="form_s" name="Sf" method="post" onsubmit="Wd()">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
|
||||
<h2>Time setup</h2>
|
||||
@@ -381,40 +358,23 @@ Alexa On/Off Macros: <input name="A0" type="number" min="0" max="16" required> <
|
||||
Button Macro: <input name="MP" type="number" min="0" max="16" required> Long Press: <input name="ML" type="number" min="0" max="16" required><br>
|
||||
Countdown-Over Macro: <input name="MC" type="number" min="0" max="16" required><br>
|
||||
Timed-Light-Over Macro: <input name="MN" type="number" min="0" max="16" required><br>
|
||||
Time-Controlled Macros (Hours/Minutes > Macro):<br>
|
||||
<input name="H0" type="number" min="0" max="24"> <input name="N0" type="number" min="0" max="59">
|
||||
> <input name="T0" type="number" min="0" max="16"><br>
|
||||
<input name="H1" type="number" min="0" max="24"> <input name="N1" type="number" min="0" max="59">
|
||||
> <input name="T1" type="number" min="0" max="16"><br>
|
||||
<input name="H2" type="number" min="0" max="24"> <input name="N2" type="number" min="0" max="59">
|
||||
> <input name="T2" type="number" min="0" max="16"><br>
|
||||
<input name="H3" type="number" min="0" max="24"> <input name="N3" type="number" min="0" max="59">
|
||||
> <input name="T3" type="number" min="0" max="16"><br>
|
||||
<input name="H4" type="number" min="0" max="24"> <input name="N4" type="number" min="0" max="59">
|
||||
> <input name="T4" type="number" min="0" max="16"><br>
|
||||
<input name="H5" type="number" min="0" max="24"> <input name="N5" type="number" min="0" max="59">
|
||||
> <input name="T5" type="number" min="0" max="16"><br>
|
||||
<input name="H6" type="number" min="0" max="24"> <input name="N6" type="number" min="0" max="59">
|
||||
> <input name="T6" type="number" min="0" max="16"><br>
|
||||
<input name="H7" type="number" min="0" max="24"> <input name="N7" type="number" min="0" max="59">
|
||||
> <input name="T7" type="number" min="0" max="16"><hr>
|
||||
Time-Controlled Macros:<br>
|
||||
<div style="display: inline-block">
|
||||
<table id="TMT">
|
||||
</table></div><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
|
||||
|
||||
//security settings and about
|
||||
const char PAGE_settings_sec0[] PROGMEM = R"=====(
|
||||
<!DOCTYPE html>
|
||||
const char PAGE_settings_sec0[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500">
|
||||
<title>Misc Settings</title>
|
||||
<script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#security-settings");}function B(){window.open("/settings","_self");}function U(){window.open("/update","_self");}function GetV(){var d = document;
|
||||
)=====";
|
||||
<script>function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#security-settings");}function B(){window.open("/settings","_self");}function U(){window.open("/update","_self");}function GetV(){var d = document;)=====";
|
||||
|
||||
const char PAGE_settings_sec1[] PROGMEM = R"=====(
|
||||
</head>
|
||||
const char PAGE_settings_sec1[] PROGMEM = R"=====(</head>
|
||||
<body onload="GetV()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
@@ -437,14 +397,13 @@ HTTP traffic is unencrypted. An attacker in the same network can intercept form
|
||||
<button type="button" onclick="U()">Manual OTA Update</button><br>
|
||||
Enable ArduinoOTA: <input type="checkbox" name="AO"><br>
|
||||
<h3>About</h3>
|
||||
<a href="https://github.com/Aircoookie/WLED" target="_blank">WLED</a> version 0.8.2<br><br>
|
||||
<a href="https://github.com/Aircoookie/WLED" target="_blank">WLED</a> version 0.8.3<br><br>
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki/Contributors-&-About" target="_blank">Contributors, dependencies and special thanks</a><br>
|
||||
A huge thank you to everyone who helped me create WLED!<br><br>
|
||||
(c) 2016-2018 Christian Schwinne <br>
|
||||
(c) 2016-2019 Christian Schwinne <br>
|
||||
<i>Licensed under the MIT license</i><br><br>
|
||||
Server message: <span class="msg"> Response error! </span><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
||||
</html>)=====";
|
||||
|
||||
@@ -0,0 +1,487 @@
|
||||
#ifndef Espalexa_h
|
||||
#define Espalexa_h
|
||||
|
||||
/*
|
||||
* Alexa Voice On/Off/Brightness/Color Control. Emulates a Philips Hue bridge to Alexa.
|
||||
*
|
||||
* This was put together from these two excellent projects:
|
||||
* https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch
|
||||
* https://github.com/probonopd/ESP8266HueEmulator
|
||||
*/
|
||||
/*
|
||||
* @title Espalexa library
|
||||
* @version 2.3.3
|
||||
* @author Christian Schwinne
|
||||
* @license MIT
|
||||
* @contributors d-999
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
//you can use these defines for library config in your sketch. Just use them before #include <Espalexa.h>
|
||||
//#define ESPALEXA_ASYNC
|
||||
|
||||
#ifndef ESPALEXA_MAXDEVICES
|
||||
#define ESPALEXA_MAXDEVICES 10 //this limit only has memory reasons, set it higher should you need to
|
||||
#endif
|
||||
|
||||
//#define ESPALEXA_DEBUG
|
||||
|
||||
#ifdef ESPALEXA_ASYNC
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <AsyncTCP.h>
|
||||
#else
|
||||
#include <ESPAsyncTCP.h>
|
||||
#endif
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#else
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <WiFi.h>
|
||||
#include "../webserver/WebServer.h" //if you get an error here please update to ESP32 arduino core 1.0.0
|
||||
#else
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
#ifdef ESPALEXA_DEBUG
|
||||
#pragma message "Espalexa 2.3.3 debug mode"
|
||||
#define EA_DEBUG(x) Serial.print (x)
|
||||
#define EA_DEBUGLN(x) Serial.println (x)
|
||||
#else
|
||||
#define EA_DEBUG(x)
|
||||
#define EA_DEBUGLN(x)
|
||||
#endif
|
||||
|
||||
#include "EspalexaDevice.h"
|
||||
|
||||
class Espalexa {
|
||||
private:
|
||||
//private member vars
|
||||
#ifdef ESPALEXA_ASYNC
|
||||
AsyncWebServer* serverAsync;
|
||||
AsyncWebServerRequest* server; //this saves many #defines
|
||||
String body = "";
|
||||
#elif defined ARDUINO_ARCH_ESP32
|
||||
WebServer* server;
|
||||
#else
|
||||
ESP8266WebServer* server;
|
||||
#endif
|
||||
uint8_t currentDeviceCount = 0;
|
||||
|
||||
EspalexaDevice* devices[ESPALEXA_MAXDEVICES] = {};
|
||||
//Keep in mind that Device IDs go from 1 to DEVICES, cpp arrays from 0 to DEVICES-1!!
|
||||
|
||||
WiFiUDP espalexaUdp;
|
||||
IPAddress ipMulti;
|
||||
bool udpConnected = false;
|
||||
char packetBuffer[255]; //buffer to hold incoming udp packet
|
||||
String escapedMac=""; //lowercase mac address
|
||||
|
||||
//private member functions
|
||||
String deviceJsonString(uint8_t deviceId)
|
||||
{
|
||||
if (deviceId < 1 || deviceId > currentDeviceCount) return "{}"; //error
|
||||
EspalexaDevice* dev = devices[deviceId-1];
|
||||
String json = "{\"type\":\"";
|
||||
json += dev->isColorDevice() ? "Extended color light" : "Dimmable light";
|
||||
json += "\",\"manufacturername\":\"OpenSource\",\"swversion\":\"0.1\",\"name\":\"";
|
||||
json += dev->getName();
|
||||
json += "\",\"uniqueid\":\""+ WiFi.macAddress() +"-"+ (deviceId+1) ;
|
||||
json += "\",\"modelid\":\"LST001\",\"state\":{\"on\":";
|
||||
json += boolString(dev->getValue()) +",\"bri\":"+ (String)(dev->getLastValue()-1) ;
|
||||
if (dev->isColorDevice())
|
||||
{
|
||||
json += ",\"xy\":[0.00000,0.00000],\"colormode\":\"";
|
||||
json += (dev->isColorTemperatureMode()) ? "ct":"hs";
|
||||
json += "\",\"effect\":\"none\",\"ct\":" + (String)(dev->getCt()) + ",\"hue\":" + (String)(dev->getHue()) + ",\"sat\":" + (String)(dev->getSat());
|
||||
}
|
||||
json +=",\"alert\":\"none\",\"reachable\":true}}";
|
||||
return json;
|
||||
}
|
||||
|
||||
//Espalexa status page /espalexa
|
||||
void servePage()
|
||||
{
|
||||
EA_DEBUGLN("HTTP Req espalexa ...\n");
|
||||
String res = "Hello from Espalexa!\r\n\r\n";
|
||||
for (int i=0; i<currentDeviceCount; i++)
|
||||
{
|
||||
res += "Value of device " + String(i+1) + " (" + devices[i]->getName() + "): " + String(devices[i]->getValue()) + "\r\n";
|
||||
}
|
||||
res += "\r\nFree Heap: " + (String)ESP.getFreeHeap();
|
||||
res += "\r\nUptime: " + (String)millis();
|
||||
res += "\r\n\r\nEspalexa library v2.3.3 by Christian Schwinne 2019";
|
||||
server->send(200, "text/plain", res);
|
||||
}
|
||||
|
||||
//not found URI (only if internal webserver is used)
|
||||
void serveNotFound()
|
||||
{
|
||||
EA_DEBUGLN("Not-Found HTTP call:");
|
||||
#ifndef ESPALEXA_ASYNC
|
||||
EA_DEBUGLN("URI: " + server->uri());
|
||||
EA_DEBUGLN("Body: " + server->arg(0));
|
||||
if(!handleAlexaApiCall(server->uri(), server->arg(0)))
|
||||
#else
|
||||
EA_DEBUGLN("URI: " + server->url());
|
||||
EA_DEBUGLN("Body: " + body);
|
||||
if(!handleAlexaApiCall(server))
|
||||
#endif
|
||||
server->send(404, "text/plain", "Not Found (espalexa-internal)");
|
||||
}
|
||||
|
||||
//send description.xml device property page
|
||||
void serveDescription()
|
||||
{
|
||||
EA_DEBUGLN("# Responding to description.xml ... #\n");
|
||||
IPAddress localIP = WiFi.localIP();
|
||||
char s[16];
|
||||
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
|
||||
|
||||
String setup_xml = "<?xml version=\"1.0\" ?>"
|
||||
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
|
||||
"<specVersion><major>1</major><minor>0</minor></specVersion>"
|
||||
"<URLBase>http://"+ String(s) +":80/</URLBase>"
|
||||
"<device>"
|
||||
"<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>"
|
||||
"<friendlyName>Philips hue ("+ String(s) +")</friendlyName>"
|
||||
"<manufacturer>Royal Philips Electronics</manufacturer>"
|
||||
"<manufacturerURL>http://www.philips.com</manufacturerURL>"
|
||||
"<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>"
|
||||
"<modelName>Philips hue bridge 2012</modelName>"
|
||||
"<modelNumber>929000226503</modelNumber>"
|
||||
"<modelURL>http://www.meethue.com</modelURL>"
|
||||
"<serialNumber>"+ escapedMac +"</serialNumber>"
|
||||
"<UDN>uuid:2f402f80-da50-11e1-9b23-"+ escapedMac +"</UDN>"
|
||||
"<presentationURL>index.html</presentationURL>"
|
||||
"<iconList>"
|
||||
" <icon>"
|
||||
" <mimetype>image/png</mimetype>"
|
||||
" <height>48</height>"
|
||||
" <width>48</width>"
|
||||
" <depth>24</depth>"
|
||||
" <url>hue_logo_0.png</url>"
|
||||
" </icon>"
|
||||
" <icon>"
|
||||
" <mimetype>image/png</mimetype>"
|
||||
" <height>120</height>"
|
||||
" <width>120</width>"
|
||||
" <depth>24</depth>"
|
||||
" <url>hue_logo_3.png</url>"
|
||||
" </icon>"
|
||||
"</iconList>"
|
||||
"</device>"
|
||||
"</root>";
|
||||
|
||||
server->send(200, "text/xml", setup_xml.c_str());
|
||||
|
||||
EA_DEBUG("Sending :");
|
||||
EA_DEBUGLN(setup_xml);
|
||||
}
|
||||
|
||||
//init the server
|
||||
void startHttpServer()
|
||||
{
|
||||
#ifdef ESPALEXA_ASYNC
|
||||
if (serverAsync == nullptr) {
|
||||
serverAsync = new AsyncWebServer(80);
|
||||
serverAsync->onNotFound([=](AsyncWebServerRequest *request){server = request; serveNotFound();});
|
||||
}
|
||||
|
||||
serverAsync->onRequestBody([=](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){
|
||||
char b[len +1];
|
||||
b[len] = 0;
|
||||
memcpy(b, data, len);
|
||||
body = b; //save the body so we can use it for the API call
|
||||
EA_DEBUG("Received body: ");
|
||||
EA_DEBUGLN(body);
|
||||
});
|
||||
serverAsync->on("/espalexa", HTTP_GET, [=](AsyncWebServerRequest *request){server = request; servePage();});
|
||||
serverAsync->on("/description.xml", HTTP_GET, [=](AsyncWebServerRequest *request){server = request; serveDescription();});
|
||||
serverAsync->begin();
|
||||
|
||||
#else
|
||||
if (server == nullptr) {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
server = new WebServer(80);
|
||||
#else
|
||||
server = new ESP8266WebServer(80);
|
||||
#endif
|
||||
server->onNotFound([=](){serveNotFound();});
|
||||
}
|
||||
|
||||
server->on("/espalexa", HTTP_GET, [=](){servePage();});
|
||||
server->on("/description.xml", HTTP_GET, [=](){serveDescription();});
|
||||
server->begin();
|
||||
#endif
|
||||
}
|
||||
|
||||
//called when Alexa sends ON command
|
||||
void alexaOn(uint8_t deviceId)
|
||||
{
|
||||
devices[deviceId-1]->setValue(devices[deviceId-1]->getLastValue());
|
||||
devices[deviceId-1]->setPropertyChanged(1);
|
||||
devices[deviceId-1]->doCallback();
|
||||
}
|
||||
|
||||
//called when Alexa sends OFF command
|
||||
void alexaOff(uint8_t deviceId)
|
||||
{
|
||||
devices[deviceId-1]->setValue(0);
|
||||
devices[deviceId-1]->setPropertyChanged(2);
|
||||
devices[deviceId-1]->doCallback();
|
||||
}
|
||||
|
||||
//called when Alexa sends BRI command
|
||||
void alexaDim(uint8_t deviceId, uint8_t briL)
|
||||
{
|
||||
if (briL == 255)
|
||||
{
|
||||
devices[deviceId-1]->setValue(255);
|
||||
} else {
|
||||
devices[deviceId-1]->setValue(briL+1);
|
||||
}
|
||||
devices[deviceId-1]->setPropertyChanged(3);
|
||||
devices[deviceId-1]->doCallback();
|
||||
}
|
||||
|
||||
//called when Alexa sends HUE command
|
||||
void alexaCol(uint8_t deviceId, uint16_t hue, uint8_t sat)
|
||||
{
|
||||
devices[deviceId-1]->setColor(hue, sat);
|
||||
devices[deviceId-1]->setPropertyChanged(4);
|
||||
devices[deviceId-1]->doCallback();
|
||||
}
|
||||
|
||||
//called when Alexa sends CT command (color temperature)
|
||||
void alexaCt(uint8_t deviceId, uint16_t ct)
|
||||
{
|
||||
devices[deviceId-1]->setColor(ct);
|
||||
devices[deviceId-1]->setPropertyChanged(5);
|
||||
devices[deviceId-1]->doCallback();
|
||||
}
|
||||
|
||||
//respond to UDP SSDP M-SEARCH
|
||||
void respondToSearch()
|
||||
{
|
||||
IPAddress localIP = WiFi.localIP();
|
||||
char s[16];
|
||||
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
|
||||
|
||||
String response =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"EXT:\r\n"
|
||||
"CACHE-CONTROL: max-age=100\r\n" // SSDP_INTERVAL
|
||||
"LOCATION: http://"+ String(s) +":80/description.xml\r\n"
|
||||
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.17.0\r\n" // _modelName, _modelNumber
|
||||
"hue-bridgeid: "+ escapedMac +"\r\n"
|
||||
"ST: urn:schemas-upnp-org:device:basic:1\r\n" // _deviceType
|
||||
"USN: uuid:2f402f80-da50-11e1-9b23-"+ escapedMac +"::upnp:rootdevice\r\n" // _uuid::_deviceType
|
||||
"\r\n";
|
||||
|
||||
espalexaUdp.beginPacket(espalexaUdp.remoteIP(), espalexaUdp.remotePort());
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
espalexaUdp.write((uint8_t*)response.c_str(), response.length());
|
||||
#else
|
||||
espalexaUdp.write(response.c_str());
|
||||
#endif
|
||||
espalexaUdp.endPacket();
|
||||
}
|
||||
|
||||
String boolString(bool st)
|
||||
{
|
||||
return(st)?"true":"false";
|
||||
}
|
||||
|
||||
public:
|
||||
Espalexa(){}
|
||||
|
||||
//initialize interfaces
|
||||
#ifdef ESPALEXA_ASYNC
|
||||
bool begin(AsyncWebServer* externalServer = nullptr)
|
||||
#elif defined ARDUINO_ARCH_ESP32
|
||||
bool begin(WebServer* externalServer = nullptr)
|
||||
#else
|
||||
bool begin(ESP8266WebServer* externalServer = nullptr)
|
||||
#endif
|
||||
{
|
||||
EA_DEBUGLN("Espalexa Begin...");
|
||||
EA_DEBUG("MAXDEVICES ");
|
||||
EA_DEBUGLN(ESPALEXA_MAXDEVICES);
|
||||
escapedMac = WiFi.macAddress();
|
||||
escapedMac.replace(":", "");
|
||||
escapedMac.toLowerCase();
|
||||
|
||||
#ifdef ESPALEXA_ASYNC
|
||||
serverAsync = externalServer;
|
||||
#else
|
||||
server = externalServer;
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
udpConnected = espalexaUdp.beginMulticast(IPAddress(239, 255, 255, 250), 1900);
|
||||
#else
|
||||
udpConnected = espalexaUdp.beginMulticast(WiFi.localIP(), IPAddress(239, 255, 255, 250), 1900);
|
||||
#endif
|
||||
|
||||
if (udpConnected){
|
||||
|
||||
startHttpServer();
|
||||
EA_DEBUGLN("Done");
|
||||
return true;
|
||||
}
|
||||
EA_DEBUGLN("Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
//service loop
|
||||
void loop() {
|
||||
#ifndef ESPALEXA_ASYNC
|
||||
if (server == nullptr) return; //only if begin() was not called
|
||||
server->handleClient();
|
||||
#endif
|
||||
|
||||
if (!udpConnected) return;
|
||||
int packetSize = espalexaUdp.parsePacket();
|
||||
if (!packetSize) return; //no new udp packet
|
||||
|
||||
EA_DEBUGLN("Got UDP!");
|
||||
int len = espalexaUdp.read(packetBuffer, 254);
|
||||
if (len > 0) {
|
||||
packetBuffer[len] = 0;
|
||||
}
|
||||
espalexaUdp.flush();
|
||||
|
||||
String request = packetBuffer;
|
||||
EA_DEBUGLN(request);
|
||||
if(request.indexOf("M-SEARCH") >= 0) {
|
||||
if(request.indexOf("upnp:rootdevice") > 0 || request.indexOf("asic:1") > 0) {
|
||||
EA_DEBUGLN("Responding search req...");
|
||||
respondToSearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool addDevice(EspalexaDevice* d)
|
||||
{
|
||||
EA_DEBUG("Adding device ");
|
||||
EA_DEBUGLN((currentDeviceCount+1));
|
||||
if (currentDeviceCount >= ESPALEXA_MAXDEVICES) return false;
|
||||
devices[currentDeviceCount] = d;
|
||||
currentDeviceCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addDevice(String deviceName, CallbackBriFunction callback, uint8_t initialValue = 0)
|
||||
{
|
||||
EA_DEBUG("Constructing device ");
|
||||
EA_DEBUGLN((currentDeviceCount+1));
|
||||
if (currentDeviceCount >= ESPALEXA_MAXDEVICES) return false;
|
||||
EspalexaDevice* d = new EspalexaDevice(deviceName, callback, initialValue);
|
||||
return addDevice(d);
|
||||
}
|
||||
|
||||
bool addDevice(String deviceName, CallbackColFunction callback, uint8_t initialValue = 0)
|
||||
{
|
||||
EA_DEBUG("Constructing device ");
|
||||
EA_DEBUGLN((currentDeviceCount+1));
|
||||
if (currentDeviceCount >= ESPALEXA_MAXDEVICES) return false;
|
||||
EspalexaDevice* d = new EspalexaDevice(deviceName, callback, initialValue);
|
||||
return addDevice(d);
|
||||
}
|
||||
|
||||
//basic implementation of Philips hue api functions needed for basic Alexa control
|
||||
#ifdef ESPALEXA_ASYNC
|
||||
bool handleAlexaApiCall(AsyncWebServerRequest* request)
|
||||
{
|
||||
server = request; //copy request reference
|
||||
String req = request->url(); //body from global variable
|
||||
EA_DEBUGLN(request->contentType());
|
||||
if (request->hasParam("body", true)) // This is necessary, otherwise ESP crashes if there is no body
|
||||
{
|
||||
EA_DEBUG("BodyMethod2");
|
||||
body = request->getParam("body", true)->value();
|
||||
}
|
||||
EA_DEBUG("FinalBody: ");
|
||||
EA_DEBUGLN(body);
|
||||
#else
|
||||
bool handleAlexaApiCall(String req, String body)
|
||||
{
|
||||
#endif
|
||||
EA_DEBUGLN("AlexaApiCall");
|
||||
if (req.indexOf("api") <0) return false; //return if not an API call
|
||||
EA_DEBUGLN("ok");
|
||||
|
||||
if (body.indexOf("devicetype") > 0) //client wants a hue api username, we dont care and give static
|
||||
{
|
||||
EA_DEBUGLN("devType");
|
||||
body = "";
|
||||
server->send(200, "application/json", "[{\"success\":{\"username\": \"2WLEDHardQrI3WHYTHoMcXHgEspsM8ZZRpSKtBQr\"}}]");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (req.indexOf("state") > 0) //client wants to control light
|
||||
{
|
||||
server->send(200, "application/json", "[{\"success\":true}]"); //short valid response
|
||||
|
||||
int tempDeviceId = req.substring(req.indexOf("lights")+7).toInt();
|
||||
EA_DEBUG("ls"); EA_DEBUGLN(tempDeviceId);
|
||||
if (body.indexOf("false")>0) {alexaOff(tempDeviceId); return true;}
|
||||
if (body.indexOf("bri")>0 ) {alexaDim(tempDeviceId, body.substring(body.indexOf("bri") +5).toInt()); return true;}
|
||||
if (body.indexOf("hue")>0 ) {alexaCol(tempDeviceId, body.substring(body.indexOf("hue") +5).toInt(), body.substring(body.indexOf("sat") +5).toInt()); return true;}
|
||||
if (body.indexOf("ct") >0 ) {alexaCt (tempDeviceId, body.substring(body.indexOf("ct") +4).toInt()); return true;}
|
||||
alexaOn(tempDeviceId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int pos = req.indexOf("lights");
|
||||
if (pos > 0) //client wants light info
|
||||
{
|
||||
int tempDeviceId = req.substring(pos+7).toInt();
|
||||
EA_DEBUG("l"); EA_DEBUGLN(tempDeviceId);
|
||||
|
||||
if (tempDeviceId == 0) //client wants all lights
|
||||
{
|
||||
EA_DEBUGLN("lAll");
|
||||
String jsonTemp = "{";
|
||||
for (int i = 0; i<currentDeviceCount; i++)
|
||||
{
|
||||
jsonTemp += "\"" + String(i+1) + "\":";
|
||||
jsonTemp += deviceJsonString(i+1);
|
||||
if (i < currentDeviceCount-1) jsonTemp += ",";
|
||||
}
|
||||
jsonTemp += "}";
|
||||
server->send(200, "application/json", jsonTemp);
|
||||
} else //client wants one light (tempDeviceId)
|
||||
{
|
||||
server->send(200, "application/json", deviceJsonString(tempDeviceId));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//we dont care about other api commands at this time and send empty JSON
|
||||
server->send(200, "application/json", "{}");
|
||||
return true;
|
||||
}
|
||||
|
||||
//is an unique device ID
|
||||
String getEscapedMac()
|
||||
{
|
||||
return escapedMac;
|
||||
}
|
||||
|
||||
//convert brightness (0-255) to percentage
|
||||
uint8_t toPercent(uint8_t bri)
|
||||
{
|
||||
uint16_t perc = bri * 100;
|
||||
return perc / 255;
|
||||
}
|
||||
|
||||
~Espalexa(){delete devices;} //note: Espalexa is NOT meant to be destructed
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
//EspalexaDevice Class
|
||||
|
||||
#include "EspalexaDevice.h"
|
||||
|
||||
EspalexaDevice::EspalexaDevice(){}
|
||||
|
||||
EspalexaDevice::EspalexaDevice(String deviceName, CallbackBriFunction gnCallback, uint8_t initialValue) { //constructor
|
||||
|
||||
_deviceName = deviceName;
|
||||
_callback = gnCallback;
|
||||
_val = initialValue;
|
||||
_val_last = _val;
|
||||
}
|
||||
|
||||
EspalexaDevice::EspalexaDevice(String deviceName, CallbackColFunction gnCallback, uint8_t initialValue) { //constructor for color device
|
||||
|
||||
_deviceName = deviceName;
|
||||
_callbackCol = gnCallback;
|
||||
_callback = nullptr;
|
||||
_val = initialValue;
|
||||
_val_last = _val;
|
||||
}
|
||||
|
||||
EspalexaDevice::~EspalexaDevice(){/*nothing to destruct*/}
|
||||
|
||||
bool EspalexaDevice::isColorDevice()
|
||||
{
|
||||
//if brightness-only callback is null, we have color device
|
||||
return (_callback == nullptr);
|
||||
}
|
||||
|
||||
bool EspalexaDevice::isColorTemperatureMode()
|
||||
{
|
||||
return _ct;
|
||||
}
|
||||
|
||||
String EspalexaDevice::getName()
|
||||
{
|
||||
return _deviceName;
|
||||
}
|
||||
|
||||
uint8_t EspalexaDevice::getLastChangedProperty()
|
||||
{
|
||||
return _changed;
|
||||
}
|
||||
|
||||
uint8_t EspalexaDevice::getValue()
|
||||
{
|
||||
return _val;
|
||||
}
|
||||
|
||||
uint16_t EspalexaDevice::getHue()
|
||||
{
|
||||
return _hue;
|
||||
}
|
||||
|
||||
uint8_t EspalexaDevice::getSat()
|
||||
{
|
||||
return _sat;
|
||||
}
|
||||
|
||||
uint16_t EspalexaDevice::getCt()
|
||||
{
|
||||
if (_ct == 0) return 500;
|
||||
return _ct;
|
||||
}
|
||||
|
||||
uint32_t EspalexaDevice::getColorRGB()
|
||||
{
|
||||
uint8_t rgb[3];
|
||||
|
||||
if (isColorTemperatureMode())
|
||||
{
|
||||
//TODO tweak a bit to match hue lamp characteristics
|
||||
//based on https://gist.github.com/paulkaplan/5184275
|
||||
float temp = 10000/ _ct; //kelvins = 1,000,000/mired (and that /100)
|
||||
float r, g, b;
|
||||
|
||||
if( temp <= 66 ){
|
||||
r = 255;
|
||||
g = temp;
|
||||
g = 99.470802 * log(g) - 161.119568;
|
||||
if( temp <= 19){
|
||||
b = 0;
|
||||
} else {
|
||||
b = temp-10;
|
||||
b = 138.517731 * log(b) - 305.044793;
|
||||
}
|
||||
} else {
|
||||
r = temp - 60;
|
||||
r = 329.698727 * pow(r, -0.13320476);
|
||||
g = temp - 60;
|
||||
g = 288.12217 * pow(g, -0.07551485 );
|
||||
b = 255;
|
||||
}
|
||||
|
||||
rgb[0] = (byte)constrain(r,0.1,255.1);
|
||||
rgb[1] = (byte)constrain(g,0.1,255.1);
|
||||
rgb[2] = (byte)constrain(b,0.1,255.1);
|
||||
}
|
||||
else
|
||||
{ //hue + sat mode
|
||||
float h = ((float)_hue)/65535.0;
|
||||
float s = ((float)_sat)/255.0;
|
||||
byte i = floor(h*6);
|
||||
float f = h * 6-i;
|
||||
float p = 255 * (1-s);
|
||||
float q = 255 * (1-f*s);
|
||||
float t = 255 * (1-(1-f)*s);
|
||||
switch (i%6) {
|
||||
case 0: rgb[0]=255,rgb[1]=t,rgb[2]=p;break;
|
||||
case 1: rgb[0]=q,rgb[1]=255,rgb[2]=p;break;
|
||||
case 2: rgb[0]=p,rgb[1]=255,rgb[2]=t;break;
|
||||
case 3: rgb[0]=p,rgb[1]=q,rgb[2]=255;break;
|
||||
case 4: rgb[0]=t,rgb[1]=p,rgb[2]=255;break;
|
||||
case 5: rgb[0]=255,rgb[1]=p,rgb[2]=q;
|
||||
}
|
||||
}
|
||||
return ((rgb[0] << 16) | (rgb[1] << 8) | (rgb[2]));
|
||||
}
|
||||
|
||||
uint8_t EspalexaDevice::getLastValue()
|
||||
{
|
||||
if (_val_last == 0) return 255;
|
||||
return _val_last;
|
||||
}
|
||||
|
||||
void EspalexaDevice::setPropertyChanged(uint8_t p)
|
||||
{
|
||||
//0: initial 1: on 2: off 3: bri 4: col 5: ct
|
||||
_changed = p;
|
||||
}
|
||||
|
||||
//you need to re-discover the device for the Alexa name to change
|
||||
void EspalexaDevice::setName(String name)
|
||||
{
|
||||
_deviceName = name;
|
||||
}
|
||||
|
||||
void EspalexaDevice::setValue(uint8_t val)
|
||||
{
|
||||
if (_val != 0)
|
||||
{
|
||||
_val_last = _val;
|
||||
}
|
||||
if (val != 0)
|
||||
{
|
||||
_val_last = val;
|
||||
}
|
||||
_val = val;
|
||||
}
|
||||
|
||||
void EspalexaDevice::setPercent(uint8_t perc)
|
||||
{
|
||||
uint16_t val = perc * 255;
|
||||
val /= 100;
|
||||
if (val > 255) val = 255;
|
||||
setValue(val);
|
||||
}
|
||||
|
||||
void EspalexaDevice::setColor(uint16_t hue, uint8_t sat)
|
||||
{
|
||||
_hue = hue;
|
||||
_sat = sat;
|
||||
_ct = 0;
|
||||
}
|
||||
|
||||
void EspalexaDevice::setColor(uint16_t ct)
|
||||
{
|
||||
_ct = ct;
|
||||
}
|
||||
|
||||
void EspalexaDevice::doCallback()
|
||||
{
|
||||
(_callback != nullptr) ? _callback(_val) : _callbackCol(_val, getColorRGB());
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef EspalexaDevice_h
|
||||
#define EspalexaDevice_h
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
typedef void (*CallbackBriFunction) (uint8_t br);
|
||||
typedef void (*CallbackColFunction) (uint8_t br, uint32_t col);
|
||||
|
||||
class EspalexaDevice {
|
||||
private:
|
||||
String _deviceName;
|
||||
CallbackBriFunction _callback;
|
||||
CallbackColFunction _callbackCol;
|
||||
uint8_t _val, _val_last, _sat = 0;
|
||||
uint16_t _hue = 0, _ct = 0;
|
||||
uint8_t _changed = 0;
|
||||
|
||||
public:
|
||||
EspalexaDevice();
|
||||
~EspalexaDevice();
|
||||
EspalexaDevice(String deviceName, CallbackBriFunction gnCallback, uint8_t initialValue =0);
|
||||
EspalexaDevice(String deviceName, CallbackColFunction gnCallback, uint8_t initialValue =0);
|
||||
|
||||
bool isColorDevice();
|
||||
bool isColorTemperatureMode();
|
||||
String getName();
|
||||
uint8_t getLastChangedProperty();
|
||||
uint8_t getValue();
|
||||
uint16_t getHue();
|
||||
uint8_t getSat();
|
||||
uint16_t getCt();
|
||||
uint32_t getColorRGB();
|
||||
|
||||
void setPropertyChanged(uint8_t p);
|
||||
void setValue(uint8_t bri);
|
||||
void setPercent(uint8_t perc);
|
||||
void setName(String name);
|
||||
void setColor(uint16_t hue, uint8_t sat);
|
||||
void setColor(uint16_t ct);
|
||||
|
||||
void doCallback();
|
||||
|
||||
uint8_t getLastValue(); //last value that was not off (1-255)
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Christian Schwinne
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -16,7 +16,8 @@
|
||||
#else
|
||||
#include <WProgram.h>
|
||||
#endif
|
||||
#include <Time.h> //http://www.arduino.cc/playground/Code/Time
|
||||
|
||||
#include "../time/Time.h" //http://www.arduino.cc/playground/Code/Time
|
||||
|
||||
//convenient constants for dstRules
|
||||
enum week_t {Last, First, Second, Third, Fourth};
|
||||
|
||||
+40
-26
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
/*
|
||||
* @title WLED project sketch
|
||||
* @version 0.8.2
|
||||
* @version 0.8.3
|
||||
* @author Christian Schwinne
|
||||
*/
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
#endif
|
||||
|
||||
#include <EEPROM.h>
|
||||
#include <WiFiUDP.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <DNSServer.h>
|
||||
#ifndef WLED_DISABLE_OTA
|
||||
#include <ArduinoOTA.h>
|
||||
@@ -60,6 +60,10 @@
|
||||
#include "src/dependencies/time/Time.h"
|
||||
#include "src/dependencies/time/TimeLib.h"
|
||||
#include "src/dependencies/timezone/Timezone.h"
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
#define ESPALEXA_MAXDEVICES 1
|
||||
#include "src/dependencies/espalexa/Espalexa.h"
|
||||
#endif
|
||||
#ifndef WLED_DISABLE_BLYNK
|
||||
#include "src/dependencies/blynk/BlynkSimpleEsp.h"
|
||||
#endif
|
||||
@@ -74,8 +78,8 @@
|
||||
|
||||
|
||||
//version code in format yymmddb (b = daily build)
|
||||
#define VERSION 1812052
|
||||
char versionString[] = "0.8.2";
|
||||
#define VERSION 1902112
|
||||
char versionString[] = "0.8.3";
|
||||
|
||||
|
||||
//AP and OTA default passwords (for maximum change them!)
|
||||
@@ -98,7 +102,7 @@ char ntpServerName[] = "0.wled.pool.ntp.org"; //NTP server to use
|
||||
//WiFi CONFIG (all these can be changed via web UI, no need to set them here)
|
||||
char clientSSID[33] = "Your_Network";
|
||||
char clientPass[65] = "";
|
||||
char cmDNS[33] = "led"; //mDNS address (x.local), only for Apple and Windows, if Bonjour installed
|
||||
char cmDNS[33] = "x"; //mDNS address (placeholder, will be replaced by wledXXXXXXXXXXXX.local)
|
||||
char apSSID[65] = ""; //AP off by default (unless setup)
|
||||
byte apChannel = 1; //2.4GHz WiFi AP channel (1-13)
|
||||
byte apHide = 0; //hidden AP SSID
|
||||
@@ -118,10 +122,8 @@ bool autoRGBtoRGBW = false; //if RGBW enabled, calculate White
|
||||
bool turnOnAtBoot = true; //turn on LEDs at power-up
|
||||
byte bootPreset = 0; //save preset to load after power-up
|
||||
|
||||
byte colS[]{255, 159, 0}; //default RGB color
|
||||
byte colSecS[]{0, 0, 0}; //default RGB secondary color
|
||||
byte whiteS = 0; //default White channel
|
||||
byte whiteSecS = 0; //default secondary White channel
|
||||
byte colS[]{255, 159, 0, 0}; //default RGB(W) color
|
||||
byte colSecS[]{0, 0, 0, 0}; //default RGB(W) secondary color
|
||||
byte briS = 127; //default brightness
|
||||
byte effectDefault = 0;
|
||||
byte effectSpeedDefault = 75;
|
||||
@@ -139,7 +141,6 @@ bool enableSecTransition = true; //also enable transition for secon
|
||||
uint16_t transitionDelay = 900; //default crossfade duration in ms
|
||||
|
||||
bool reverseMode = false; //flip entire LED strip (reverses all effect directions)
|
||||
bool initLedsLast = false; //turn on LEDs only after WiFi connected/AP open
|
||||
bool skipFirstLed = false; //ignore first LED in strip (useful if you need the LED as signal repeater)
|
||||
byte briMultiplier = 100; //% of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127)
|
||||
|
||||
@@ -242,16 +243,14 @@ uint16_t userVar0 = 0, userVar1 = 0;
|
||||
|
||||
//internal global variable declarations
|
||||
//color
|
||||
byte col[]{255, 159, 0}; //target RGB color
|
||||
byte colOld[]{0, 0, 0}; //color before transition
|
||||
byte colT[]{0, 0, 0}; //current color
|
||||
byte colIT[]{0, 0, 0}; //color that was last sent to LEDs
|
||||
byte colSec[]{0, 0, 0};
|
||||
byte colSecT[]{0, 0, 0};
|
||||
byte colSecOld[]{0, 0, 0};
|
||||
byte colSecIT[]{0, 0, 0};
|
||||
byte white = whiteS, whiteOld, whiteT, whiteIT;
|
||||
byte whiteSec = whiteSecS, whiteSecOld, whiteSecT, whiteSecIT;
|
||||
byte col[]{255, 159, 0, 0}; //target RGB(W) color
|
||||
byte colOld[]{0, 0, 0, 0}; //color before transition
|
||||
byte colT[]{0, 0, 0, 0}; //current color
|
||||
byte colIT[]{0, 0, 0, 0}; //color that was last sent to LEDs
|
||||
byte colSec[]{0, 0, 0, 0};
|
||||
byte colSecT[]{0, 0, 0, 0};
|
||||
byte colSecOld[]{0, 0, 0, 0};
|
||||
byte colSecIT[]{0, 0, 0, 0};
|
||||
|
||||
byte lastRandomIndex = 0; //used to save last random color so the new one is not the same
|
||||
|
||||
@@ -271,6 +270,8 @@ unsigned long nightlightStartTime;
|
||||
byte briNlT = 0; //current nightlight brightness
|
||||
|
||||
//brightness
|
||||
bool offMode = false;
|
||||
unsigned long lastOnTime = 0;
|
||||
byte bri = briS;
|
||||
byte briOld = 0;
|
||||
byte briT = 0;
|
||||
@@ -337,7 +338,7 @@ byte timerHours[] = {0,0,0,0,0,0,0,0};
|
||||
byte timerMinutes[] = {0,0,0,0,0,0,0,0};
|
||||
byte timerMacro[] = {0,0,0,0,0,0,0,0};
|
||||
byte timerWeekday[] = {255,255,255,255,255,255,255,255}; //weekdays to activate on
|
||||
//bit pattern of arr elem: 0b11111111: sat,fri,thu,wed,tue,mon,sun,validity
|
||||
//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity
|
||||
|
||||
//blynk
|
||||
bool blynkEnabled = false;
|
||||
@@ -368,11 +369,11 @@ unsigned long auxStartTime = 0;
|
||||
bool auxActive = false, auxActiveBefore = false;
|
||||
|
||||
//alexa udp
|
||||
WiFiUDP alexaUDP;
|
||||
bool alexaUdpConnected = false;
|
||||
IPAddress ipMulti(239, 255, 255, 250);
|
||||
unsigned int portMulti = 1900;
|
||||
String escapedMac;
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
Espalexa espalexa;
|
||||
EspalexaDevice* espalexaDevice;
|
||||
#endif
|
||||
|
||||
//dns server
|
||||
DNSServer dnsServer;
|
||||
@@ -463,6 +464,7 @@ void serveMessage(int,String,String,int=255);
|
||||
void reset()
|
||||
{
|
||||
briT = 0;
|
||||
delay(250); //enough time to send response to client
|
||||
setAllLeds();
|
||||
DEBUG_PRINTLN("MODULE RESET");
|
||||
ESP.restart();
|
||||
@@ -528,7 +530,19 @@ void loop() {
|
||||
handleHue();
|
||||
handleBlynk();
|
||||
}
|
||||
if (briT) strip.service(); //do not update strip if off, prevents flicker on ESP32
|
||||
if (briT) lastOnTime = millis();
|
||||
if (millis() - lastOnTime < 600)
|
||||
{
|
||||
offMode = false;
|
||||
strip.service();
|
||||
} else if (!offMode)
|
||||
{
|
||||
/*#if LEDPIN == 2 //turn off onboard LED
|
||||
pinMode(2, OUTPUT);
|
||||
digitalWrite(2, HIGH);
|
||||
#endif*/
|
||||
offMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
//DEBUG serial logging
|
||||
|
||||
+18
-25
@@ -129,7 +129,7 @@ void saveSettingsToEEPROM()
|
||||
EEPROM.write(368, abs(arlsOffset));
|
||||
EEPROM.write(369, turnOnAtBoot);
|
||||
EEPROM.write(370, useHSBDefault);
|
||||
EEPROM.write(371, whiteS);
|
||||
EEPROM.write(371, colS[3]); //white default
|
||||
EEPROM.write(372, useRGBW);
|
||||
EEPROM.write(373, effectPaletteDefault);
|
||||
EEPROM.write(374, strip.paletteFade);
|
||||
@@ -141,7 +141,7 @@ void saveSettingsToEEPROM()
|
||||
EEPROM.write(378, colSecS[0]);
|
||||
EEPROM.write(379, colSecS[1]);
|
||||
EEPROM.write(380, colSecS[2]);
|
||||
EEPROM.write(381, whiteSecS);
|
||||
EEPROM.write(381, colSecS[3]);
|
||||
EEPROM.write(382, strip.paletteBlend);
|
||||
EEPROM.write(383, strip.colorOrder);
|
||||
|
||||
@@ -158,7 +158,7 @@ void saveSettingsToEEPROM()
|
||||
EEPROM.write(394, abs(utcOffsetSecs) & 0xFF);
|
||||
EEPROM.write(395, (abs(utcOffsetSecs) >> 8) & 0xFF);
|
||||
EEPROM.write(396, (utcOffsetSecs<0)); //is negative
|
||||
EEPROM.write(397, initLedsLast);
|
||||
//397 was initLedsLast
|
||||
EEPROM.write(398, (ledCount >> 8) & 0xFF);
|
||||
EEPROM.write(399, !enableSecTransition);
|
||||
|
||||
@@ -352,7 +352,7 @@ void loadSettingsFromEEPROM(bool first)
|
||||
if (!EEPROM.read(367)) arlsOffset = -arlsOffset;
|
||||
turnOnAtBoot = EEPROM.read(369);
|
||||
useHSBDefault = EEPROM.read(370);
|
||||
whiteS = EEPROM.read(371); white = whiteS;
|
||||
colS[3] = EEPROM.read(371); col[3] = colS[3];
|
||||
useRGBW = EEPROM.read(372);
|
||||
effectPaletteDefault = EEPROM.read(373); effectPalette = effectPaletteDefault;
|
||||
//374 - strip.paletteFade
|
||||
@@ -363,10 +363,10 @@ void loadSettingsFromEEPROM(bool first)
|
||||
}
|
||||
//377 = lastEEPROMversion
|
||||
if (lastEEPROMversion > 1) {
|
||||
colSecS[0] = EEPROM.read(378); colSec[0] = colSecS[0];
|
||||
colSecS[1] = EEPROM.read(379); colSec[1] = colSecS[1];
|
||||
colSecS[2] = EEPROM.read(380); colSec[2] = colSecS[2];
|
||||
whiteSecS = EEPROM.read(381); whiteSec = whiteSecS;
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
colSecS[i] = EEPROM.read(378+i); colSec[i] = colSecS[i];
|
||||
}
|
||||
}
|
||||
if (lastEEPROMversion > 3) {
|
||||
effectIntensityDefault = EEPROM.read(326); effectIntensity = effectIntensityDefault;
|
||||
@@ -495,7 +495,6 @@ void loadSettingsFromEEPROM(bool first)
|
||||
wifiLock = EEPROM.read(393);
|
||||
utcOffsetSecs = EEPROM.read(394) + ((EEPROM.read(395) << 8) & 0xFF00);
|
||||
if (EEPROM.read(396)) utcOffsetSecs = -utcOffsetSecs; //negative
|
||||
initLedsLast = EEPROM.read(397);
|
||||
enableSecTransition = !EEPROM.read(399);
|
||||
|
||||
//favorite setting (preset) memory (25 slots/ each 20byte)
|
||||
@@ -538,14 +537,11 @@ bool applyPreset(byte index, bool loadBri = true, bool loadCol = true, bool load
|
||||
if (loadBri) bri = EEPROM.read(i+1);
|
||||
if (loadCol)
|
||||
{
|
||||
col[0] = EEPROM.read(i+2);
|
||||
col[1] = EEPROM.read(i+3);
|
||||
col[2] = EEPROM.read(i+4);
|
||||
white = EEPROM.read(i+5);
|
||||
colSec[0] = EEPROM.read(i+6);
|
||||
colSec[1] = EEPROM.read(i+7);
|
||||
colSec[2] = EEPROM.read(i+8);
|
||||
whiteSec = EEPROM.read(i+9);
|
||||
for (byte j=0; j<4; j++)
|
||||
{
|
||||
col[j] = EEPROM.read(i+j+2);
|
||||
colSec[j] = EEPROM.read(i+j+6);
|
||||
}
|
||||
}
|
||||
if (loadFX)
|
||||
{
|
||||
@@ -564,14 +560,11 @@ void savePreset(byte index)
|
||||
uint16_t i = 380 + index*20;//min400
|
||||
EEPROM.write(i, 1);
|
||||
EEPROM.write(i+1, bri);
|
||||
EEPROM.write(i+2, col[0]);
|
||||
EEPROM.write(i+3, col[1]);
|
||||
EEPROM.write(i+4, col[2]);
|
||||
EEPROM.write(i+5, white);
|
||||
EEPROM.write(i+6, colSec[0]);
|
||||
EEPROM.write(i+7, colSec[1]);
|
||||
EEPROM.write(i+8, colSec[2]);
|
||||
EEPROM.write(i+9, whiteSec);
|
||||
for (uint16_t j=0; j<4; j++)
|
||||
{
|
||||
EEPROM.write(i+j+2, col[j]);
|
||||
EEPROM.write(i+j+6, colSec[j]);
|
||||
}
|
||||
EEPROM.write(i+10, effectCurrent);
|
||||
EEPROM.write(i+11, effectSpeed);
|
||||
|
||||
|
||||
+21
-11
@@ -16,6 +16,12 @@ void XML_response(bool isHTTP, bool includeTheme)
|
||||
oappendi(col[i]);
|
||||
oappend("</cl>");
|
||||
}
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
oappend("<cs>");
|
||||
oappendi(colSec[i]);
|
||||
oappend("</cs>");
|
||||
}
|
||||
|
||||
oappend("<ns>");
|
||||
oappendi(notifyDirect);
|
||||
@@ -39,13 +45,17 @@ void XML_response(bool isHTTP, bool includeTheme)
|
||||
oappendi(effectPalette);
|
||||
oappend("</fp><wv>");
|
||||
if (useRGBW && !autoRGBtoRGBW) {
|
||||
oappendi(white);
|
||||
oappendi(col[3]);
|
||||
} else {
|
||||
oappend("-1");
|
||||
}
|
||||
oappend("</wv><md>");
|
||||
oappend("</wv><ws>");
|
||||
oappendi(colSec[3]);
|
||||
oappend("</ws><md>");
|
||||
oappendi(useHSB);
|
||||
oappend("</md><ds>");
|
||||
oappend("</md><cy>");
|
||||
oappendi(presetCyclingEnabled);
|
||||
oappend("</cy><ds>");
|
||||
oappend(serverDescription);
|
||||
oappend("</ds>");
|
||||
if (includeTheme)
|
||||
@@ -60,9 +70,9 @@ void XML_response(bool isHTTP, bool includeTheme)
|
||||
oappend(cs[2]);
|
||||
oappend("</cc><cd>#");
|
||||
oappend(cs[3]);
|
||||
oappend("</cd><cs>#");
|
||||
oappend("</cd><cu>#");
|
||||
oappend(cs[4]);
|
||||
oappend("</cs><ct>#");
|
||||
oappend("</cu><ct>#");
|
||||
oappend(cs[5]);
|
||||
oappend("</ct><cf>");
|
||||
oappend(cssFont);
|
||||
@@ -206,11 +216,11 @@ void getSettingsJS(byte subPage)
|
||||
sappend('c',"EW",useRGBW);
|
||||
sappend('i',"CO",strip.colorOrder);
|
||||
sappend('c',"AW",autoRGBtoRGBW);
|
||||
sappend('v',"CW",whiteS);
|
||||
sappend('v',"CW",colS[3]);
|
||||
sappend('v',"SR",colSecS[0]);
|
||||
sappend('v',"SG",colSecS[1]);
|
||||
sappend('v',"SB",colSecS[2]);
|
||||
sappend('v',"SW",whiteSecS);
|
||||
sappend('v',"SW",colSecS[3]);
|
||||
sappend('c',"BO",turnOnAtBoot);
|
||||
sappend('v',"BP",bootPreset);
|
||||
sappend('v',"FX",effectDefault);
|
||||
@@ -229,7 +239,6 @@ void getSettingsJS(byte subPage)
|
||||
sappend('c',"TW",nightlightFade);
|
||||
sappend('i',"PB",strip.paletteBlend);
|
||||
sappend('c',"RV",reverseMode);
|
||||
sappend('c',"EI",initLedsLast);
|
||||
sappend('c',"SL",skipFirstLed);
|
||||
}
|
||||
|
||||
@@ -333,6 +342,7 @@ void getSettingsJS(byte subPage)
|
||||
k[0] = 'H'; sappend('v',k,timerHours[i]);
|
||||
k[0] = 'N'; sappend('v',k,timerMinutes[i]);
|
||||
k[0] = 'T'; sappend('v',k,timerMacro[i]);
|
||||
k[0] = 'W'; sappend('v',k,timerWeekday[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,12 +371,12 @@ void getThemeColors(char o[][9])
|
||||
// accent color (aCol) background (bCol) panel (cCol) controls (dCol) shadows (sCol) text (tCol)
|
||||
default: strcpy(o[0], "D9B310"); strcpy(o[1], "0B3C5D"); strcpy(o[2], "1D2731"); strcpy(o[3], "328CC1"); strcpy(o[4], "000"); strcpy(o[5], "328CC1"); break; //night
|
||||
case 1: strcpy(o[0], "eee"); strcpy(o[1], "ddd"); strcpy(o[2], "b9b9b9"); strcpy(o[3], "049"); strcpy(o[4], "777"); strcpy(o[5], "049"); break; //modern
|
||||
case 2: strcpy(o[0], "abc"); strcpy(o[1], "fff"); strcpy(o[2], "ddd"); strcpy(o[3], "000"); strcpy(o[4], "0004"); strcpy(o[5], "000"); break; //bright
|
||||
case 2: strcpy(o[0], "abb"); strcpy(o[1], "fff"); strcpy(o[2], "ddd"); strcpy(o[3], "000"); strcpy(o[4], "0004"); strcpy(o[5], "000"); break; //bright
|
||||
case 3: strcpy(o[0], "c09f80"); strcpy(o[1], "d7cec7"); strcpy(o[2], "76323f"); strcpy(o[3], "888"); strcpy(o[4], "3334"); strcpy(o[5], "888"); break; //wine
|
||||
case 4: strcpy(o[0], "3cc47c"); strcpy(o[1], "828081"); strcpy(o[2], "d9a803"); strcpy(o[3], "1e392a"); strcpy(o[4], "000a"); strcpy(o[5], "1e392a"); break; //electric
|
||||
case 5: strcpy(o[0], "57bc90"); strcpy(o[1], "a5a5af"); strcpy(o[2], "015249"); strcpy(o[3], "88c9d4"); strcpy(o[4], "0004"); strcpy(o[5], "88c9d4"); break; //mint
|
||||
case 6: strcpy(o[0], "f7c331"); strcpy(o[1], "dcc7aa"); strcpy(o[2], "6b7a8f"); strcpy(o[3], "f7882f"); strcpy(o[4], "0007"); strcpy(o[5], "f7882f"); break; //amber
|
||||
case 7: strcpy(o[0], "fc3"); strcpy(o[1], "124"); strcpy(o[2], "334"); strcpy(o[3], "f1d"); strcpy(o[4], "f00"); strcpy(o[5], "f1d"); break; //club
|
||||
case 6: strcpy(o[0], "f7c331"); strcpy(o[1], "dca"); strcpy(o[2], "6b7a8f"); strcpy(o[3], "f7882f"); strcpy(o[4], "0007"); strcpy(o[5], "f7882f"); break; //amber
|
||||
case 7: strcpy(o[0], "fff"); strcpy(o[1], "333"); strcpy(o[2], "222"); strcpy(o[3], "666"); strcpy(o[4], ""); strcpy(o[5], "fff"); break; //dark
|
||||
case 8: strcpy(o[0], "0ac"); strcpy(o[1], "124"); strcpy(o[2], "224"); strcpy(o[3], "003eff"); strcpy(o[4], "003eff"); strcpy(o[5], "003eff"); break; //air
|
||||
case 9: strcpy(o[0], "f70"); strcpy(o[1], "421"); strcpy(o[2], "221"); strcpy(o[3], "a50"); strcpy(o[4], "f70"); strcpy(o[5], "f70"); break; //nixie
|
||||
case 10: strcpy(o[0], "2d2"); strcpy(o[1], "010"); strcpy(o[2], "121"); strcpy(o[3], "060"); strcpy(o[4], "040"); strcpy(o[5], "3f3"); break; //terminal
|
||||
|
||||
+23
-25
@@ -67,14 +67,11 @@ void handleSettingsSet(byte subPage)
|
||||
//ignore settings and save current brightness, colors and fx as default
|
||||
if (server.hasArg("IS"))
|
||||
{
|
||||
colS[0] = col[0];
|
||||
colS[1] = col[1];
|
||||
colS[2] = col[2];
|
||||
colSecS[0] = colSec[0];
|
||||
colSecS[1] = colSec[1];
|
||||
colSecS[2] = colSec[2];
|
||||
whiteS = white;
|
||||
whiteSecS = whiteSec;
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
colS[i] = col[i];
|
||||
colSecS[i] = colSec[i];
|
||||
}
|
||||
briS = bri;
|
||||
effectDefault = effectCurrent;
|
||||
effectSpeedDefault = effectSpeed;
|
||||
@@ -87,8 +84,8 @@ void handleSettingsSet(byte subPage)
|
||||
colSecS[0] = server.arg("SR").toInt();
|
||||
colSecS[1] = server.arg("SG").toInt();
|
||||
colSecS[2] = server.arg("SB").toInt();
|
||||
whiteS = server.arg("CW").toInt();
|
||||
whiteSecS = server.arg("SW").toInt();
|
||||
colS[3] = server.arg("CW").toInt();
|
||||
colSecS[3] = server.arg("SW").toInt();
|
||||
briS = server.arg("CA").toInt();
|
||||
effectDefault = server.arg("FX").toInt();
|
||||
effectSpeedDefault = server.arg("SX").toInt();
|
||||
@@ -116,7 +113,6 @@ void handleSettingsSet(byte subPage)
|
||||
|
||||
t = server.arg("PB").toInt();
|
||||
if (t >= 0 && t < 4) strip.paletteBlend = t;
|
||||
initLedsLast = server.hasArg("EI");
|
||||
reverseMode = server.hasArg("RV");
|
||||
strip.setReverseMode(reverseMode);
|
||||
skipFirstLed = server.hasArg("SL");
|
||||
@@ -273,6 +269,9 @@ void handleSettingsSet(byte subPage)
|
||||
|
||||
k[0] = 'T'; //macros
|
||||
timerMacro[i] = server.arg(k).toInt();
|
||||
|
||||
k[0] = 'W'; //weekdays
|
||||
timerWeekday[i] = server.arg(k).toInt();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,6 +308,7 @@ void handleSettingsSet(byte subPage)
|
||||
}
|
||||
saveSettingsToEEPROM();
|
||||
if (subPage == 2) strip.init(useRGBW,ledCount,skipFirstLed);
|
||||
if (subPage == 4) alexaInit();
|
||||
}
|
||||
|
||||
|
||||
@@ -382,7 +382,7 @@ bool handleSet(String req)
|
||||
//set white value
|
||||
pos = req.indexOf("&W=");
|
||||
if (pos > 0) {
|
||||
white = getNumVal(&req, pos);
|
||||
col[3] = getNumVal(&req, pos);
|
||||
}
|
||||
|
||||
//set 2nd red value
|
||||
@@ -403,24 +403,24 @@ bool handleSet(String req)
|
||||
//set 2nd white value
|
||||
pos = req.indexOf("W2=");
|
||||
if (pos > 0) {
|
||||
whiteSec = getNumVal(&req, pos);
|
||||
colSec[3] = getNumVal(&req, pos);
|
||||
}
|
||||
|
||||
//set color from HEX or 32bit DEC
|
||||
pos = req.indexOf("CL=");
|
||||
if (pos > 0) {
|
||||
colorFromDecOrHexString(col, &white, (char*)req.substring(pos + 3).c_str());
|
||||
colorFromDecOrHexString(col, (char*)req.substring(pos + 3).c_str());
|
||||
}
|
||||
pos = req.indexOf("C2=");
|
||||
if (pos > 0) {
|
||||
colorFromDecOrHexString(colSec, &whiteSec, (char*)req.substring(pos + 3).c_str());
|
||||
colorFromDecOrHexString(colSec, (char*)req.substring(pos + 3).c_str());
|
||||
}
|
||||
|
||||
//set 2nd to white
|
||||
pos = req.indexOf("SW");
|
||||
if (pos > 0) {
|
||||
if(useRGBW) {
|
||||
whiteSec = 255;
|
||||
colSec[3] = 255;
|
||||
colSec[0] = 0;
|
||||
colSec[1] = 0;
|
||||
colSec[2] = 0;
|
||||
@@ -434,7 +434,7 @@ bool handleSet(String req)
|
||||
//set 2nd to black
|
||||
pos = req.indexOf("SB");
|
||||
if (pos > 0) {
|
||||
whiteSec = 0;
|
||||
colSec[3] = 0;
|
||||
colSec[0] = 0;
|
||||
colSec[1] = 0;
|
||||
colSec[2] = 0;
|
||||
@@ -451,27 +451,25 @@ bool handleSet(String req)
|
||||
colSec[0] = col[0];
|
||||
colSec[1] = col[1];
|
||||
colSec[2] = col[2];
|
||||
whiteSec = white;
|
||||
colSec[3] = col[3];
|
||||
}
|
||||
//swap 2nd & 1st
|
||||
pos = req.indexOf("SC");
|
||||
if (pos > 0) {
|
||||
byte _temp[4];
|
||||
for (int i = 0; i<3; i++)
|
||||
byte temp;
|
||||
for (uint8_t i=0; i<4; i++)
|
||||
{
|
||||
_temp[i] = col[i];
|
||||
temp = col[i];
|
||||
col[i] = colSec[i];
|
||||
colSec[i] = _temp[i];
|
||||
colSec[i] = temp;
|
||||
}
|
||||
_temp[3] = white;
|
||||
white = whiteSec;
|
||||
whiteSec = _temp[3];
|
||||
}
|
||||
|
||||
//set current effect index
|
||||
pos = req.indexOf("FX=");
|
||||
if (pos > 0) {
|
||||
effectCurrent = getNumVal(&req, pos);
|
||||
presetCyclingEnabled = false;
|
||||
}
|
||||
//set effect speed
|
||||
pos = req.indexOf("SX=");
|
||||
|
||||
+19
-18
@@ -11,23 +11,23 @@ void wledInit()
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (ledCount > 600) ledCount = 600;
|
||||
#endif
|
||||
if (!EEPROM.read(397)) strip.init(EEPROM.read(372),ledCount,EEPROM.read(2204)); //quick init
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.setTimeout(50);
|
||||
|
||||
strip.init(EEPROM.read(372),ledCount,EEPROM.read(2204)); //init LEDs quickly
|
||||
|
||||
#ifdef USEFS
|
||||
SPIFFS.begin();
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTLN("Load EEPROM");
|
||||
loadSettingsFromEEPROM(true);
|
||||
if (!initLedsLast) initStrip();
|
||||
beginStrip();
|
||||
DEBUG_PRINT("CSSID: ");
|
||||
DEBUG_PRINT(clientSSID);
|
||||
userBeginPreConnection();
|
||||
if (strcmp(clientSSID,"Your_Network") == 0) showWelcomePage = true;
|
||||
|
||||
WiFi.persistent(false);
|
||||
initCon();
|
||||
|
||||
DEBUG_PRINTLN("");
|
||||
@@ -58,6 +58,11 @@ void wledInit()
|
||||
}
|
||||
|
||||
prepareIds(); //UUID from MAC (for Alexa and MQTT)
|
||||
if (strcmp(cmDNS,"x") == 0) //fill in unique mdns default
|
||||
{
|
||||
strcpy(cmDNS, "wled-");
|
||||
strcat(cmDNS, escapedMac.c_str());
|
||||
}
|
||||
if (mqttDeviceTopic[0] == 0)
|
||||
{
|
||||
strcpy(mqttDeviceTopic, "wled/");
|
||||
@@ -72,12 +77,12 @@ void wledInit()
|
||||
mqttInit = initMQTT();
|
||||
}
|
||||
|
||||
if (!initLedsLast) strip.service();
|
||||
strip.service();
|
||||
|
||||
//HTTP server page init
|
||||
initServer();
|
||||
|
||||
if (!initLedsLast) strip.service();
|
||||
strip.service();
|
||||
//init Alexa hue emulation
|
||||
if (alexaEnabled && !onlyAP) alexaInit();
|
||||
|
||||
@@ -100,7 +105,7 @@ void wledInit()
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!initLedsLast) strip.service();
|
||||
strip.service();
|
||||
// Set up mDNS responder:
|
||||
if (strlen(cmDNS) > 0 && !onlyAP)
|
||||
{
|
||||
@@ -108,8 +113,9 @@ void wledInit()
|
||||
DEBUG_PRINTLN("mDNS responder started");
|
||||
// Add service to MDNS
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
MDNS.addService("wled", "tcp", 80);
|
||||
}
|
||||
if (!initLedsLast) strip.service();
|
||||
strip.service();
|
||||
|
||||
initBlynk(blynkApiKey);
|
||||
initE131();
|
||||
@@ -119,7 +125,6 @@ void wledInit()
|
||||
e131Enabled = false;
|
||||
}
|
||||
|
||||
if (initLedsLast) initStrip();
|
||||
userBegin();
|
||||
|
||||
if (macroBoot>0) applyMacro(macroBoot);
|
||||
@@ -127,10 +132,9 @@ void wledInit()
|
||||
}
|
||||
|
||||
|
||||
void initStrip()
|
||||
void beginStrip()
|
||||
{
|
||||
// Initialize NeoPixel Strip and button
|
||||
if (initLedsLast) strip.init(useRGBW,ledCount,skipFirstLed);
|
||||
strip.setReverseMode(reverseMode);
|
||||
strip.setColor(0);
|
||||
strip.setBrightness(255);
|
||||
@@ -190,13 +194,10 @@ void initCon()
|
||||
while(!con)
|
||||
{
|
||||
yield();
|
||||
if (!initLedsLast)
|
||||
{
|
||||
handleTransitions();
|
||||
handleButton();
|
||||
handleOverlays();
|
||||
if (briT) strip.service();
|
||||
}
|
||||
handleTransitions();
|
||||
handleButton();
|
||||
handleOverlays();
|
||||
if (briT) strip.service();
|
||||
if (millis()-lastTry > 499) {
|
||||
con = (WiFi.status() == WL_CONNECTED);
|
||||
lastTry = millis();
|
||||
|
||||
@@ -16,9 +16,10 @@ void notify(byte callMode, bool followUp=false)
|
||||
case 2: if (!notifyButton) return; break;
|
||||
case 4: if (!notifyDirect) return; break;
|
||||
case 6: if (!notifyDirect) return; break; //fx change
|
||||
case 7: if (!notifyHue) return; break;
|
||||
case 7: if (!notifyHue) return; break;
|
||||
case 8: if (!notifyDirect) return; break;
|
||||
case 9: if (!notifyDirect) return; break;
|
||||
case 10: if (!notifyAlexa) return; break;
|
||||
default: return;
|
||||
}
|
||||
byte udpOut[WLEDPACKETSIZE];
|
||||
@@ -32,12 +33,12 @@ void notify(byte callMode, bool followUp=false)
|
||||
udpOut[7] = nightlightDelayMins;
|
||||
udpOut[8] = effectCurrent;
|
||||
udpOut[9] = effectSpeed;
|
||||
udpOut[10] = white;
|
||||
udpOut[10] = col[3];
|
||||
udpOut[11] = 5; //compatibilityVersionByte: 0: old 1: supports white 2: supports secondary color 3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette
|
||||
udpOut[12] = colSec[0];
|
||||
udpOut[13] = colSec[1];
|
||||
udpOut[14] = colSec[2];
|
||||
udpOut[15] = whiteSec;
|
||||
udpOut[15] = colSec[3];
|
||||
udpOut[16] = effectIntensity;
|
||||
udpOut[17] = (transitionDelay >> 0) & 0xFF;
|
||||
udpOut[18] = (transitionDelay >> 8) & 0xFF;
|
||||
@@ -167,13 +168,13 @@ void handleNotifications()
|
||||
col[2] = udpIn[5];
|
||||
if (udpIn[11] > 0) //check if sending modules white val is inteded
|
||||
{
|
||||
white = udpIn[10];
|
||||
col[3] = udpIn[10];
|
||||
if (udpIn[11] > 1)
|
||||
{
|
||||
colSec[0] = udpIn[12];
|
||||
colSec[1] = udpIn[13];
|
||||
colSec[2] = udpIn[14];
|
||||
whiteSec = udpIn[15];
|
||||
colSec[3] = udpIn[15];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-46
@@ -34,51 +34,46 @@ void setAllLeds() {
|
||||
{
|
||||
colSecT[i] = colSec[i];
|
||||
}
|
||||
whiteSecT = whiteSec;
|
||||
colSecT[3] = colSec[3];
|
||||
}
|
||||
if (autoRGBtoRGBW)
|
||||
if (useRGBW && autoRGBtoRGBW)
|
||||
{
|
||||
colorRGBtoRGBW(colT,&whiteT);
|
||||
colorRGBtoRGBW(colSecT,&whiteSecT);
|
||||
colorRGBtoRGBW(colT);
|
||||
colorRGBtoRGBW(colSecT);
|
||||
}
|
||||
if (useGammaCorrectionRGB)
|
||||
{
|
||||
strip.setColor(gamma8[colT[0]], gamma8[colT[1]], gamma8[colT[2]], gamma8[whiteT]);
|
||||
strip.setSecondaryColor(gamma8[colSecT[0]], gamma8[colSecT[1]], gamma8[colSecT[2]], gamma8[whiteSecT]);
|
||||
strip.setColor(gamma8[colT[0]], gamma8[colT[1]], gamma8[colT[2]], gamma8[colT[3]]);
|
||||
strip.setSecondaryColor(gamma8[colSecT[0]], gamma8[colSecT[1]], gamma8[colSecT[2]], gamma8[colSecT[3]]);
|
||||
} else {
|
||||
strip.setColor(colT[0], colT[1], colT[2], whiteT);
|
||||
strip.setSecondaryColor(colSecT[0], colSecT[1], colSecT[2], whiteSecT);
|
||||
strip.setColor(colT[0], colT[1], colT[2], colT[3]);
|
||||
strip.setSecondaryColor(colSecT[0], colSecT[1], colSecT[2], colSecT[3]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setLedsStandard()
|
||||
{
|
||||
for (byte i = 0; i<3; i++)
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
colOld[i] = col[i];
|
||||
colT[i] = col[i];
|
||||
colSecOld[i] = colSec[i];
|
||||
colSecT[i] = colSec[i];
|
||||
}
|
||||
whiteOld = white;
|
||||
briOld = bri;
|
||||
whiteSecOld = whiteSec;
|
||||
whiteT = white;
|
||||
briT = bri;
|
||||
whiteSecT = whiteSec;
|
||||
setAllLeds();
|
||||
}
|
||||
|
||||
|
||||
bool colorChanged()
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
if (col[i] != colIT[i]) return true;
|
||||
if (colSec[i] != colSecIT[i]) return true;
|
||||
}
|
||||
if (white != whiteIT || whiteSec != whiteSecIT) return true;
|
||||
if (bri != briIT) return true;
|
||||
return false;
|
||||
}
|
||||
@@ -87,7 +82,7 @@ bool colorChanged()
|
||||
void colorUpdated(int callMode)
|
||||
{
|
||||
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
|
||||
// 6: fx changed 7: hue 8: preset cycle 9: blynk
|
||||
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
|
||||
bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette);
|
||||
if (!colorChanged())
|
||||
{
|
||||
@@ -104,14 +99,11 @@ void colorUpdated(int callMode)
|
||||
nightlightDelayMs -= (millis() - nightlightStartTime);
|
||||
nightlightStartTime = millis();
|
||||
}
|
||||
colIT[0] = col[0];
|
||||
colIT[1] = col[1];
|
||||
colIT[2] = col[2];
|
||||
colSecIT[0] = colSec[0];
|
||||
colSecIT[1] = colSec[1];
|
||||
colSecIT[2] = colSec[2];
|
||||
whiteIT = white;
|
||||
whiteSecIT = whiteSec;
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
colIT[i] = col[i];
|
||||
colSecIT[i] = colSec[i];
|
||||
}
|
||||
briIT = bri;
|
||||
if (bri > 0) briLast = bri;
|
||||
|
||||
@@ -125,14 +117,11 @@ void colorUpdated(int callMode)
|
||||
|
||||
if (transitionActive)
|
||||
{
|
||||
colOld[0] = colT[0];
|
||||
colOld[1] = colT[1];
|
||||
colOld[2] = colT[2];
|
||||
whiteOld = whiteT;
|
||||
colSecOld[0] = colSecT[0];
|
||||
colSecOld[1] = colSecT[1];
|
||||
colSecOld[2] = colSecT[2];
|
||||
whiteSecOld = whiteSecT;
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
colOld[i] = colT[i];
|
||||
colSecOld[i] = colSecT[i];
|
||||
}
|
||||
briOld = briT;
|
||||
tperLast = 0;
|
||||
}
|
||||
@@ -146,6 +135,9 @@ void colorUpdated(int callMode)
|
||||
}
|
||||
|
||||
if (callMode == 8) return;
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
if (espalexaDevice != nullptr) espalexaDevice->setValue(bri);
|
||||
#endif
|
||||
//only update Blynk and mqtt every 2 seconds to reduce lag
|
||||
if (millis() - lastInterfaceUpdate <= 2000)
|
||||
{
|
||||
@@ -184,23 +176,16 @@ void handleTransitions()
|
||||
setLedsStandard();
|
||||
return;
|
||||
}
|
||||
if (tper - tperLast < 0.004)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (tper - tperLast < 0.004) return;
|
||||
tperLast = tper;
|
||||
if (fadeTransition)
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
for (byte i = 0; i<3; i++)
|
||||
{
|
||||
colT[i] = colOld[i]+((col[i] - colOld[i])*tper);
|
||||
colSecT[i] = colSecOld[i]+((colSec[i] - colSecOld[i])*tper);
|
||||
}
|
||||
whiteT = whiteOld +((white - whiteOld )*tper);
|
||||
whiteSecT = whiteSecOld +((whiteSec - whiteSecOld )*tper);
|
||||
briT = briOld +((bri - briOld )*tper);
|
||||
colT[i] = colOld[i]+((col[i] - colOld[i])*tper);
|
||||
colSecT[i] = colSecOld[i]+((colSec[i] - colSecOld[i])*tper);
|
||||
}
|
||||
if (fadeTransition) setAllLeds();
|
||||
briT = briOld +((bri - briOld )*tper);
|
||||
|
||||
setAllLeds();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,6 +166,13 @@ bool checkCountdown()
|
||||
return false;
|
||||
}
|
||||
|
||||
byte weekdayMondayFirst()
|
||||
{
|
||||
byte wd = weekday(local) -1;
|
||||
if (wd == 0) wd = 7;
|
||||
return wd;
|
||||
}
|
||||
|
||||
void checkTimers()
|
||||
{
|
||||
if (lastTimerMinute != minute(local)) //only check once a new minute begins
|
||||
@@ -176,11 +183,11 @@ void checkTimers()
|
||||
if (timerMacro[i] != 0
|
||||
&& (timerHours[i] == hour(local) || timerHours[i] == 24) //if hour is set to 24, activate every hour
|
||||
&& timerMinutes[i] == minute(local)
|
||||
&& timerWeekday[i] >> weekday(local) & 0x01) //timer should activate at current day of week
|
||||
&& (timerWeekday[i] & 0x01) //timer is enabled
|
||||
&& timerWeekday[i] >> weekdayMondayFirst() & 0x01) //timer should activate at current day of week
|
||||
{
|
||||
applyMacro(timerMacro[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ void _nixieDisplay(int num[], uint16_t dur[], uint16_t pausedur[], byte cnt)
|
||||
strip.setRange(overlayMin, overlayMax, 0);
|
||||
if (num[nixieClockI] >= 0 && !nixiePause)
|
||||
{
|
||||
strip.setIndividual(num[nixieClockI],((uint32_t)white << 24)| ((uint32_t)colT[0] << 16) | ((uint32_t)colT[1] << 8) | colT[2]);
|
||||
strip.setIndividual(num[nixieClockI],((uint32_t)colT[3] << 24)| ((uint32_t)colT[0] << 16) | ((uint32_t)colT[1] << 8) | colT[2]);
|
||||
strip.unlock(num[nixieClockI]);
|
||||
}
|
||||
if (!nixiePause)
|
||||
@@ -310,11 +310,11 @@ void _overlayAnalogCountdown()
|
||||
byte pixelCnt = perc*overlaySize;
|
||||
if (analogClock12pixel + pixelCnt > overlayMax)
|
||||
{
|
||||
strip.setRange(analogClock12pixel, overlayMax, ((uint32_t)whiteSec << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]);
|
||||
strip.setRange(overlayMin, overlayMin +pixelCnt -(1+ overlayMax -analogClock12pixel), ((uint32_t)whiteSec << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]);
|
||||
strip.setRange(analogClock12pixel, overlayMax, ((uint32_t)colSec[3] << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]);
|
||||
strip.setRange(overlayMin, overlayMin +pixelCnt -(1+ overlayMax -analogClock12pixel), ((uint32_t)colSec[3] << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]);
|
||||
} else
|
||||
{
|
||||
strip.setRange(analogClock12pixel, analogClock12pixel + pixelCnt, ((uint32_t)whiteSec << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]);
|
||||
strip.setRange(analogClock12pixel, analogClock12pixel + pixelCnt, ((uint32_t)colSec[3] << 24)| ((uint32_t)colSec[0] << 16) | ((uint32_t)colSec[1] << 8) | colSec[2]);
|
||||
}
|
||||
}
|
||||
overlayRefreshMs = 998;
|
||||
|
||||
+43
-254
@@ -12,280 +12,69 @@ void prepareIds() {
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
void onAlexaChange(byte b, uint32_t color);
|
||||
|
||||
void alexaInit()
|
||||
{
|
||||
if (alexaEnabled && WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
alexaUdpConnected = connectUDP();
|
||||
|
||||
if (alexaUdpConnected) alexaInitPages();
|
||||
if (espalexaDevice == nullptr) //only init once
|
||||
{
|
||||
espalexaDevice = new EspalexaDevice(alexaInvocationName, onAlexaChange);
|
||||
espalexa.addDevice(espalexaDevice);
|
||||
espalexa.begin(&server);
|
||||
} else {
|
||||
espalexaDevice->setName(alexaInvocationName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleAlexa()
|
||||
{
|
||||
if (!alexaEnabled || WiFi.status() != WL_CONNECTED || !alexaUdpConnected) return;
|
||||
|
||||
// if there's data available, read a packet
|
||||
int packetSize = alexaUDP.parsePacket();
|
||||
if(packetSize < 1) return;
|
||||
|
||||
IPAddress remote = alexaUDP.remoteIP();
|
||||
int len = alexaUDP.read(obuf, 254);
|
||||
if (len > 0) obuf[len] = 0;
|
||||
|
||||
if(strstr(obuf,"M-SEARCH") > 0) {
|
||||
if(strstr(obuf,"upnp:rootdevice") > 0 || strstr(obuf,"device:basic:1") > 0) {
|
||||
DEBUG_PRINTLN("Responding search req...");
|
||||
respondToSearch();
|
||||
}
|
||||
}
|
||||
if (!alexaEnabled || WiFi.status() != WL_CONNECTED) return;
|
||||
espalexa.loop();
|
||||
}
|
||||
|
||||
void alexaOn()
|
||||
void onAlexaChange(byte b, uint32_t color)
|
||||
{
|
||||
if (macroAlexaOn == 0)
|
||||
{
|
||||
handleSet((notifyAlexa)?"win&T=1&IN":"win&T=1&NN&IN");
|
||||
} else
|
||||
{
|
||||
applyMacro(macroAlexaOn);
|
||||
}
|
||||
|
||||
server.send(200, "application/json", "[{\"success\":{\"/lights/1/state/on\":true}}]");
|
||||
}
|
||||
|
||||
void alexaOff()
|
||||
{
|
||||
if (macroAlexaOff == 0)
|
||||
{
|
||||
handleSet((notifyAlexa)?"win&T=0&IN":"win&T=0&NN&IN");
|
||||
} else
|
||||
{
|
||||
applyMacro(macroAlexaOff);
|
||||
}
|
||||
|
||||
server.send(200, "application/json", "[{\"success\":{\"/lights/1/state/on\":false}}]");
|
||||
}
|
||||
|
||||
void alexaDim(byte briL)
|
||||
{
|
||||
olen = 0;
|
||||
oappend("[{\"success\":{\"/lights/1/state/bri\":");
|
||||
oappendi(briL);
|
||||
oappend("}}]");
|
||||
|
||||
server.send(200, "application/json", obuf);
|
||||
byte m = espalexaDevice->getLastChangedProperty();
|
||||
|
||||
String ct = (notifyAlexa)?"win&IN&A=":"win&NN&IN&A=";
|
||||
if (briL < 255)
|
||||
if (m == 1){ //ON
|
||||
if (!macroAlexaOn)
|
||||
{
|
||||
if (bri == 0)
|
||||
{
|
||||
bri = briLast;
|
||||
colorUpdated(10);
|
||||
}
|
||||
} else applyMacro(macroAlexaOn);
|
||||
} else if (m == 2) //OFF
|
||||
{
|
||||
ct = ct + (briL+1);
|
||||
} else
|
||||
if (!macroAlexaOff)
|
||||
{
|
||||
if (bri > 0)
|
||||
{
|
||||
briLast = bri;
|
||||
bri = 0;
|
||||
colorUpdated(10);
|
||||
}
|
||||
} else applyMacro(macroAlexaOff);
|
||||
} else if (m == 3) //brightness
|
||||
{
|
||||
ct = ct + (255);
|
||||
bri = b;
|
||||
colorUpdated(10);
|
||||
} else //color
|
||||
{
|
||||
col[0] = ((color >> 16) & 0xFF);
|
||||
col[1] = ((color >> 8) & 0xFF);
|
||||
col[2] = (color & 0xFF);
|
||||
if (useRGBW) colorRGBtoRGBW(col);
|
||||
colorUpdated(10);
|
||||
}
|
||||
handleSet(ct);
|
||||
}
|
||||
|
||||
void respondToSearch() {
|
||||
DEBUG_PRINTLN("");
|
||||
DEBUG_PRINT("Send resp to ");
|
||||
DEBUG_PRINTLN(alexaUDP.remoteIP());
|
||||
DEBUG_PRINT("Port : ");
|
||||
DEBUG_PRINTLN(alexaUDP.remotePort());
|
||||
|
||||
IPAddress localIP = WiFi.localIP();
|
||||
char s[16];
|
||||
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
|
||||
|
||||
olen = 0;
|
||||
oappend(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"EXT:\r\n"
|
||||
"CACHE-CONTROL: max-age=100\r\n" // SSDP_INTERVAL
|
||||
"LOCATION: http://");
|
||||
oappend(s);
|
||||
oappend(":80/description.xml\r\n"
|
||||
"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.17.0\r\n" // _modelName, _modelNumber
|
||||
"hue-bridgeid: ");
|
||||
oappend((char*)escapedMac.c_str());
|
||||
oappend("\r\n"
|
||||
"ST: urn:schemas-upnp-org:device:basic:1\r\n" // _deviceType
|
||||
"USN: uuid:2f402f80-da50-11e1-9b23-");
|
||||
oappend((char*)escapedMac.c_str());
|
||||
oappend("::upnp:rootdevice\r\n" // _uuid::_deviceType
|
||||
"\r\n");
|
||||
|
||||
alexaUDP.beginPacket(alexaUDP.remoteIP(), alexaUDP.remotePort());
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
alexaUDP.write((byte*)obuf, olen);
|
||||
#else
|
||||
alexaUDP.write(obuf);
|
||||
#endif
|
||||
alexaUDP.endPacket();
|
||||
|
||||
DEBUG_PRINTLN("Response sent!");
|
||||
}
|
||||
|
||||
void alexaInitPages() {
|
||||
|
||||
server.on("/description.xml", HTTP_GET, [](){
|
||||
DEBUG_PRINTLN(" # Responding to description.xml ... #\n");
|
||||
|
||||
IPAddress localIP = WiFi.localIP();
|
||||
char s[16];
|
||||
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]);
|
||||
|
||||
olen = 0;
|
||||
oappend("<?xml version=\"1.0\" ?>"
|
||||
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
|
||||
"<specVersion><major>1</major><minor>0</minor></specVersion>"
|
||||
"<URLBase>http://");
|
||||
oappend(s);
|
||||
oappend(":80/</URLBase>"
|
||||
"<device>"
|
||||
"<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>"
|
||||
"<friendlyName>Philips hue (");
|
||||
oappend(s);
|
||||
oappend(")</friendlyName>"
|
||||
"<manufacturer>Royal Philips Electronics</manufacturer>"
|
||||
"<manufacturerURL>http://www.philips.com</manufacturerURL>"
|
||||
"<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>"
|
||||
"<modelName>Philips hue bridge 2012</modelName>"
|
||||
"<modelNumber>929000226503</modelNumber>"
|
||||
"<modelURL>http://www.meethue.com</modelURL>"
|
||||
"<serialNumber>");
|
||||
oappend((char*)escapedMac.c_str());
|
||||
oappend("</serialNumber>"
|
||||
"<UDN>uuid:2f402f80-da50-11e1-9b23-");
|
||||
oappend((char*)escapedMac.c_str());
|
||||
oappend("</UDN>"
|
||||
"<presentationURL>index.html</presentationURL>"
|
||||
"<iconList>"
|
||||
" <icon>"
|
||||
" <mimetype>image/png</mimetype>"
|
||||
" <height>48</height>"
|
||||
" <width>48</width>"
|
||||
" <depth>24</depth>"
|
||||
" <url>hue_logo_0.png</url>"
|
||||
" </icon>"
|
||||
" <icon>"
|
||||
" <mimetype>image/png</mimetype>"
|
||||
" <height>120</height>"
|
||||
" <width>120</width>"
|
||||
" <depth>24</depth>"
|
||||
" <url>hue_logo_3.png</url>"
|
||||
" </icon>"
|
||||
"</iconList>"
|
||||
"</device>"
|
||||
"</root>");
|
||||
|
||||
server.send(200, "text/xml", obuf);
|
||||
|
||||
DEBUG_PRINTLN("Sending setup_xml");
|
||||
});
|
||||
|
||||
// openHAB support
|
||||
server.on("/on.html", HTTP_GET, [](){
|
||||
DEBUG_PRINTLN("on req");
|
||||
server.send(200, "text/plain", "turned on");
|
||||
alexaOn();
|
||||
});
|
||||
|
||||
server.on("/off.html", HTTP_GET, [](){
|
||||
DEBUG_PRINTLN("off req");
|
||||
server.send(200, "text/plain", "turned off");
|
||||
alexaOff();
|
||||
});
|
||||
|
||||
server.on("/status.html", HTTP_GET, [](){
|
||||
DEBUG_PRINTLN("Got status request");
|
||||
|
||||
char statrespone[] = "0";
|
||||
if (bri > 0) {
|
||||
statrespone[0] = '1';
|
||||
}
|
||||
server.send(200, "text/plain", statrespone);
|
||||
});
|
||||
}
|
||||
|
||||
String boolString(bool st)
|
||||
{
|
||||
return (st)?"true":"false";
|
||||
}
|
||||
|
||||
String briForHue(int realBri)
|
||||
{
|
||||
realBri--;
|
||||
if (realBri < 0) realBri = 0;
|
||||
return String(realBri);
|
||||
}
|
||||
|
||||
bool handleAlexaApiCall(String req, String body) //basic implementation of Philips hue api functions needed for basic Alexa control
|
||||
{
|
||||
DEBUG_PRINTLN("AlexaApiCall");
|
||||
if (req.indexOf("api") <0) return false;
|
||||
DEBUG_PRINTLN("ok");
|
||||
if (body.indexOf("devicetype") > 0) //client wants a hue api username, we dont care and give static
|
||||
{
|
||||
DEBUG_PRINTLN("devType");
|
||||
server.send(200, "application/json", "[{\"success\":{\"username\": \"2WLEDHardQrI3WHYTHoMcXHgEspsM8ZZRpSKtBQr\"}}]");
|
||||
return true;
|
||||
}
|
||||
if (req.indexOf("state") > 0) //client wants to control light
|
||||
{
|
||||
DEBUG_PRINTLN("ls");
|
||||
if (body.indexOf("bri")>0) {alexaDim(body.substring(body.indexOf("bri") +5).toInt()); return true;}
|
||||
if (body.indexOf("false")>0) {alexaOff(); return true;}
|
||||
alexaOn();
|
||||
|
||||
return true;
|
||||
}
|
||||
if (req.indexOf("lights/1") > 0) //client wants light info
|
||||
{
|
||||
DEBUG_PRINTLN("l1");
|
||||
server.send(200, "application/json", "{\"manufacturername\":\"OpenSource\",\"modelid\":\"LST001\",\"name\":\""+ String(alexaInvocationName) +"\",\"state\":{\"on\":"+ boolString(bri) +",\"hue\":0,\"bri\":"+ briForHue(bri) +",\"sat\":0,\"xy\":[0.00000,0.00000],\"ct\":500,\"alert\":\"none\",\"effect\":\"none\",\"colormode\":\"hs\",\"reachable\":true},\"swversion\":\"0.1\",\"type\":\"Extended color light\",\"uniqueid\":\"2\"}");
|
||||
|
||||
return true;
|
||||
}
|
||||
if (req.indexOf("lights") > 0) //client wants all lights
|
||||
{
|
||||
DEBUG_PRINTLN("lAll");
|
||||
server.send(200, "application/json", "{\"1\":{\"type\":\"Extended color light\",\"manufacturername\":\"OpenSource\",\"swversion\":\"0.1\",\"name\":\""+ String(alexaInvocationName) +"\",\"uniqueid\":\""+ WiFi.macAddress() +"-2\",\"modelid\":\"LST001\",\"state\":{\"on\":"+ boolString(bri) +",\"bri\":"+ briForHue(bri) +",\"xy\":[0.00000,0.00000],\"colormode\":\"hs\",\"effect\":\"none\",\"ct\":500,\"hue\":0,\"sat\":0,\"alert\":\"none\",\"reachable\":true}}}");
|
||||
return true;
|
||||
}
|
||||
|
||||
//we dont care about other api commands at this time and send empty JSON
|
||||
server.send(200, "application/json", "{}");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool connectUDP(){
|
||||
bool state = false;
|
||||
|
||||
DEBUG_PRINTLN("");
|
||||
DEBUG_PRINTLN("Con UDP");
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if(alexaUDP.beginMulticast(ipMulti, portMulti))
|
||||
#else
|
||||
if(alexaUDP.beginMulticast(WiFi.localIP(), ipMulti, portMulti))
|
||||
#endif
|
||||
{
|
||||
DEBUG_PRINTLN("Con success");
|
||||
state = true;
|
||||
}
|
||||
else{
|
||||
DEBUG_PRINTLN("Con failed");
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
#else
|
||||
void alexaInit(){}
|
||||
void handleAlexa(){}
|
||||
void alexaInitPages(){}
|
||||
bool handleAlexaApiCall(String req, String body){return false;}
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
void colorFromUint32(uint32_t in)
|
||||
{
|
||||
white = in >> 24 & 0xFF;
|
||||
col[3] = in >> 24 & 0xFF;
|
||||
col[0] = in >> 16 & 0xFF;
|
||||
col[1] = in >> 8 & 0xFF;
|
||||
col[2] = in & 0xFF;
|
||||
@@ -118,7 +118,7 @@ void colorRGBtoXY(byte* rgb, float* xy) //rgb to coordinates (https://www.develo
|
||||
}
|
||||
#endif
|
||||
|
||||
void colorFromDecOrHexString(byte* rgb, byte* wht, char* in)
|
||||
void colorFromDecOrHexString(byte* rgb, char* in)
|
||||
{
|
||||
if (in[0] == 0) return;
|
||||
char first = in[0];
|
||||
@@ -132,7 +132,7 @@ void colorFromDecOrHexString(byte* rgb, byte* wht, char* in)
|
||||
c = strtoul(in, NULL, 10);
|
||||
}
|
||||
|
||||
*wht = (c >> 24) & 0xFF;
|
||||
rgb[3] = (c >> 24) & 0xFF;
|
||||
rgb[0] = (c >> 16) & 0xFF;
|
||||
rgb[1] = (c >> 8) & 0xFF;
|
||||
rgb[2] = c & 0xFF;
|
||||
@@ -150,11 +150,11 @@ float maxf (float v, float w)
|
||||
return v;
|
||||
}
|
||||
|
||||
void colorRGBtoRGBW(byte* rgb, byte* wht) //rgb to rgbw (http://codewelt.com/rgbw)
|
||||
void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw)
|
||||
{
|
||||
float low = minf(rgb[0],minf(rgb[1],rgb[2]));
|
||||
float high = maxf(rgb[0],maxf(rgb[1],rgb[2]));
|
||||
if (high < 0.1f) return;
|
||||
float sat = 255.0f * ((high - low) / high);
|
||||
*wht = (byte)((255.0f - sat) / 255.0f * (rgb[0] + rgb[1] + rgb[2]) / 3);
|
||||
rgb[3] = (byte)((255.0f - sat) / 255.0f * (rgb[0] + rgb[1] + rgb[2]) / 3);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* MQTT communication protocol for home automation
|
||||
*/
|
||||
|
||||
#define WLED_MQTT_PORT 1883
|
||||
|
||||
void parseMQTTBriPayload(char* payload)
|
||||
{
|
||||
if (strcmp(payload, "ON") == 0) {bri = briLast; colorUpdated(1);}
|
||||
@@ -25,7 +27,7 @@ void callbackMQTT(char* topic, byte* payload, unsigned int length) {
|
||||
|
||||
if (strstr(topic, "/col"))
|
||||
{
|
||||
colorFromDecOrHexString(col, &white, (char*)payload);
|
||||
colorFromDecOrHexString(col, (char*)payload);
|
||||
colorUpdated(1);
|
||||
} else if (strstr(topic, "/api"))
|
||||
{
|
||||
@@ -53,7 +55,7 @@ void publishMQTT()
|
||||
strcat(subuf, "/g");
|
||||
mqtt->publish(subuf, s);
|
||||
|
||||
sprintf(s, "#%X", white*16777216 + col[0]*65536 + col[1]*256 + col[2]);
|
||||
sprintf(s, "#%X", col[3]*16777216 + col[0]*65536 + col[1]*256 + col[2]);
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat(subuf, "/c");
|
||||
mqtt->publish(subuf, s);
|
||||
@@ -111,9 +113,9 @@ bool initMQTT()
|
||||
IPAddress mqttIP;
|
||||
if (mqttIP.fromString(mqttServer)) //see if server is IP or domain
|
||||
{
|
||||
mqtt->setServer(mqttIP,1883);
|
||||
mqtt->setServer(mqttIP, WLED_MQTT_PORT);
|
||||
} else {
|
||||
mqtt->setServer(mqttServer,1883);
|
||||
mqtt->setServer(mqttServer, WLED_MQTT_PORT);
|
||||
}
|
||||
mqtt->setCallback(callbackMQTT);
|
||||
DEBUG_PRINTLN("MQTT ready.");
|
||||
|
||||
@@ -87,6 +87,22 @@ void initServer()
|
||||
serveMessage(200,"Security settings saved.","Rebooting now... (takes ~20 seconds, wait for auto-redirect)",139);
|
||||
reset();
|
||||
});
|
||||
|
||||
server.on("/json", HTTP_ANY, [](){
|
||||
server.send(500, "application/json", "{\"error\":\"Not implemented\"}");
|
||||
});
|
||||
|
||||
server.on("/json/effects", HTTP_GET, [](){
|
||||
server.setContentLength(strlen_P(JSON_mode_names));
|
||||
server.send(200, "application/json", "");
|
||||
server.sendContent_P(JSON_mode_names);
|
||||
});
|
||||
|
||||
server.on("/json/palettes", HTTP_GET, [](){
|
||||
server.setContentLength(strlen_P(JSON_palette_names));
|
||||
server.send(200, "application/json", "");
|
||||
server.sendContent_P(JSON_palette_names);
|
||||
});
|
||||
|
||||
server.on("/version", HTTP_GET, [](){
|
||||
server.send(200, "text/plain", (String)VERSION);
|
||||
@@ -170,6 +186,17 @@ void initServer()
|
||||
DEBUG_PRINTLN("URI: " + server.uri());
|
||||
DEBUG_PRINTLN("Body: " + server.arg(0));
|
||||
|
||||
//make API CORS compatible
|
||||
if (server.method() == HTTP_OPTIONS)
|
||||
{
|
||||
server.sendHeader("Access-Control-Allow-Origin", "*");
|
||||
server.sendHeader("Access-Control-Max-Age", "10000");
|
||||
server.sendHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS");
|
||||
server.sendHeader("Access-Control-Allow-Headers", "*");
|
||||
server.send(200);
|
||||
return;
|
||||
}
|
||||
|
||||
//workaround for subpage issue
|
||||
if (server.uri().length() == 1)
|
||||
{
|
||||
@@ -178,7 +205,9 @@ void initServer()
|
||||
}
|
||||
|
||||
if(!handleSet(server.uri())){
|
||||
if(!handleAlexaApiCall(server.uri(),server.arg(0)))
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
if(!espalexa.handleAlexaApiCall(server.uri(),server.arg(0)))
|
||||
#endif
|
||||
server.send(404, "text/plain", "Not Found");
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user