diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..8b1d22d2 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: [Aircoookie] +custom: ['https://paypal.me/Aircoookie'] diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c04cdae..2c8111ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,36 @@ ### Builds after release 0.12.0 +#### Build 2106140 + +- Updated main logo +- Reduced flash usage by 0.8kB by using 8-bit instead of 32-bit PNGs for welcome and 404 pages +- Added a check to stop Alexa reporting an error if state set by macro differs from the expected state + +#### Build 2106100 + +- Added support for multiple buttons with various types (PR #1977) +- Fixed infinite playlists (PR #2020) +- Added `r` to playlist object, allows for shuffle regardless of the `repeat` value +- Improved accuracy of NTP time sync +- Added possibility for WLED UDP sync to sync system time +- Improved UDP sync accuracy, if both sender and receiver are NTP synced +- Fixed a cache issue with restored tabs +- Cache CORS request +- Disable WiFi sleep by default on ESP32 + +#### Build 2105230 + +- No longer retain MQTT `/v` topic to alleviate storage loads on MQTT broker +- Fixed Sunrise calculation (atan_t approx. used outside of value range) + +#### Build 2105200 + +- Fixed WS281x output on ESP32 +- Fixed potential out-of-bounds write in MQTT +- Fixed IR pin not changeable if IR disabled +- Fixed XML API containing -1 on Manual only RGBW mode (see #888, #1783) + #### Build 2105171 - Always copy MQTT payloads to prevent non-0-terminated strings diff --git a/images/wled_logo_akemi.png b/images/wled_logo_akemi.png index 4f52d370..87a2d485 100644 Binary files a/images/wled_logo_akemi.png and b/images/wled_logo_akemi.png differ diff --git a/images/wled_logo_clean.png b/images/wled_logo_clean.png deleted file mode 100644 index 26a0ebed..00000000 Binary files a/images/wled_logo_clean.png and /dev/null differ diff --git a/images/wled_logo.png b/images/wled_logo_old.png similarity index 100% rename from images/wled_logo.png rename to images/wled_logo_old.png diff --git a/package-lock.json b/package-lock.json index 6b18d652..2f3a4c29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.12.0-b3", + "version": "0.12.2-bl4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 8a99e02c..25b17fcf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.12.2-bl3", + "version": "0.12.2-bl4", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { diff --git a/platformio.ini b/platformio.ini index e087fe3f..e3f94953 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,6 +15,7 @@ default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth # Single binaries (uncomment your board) +; default_envs = elekstube_ips ; default_envs = nodemcuv2 ; default_envs = esp01_1m_full ; default_envs = esp07 @@ -47,7 +48,8 @@ extra_configs = # arduino core 2.7.0 = platformIO 2.5.0 # ------------------------------------------------------------------------------ arduino_core_2_6_3 = espressif8266@2.3.3 -arduino_core_2_7_4 = espressif8266@^2.6.2 +arduino_core_2_7_4 = espressif8266@2.6.2 +arduino_core_3_0_0 = espressif8266@3.0.0 # Development platforms arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop @@ -131,6 +133,8 @@ ldscript_4m1m = eagle.flash.4m1m.ld build_flags = -DESP8266 -DFP_IN_IROM + ;-Wno-deprecated-declarations + ;-Wno-register ; NONOSDK22x_190703 = 2.2.2-dev(38a443e) -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 ; lwIP 2 - Higher Bandwidth no Features @@ -418,3 +422,37 @@ platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_4m1m} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp8266} + +# ------------------------------------------------------------------------------ +# EleksTube-IPS +# ------------------------------------------------------------------------------ +[env:elekstube_ips] +board = esp32dev +platform = espressif32@3.2 +upload_speed = 921600 +lib_deps = ${env.lib_deps} + TFT_eSPI +build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED + -D USERMOD_RTC + -D USERMOD_ELEKSTUBE_IPS + -D LEDPIN=12 + -D RLYPIN=27 + -D BTNPIN=34 + -D WLED_DISABLE_INFRARED + -D DEFAULT_LED_COUNT=6 + # Display config + -D ST7789_DRIVER + -D TFT_WIDTH=135 + -D TFT_HEIGHT=240 + -D CGRAM_OFFSET + -D TFT_SDA_READ + -D TFT_MOSI=23 + -D TFT_SCLK=18 + -D TFT_DC=25 + -D TFT_RST=26 + -D SPI_FREQUENCY=40000000 + -D USER_SETUP_LOADED +monitor_filters = esp32_exception_decoder +lib_ignore = + ESPAsyncTCP + ESPAsyncUDP diff --git a/readme.md b/readme.md index 4aa96888..9cee21f3 100644 --- a/readme.md +++ b/readme.md @@ -37,6 +37,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control - MQTT - Blynk IoT - E1.31, Art-Net, DDP and TPM2.net +- [diyHue](https://github.com/diyhue/diyHue) (Wled is supported by diyHue, including Hue Sync Entertainment under udp. Thanks to [Gregory Mallios](https://github.com/gmallios)) - [Hyperion](https://github.com/hyperion-project/hyperion.ng) - UDP realtime - Alexa voice control (including dimming and color) diff --git a/requirements.txt b/requirements.txt index 6fc16f0f..c3ed11fc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -44,7 +44,7 @@ starlette==0.14.2 # via platformio tabulate==0.8.9 # via platformio -urllib3==1.26.4 +urllib3==1.26.5 # via requests uvicorn==0.13.4 # via platformio diff --git a/tools/WLED_ESP32_4MB_1MB_FS.csv b/tools/WLED_ESP32_4MB_1MB_FS.csv new file mode 100644 index 00000000..09455c6a --- /dev/null +++ b/tools/WLED_ESP32_4MB_1MB_FS.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x17B000, +app1, app, ota_1, 0x18B000,0x17B000, +spiffs, data, spiffs, 0x306000,0x0FA000, \ No newline at end of file diff --git a/usermods/EleksTube_IPS/ChipSelect.h b/usermods/EleksTube_IPS/ChipSelect.h new file mode 100644 index 00000000..c58b2854 --- /dev/null +++ b/usermods/EleksTube_IPS/ChipSelect.h @@ -0,0 +1,70 @@ +#ifndef CHIP_SELECT_H +#define CHIP_SELECT_H + +#include "Hardware.h" + +/* + * `digit`s are as defined in Hardware.h, 0 == seconds ones, 5 == hours tens. + */ + +class ChipSelect { +private: + uint8_t digits_map; + const uint8_t all_on = 0x3F; + const uint8_t all_off = 0x00; +public: + ChipSelect() : digits_map(all_off) {} + + void update() { + // Documented in README.md. Q7 and Q6 are unused. Q5 is Seconds Ones, Q0 is Hours Tens. + // Q7 is the first bit written, Q0 is the last. So we push two dummy bits, then start with + // Seconds Ones and end with Hours Tens. + // CS is Active Low, but digits_map is 1 for enable, 0 for disable. So we bit-wise NOT first. + + uint8_t to_shift = (~digits_map) << 2; + + digitalWrite(CSSR_LATCH_PIN, LOW); + shiftOut(CSSR_DATA_PIN, CSSR_CLOCK_PIN, LSBFIRST, to_shift); + digitalWrite(CSSR_LATCH_PIN, HIGH); + } + + void begin() + { + pinMode(CSSR_LATCH_PIN, OUTPUT); + pinMode(CSSR_DATA_PIN, OUTPUT); + pinMode(CSSR_CLOCK_PIN, OUTPUT); + + digitalWrite(CSSR_DATA_PIN, LOW); + digitalWrite(CSSR_CLOCK_PIN, LOW); + digitalWrite(CSSR_LATCH_PIN, LOW); + update(); + } + + // These speak the indexes defined in Hardware.h. + // So 0 is disabled, 1 is enabled (even though CS is active low, this gets mapped.) + // So bit 0 (LSB), is index 0, is SECONDS_ONES + // Translation to what the 74HC595 uses is done in update() + void setDigitMap(uint8_t map, bool update_=true) { digits_map = map; if (update_) update(); } + uint8_t getDigitMap() { return digits_map; } + + // Helper functions + // Sets just the one digit by digit number + void setDigit(uint8_t digit, bool update_=true) { setDigitMap(0x01 << digit, update_); } + void setAll(bool update_=true) { setDigitMap(all_on, update_); } + void clear(bool update_=true) { setDigitMap(all_off, update_); } + void setSecondsOnes() { setDigit(SECONDS_ONES); } + void setSecondsTens() { setDigit(SECONDS_TENS); } + void setMinutesOnes() { setDigit(MINUTES_ONES); } + void setMinutesTens() { setDigit(MINUTES_TENS); } + void setHoursOnes() { setDigit(HOURS_ONES); } + void setHoursTens() { setDigit(HOURS_TENS); } + bool isSecondsOnes() { return (digits_map&SECONDS_ONES_MAP > 0); } + bool isSecondsTens() { return (digits_map&SECONDS_TENS_MAP > 0); } + bool isMinutesOnes() { return (digits_map&MINUTES_ONES_MAP > 0); } + bool isMinutesTens() { return (digits_map&MINUTES_TENS_MAP > 0); } + bool isHoursOnes() { return (digits_map&HOURS_ONES_MAP > 0); } + bool isHoursTens() { return (digits_map&HOURS_TENS_MAP > 0); } +}; + + +#endif // CHIP_SELECT_H diff --git a/usermods/EleksTube_IPS/Hardware.h b/usermods/EleksTube_IPS/Hardware.h new file mode 100644 index 00000000..e4f79305 --- /dev/null +++ b/usermods/EleksTube_IPS/Hardware.h @@ -0,0 +1,52 @@ +/* + * Define the hardware for the EleksTube IPS clock. Mostly pin definitions + */ +#ifndef ELEKSTUBEHAX_HARDWARE_H +#define ELEKSTUBEHAX_HARDWARE_H + +#include +#include // for HIGH and LOW + +// Common indexing scheme, used to identify the digit +#define SECONDS_ONES (0) +#define SECONDS_TENS (1) +#define MINUTES_ONES (2) +#define MINUTES_TENS (3) +#define HOURS_ONES (4) +#define HOURS_TENS (5) +#define NUM_DIGITS (6) + +#define SECONDS_ONES_MAP (0x01 << SECONDS_ONES) +#define SECONDS_TENS_MAP (0x01 << SECONDS_TENS) +#define MINUTES_ONES_MAP (0x01 << MINUTES_ONES) +#define MINUTES_TENS_MAP (0x01 << MINUTES_TENS) +#define HOURS_ONES_MAP (0x01 << HOURS_ONES) +#define HOURS_TENS_MAP (0x01 << HOURS_TENS) + +// WS2812 (or compatible) LEDs on the back of the display modules. +#define BACKLIGHTS_PIN (12) + +// Buttons, active low, externally pulled up (with actual resistors!) +#define BUTTON_LEFT_PIN (33) +#define BUTTON_MODE_PIN (32) +#define BUTTON_RIGHT_PIN (35) +#define BUTTON_POWER_PIN (34) + +// I2C to DS3231 RTC. +#define RTC_SCL_PIN (22) +#define RTC_SDA_PIN (21) + +// Chip Select shift register, to select the display +#define CSSR_DATA_PIN (14) +#define CSSR_CLOCK_PIN (16) +#define CSSR_LATCH_PIN (17) + +// SPI to displays +// DEFINED IN User_Setup.h +// Look for: TFT_MOSI, TFT_SCLK, TFT_CS, TFT_DC, and TFT_RST + +// Power for all TFT displays are grounded through a MOSFET so they can all be turned off. +// Active HIGH. +#define TFT_ENABLE_PIN (27) + +#endif // ELEKSTUBEHAX_HARDWARE_H diff --git a/usermods/EleksTube_IPS/TFTs.h b/usermods/EleksTube_IPS/TFTs.h new file mode 100644 index 00000000..675129dc --- /dev/null +++ b/usermods/EleksTube_IPS/TFTs.h @@ -0,0 +1,218 @@ +#ifndef TFTS_H +#define TFTS_H + +#include "wled.h" +#include + +#include +#include "Hardware.h" +#include "ChipSelect.h" + +class TFTs : public TFT_eSPI { +private: + uint8_t digits[NUM_DIGITS]; + + // These read 16- and 32-bit types from the SD card file. + // BMP data is stored little-endian, Arduino is little-endian too. + // May need to reverse subscript order if porting elsewhere. + + uint16_t read16(fs::File &f) { + uint16_t result; + ((uint8_t *)&result)[0] = f.read(); // LSB + ((uint8_t *)&result)[1] = f.read(); // MSB + return result; + } + + uint32_t read32(fs::File &f) { + uint32_t result; + ((uint8_t *)&result)[0] = f.read(); // LSB + ((uint8_t *)&result)[1] = f.read(); + ((uint8_t *)&result)[2] = f.read(); + ((uint8_t *)&result)[3] = f.read(); // MSB + return result; + } + + uint16_t output_buffer[TFT_HEIGHT][TFT_WIDTH]; + + + // These BMP functions are stolen directly from the TFT_SPIFFS_BMP example in the TFT_eSPI library. + // Unfortunately, they aren't part of the library itself, so I had to copy them. + // I've modified drawBmp to buffer the whole image at once instead of doing it line-by-line. + + //// BEGIN STOLEN CODE + + // Draw directly from file stored in RGB565 format + bool drawBin(const char *filename) { + fs::File bmpFS; + + + // Open requested file on SD card + bmpFS = WLED_FS.open(filename, "r"); + + if (!bmpFS) + { + Serial.print(F("File not found: ")); + Serial.println(filename); + return(false); + } + + size_t sz = bmpFS.size(); + if (sz <= 64800) + { + bool oldSwapBytes = getSwapBytes(); + setSwapBytes(true); + + int16_t h = sz / (135 * 2); + + //draw img that is shorter than 240pix into the center + int16_t y = (height() - h) /2; + + bmpFS.read((uint8_t *) output_buffer,sz); + + if (!realtimeMode || realtimeOverride) strip.service(); + + pushImage(0, y, 135, h, (uint16_t *)output_buffer); + + setSwapBytes(oldSwapBytes); + } + + bmpFS.close(); + + return(true); + } + + bool drawBmp(const char *filename) { + fs::File bmpFS; + + // Open requested file on SD card + bmpFS = WLED_FS.open(filename, "r"); + + if (!bmpFS) + { + Serial.print(F("File not found: ")); + Serial.println(filename); + return(false); + } + + uint32_t seekOffset; + int16_t w, h, row; + uint8_t r, g, b; + + uint16_t magic = read16(bmpFS); + if (magic == 0xFFFF) { + Serial.println(F("BMP not found!")); + bmpFS.close(); + return(false); + } + + if (magic != 0x4D42) { + Serial.print(F("File not a BMP. Magic: ")); + Serial.println(magic); + bmpFS.close(); + return(false); + } + + read32(bmpFS); + read32(bmpFS); + seekOffset = read32(bmpFS); + read32(bmpFS); + w = read32(bmpFS); + h = read32(bmpFS); + + if ((read16(bmpFS) != 1) || (read16(bmpFS) != 24) || (read32(bmpFS) != 0)) { + Serial.println(F("BMP format not recognized.")); + bmpFS.close(); + return(false); + } + + //draw img that is shorter than 240pix into the center + int16_t y = (height() - h) /2; + + bool oldSwapBytes = getSwapBytes(); + setSwapBytes(true); + bmpFS.seek(seekOffset); + + uint16_t padding = (4 - ((w * 3) & 3)) & 3; + uint8_t lineBuffer[w * 3 + padding]; + + uint8_t serviceStrip = (!realtimeMode || realtimeOverride) ? 7 : 0; + // row is decremented as the BMP image is drawn bottom up + for (row = h-1; row >= 0; row--) { + if ((row & 0b00000111) == serviceStrip) strip.service(); //still refresh backlight to mitigate stutter every few rows + bmpFS.read(lineBuffer, sizeof(lineBuffer)); + uint8_t* bptr = lineBuffer; + + // Convert 24 to 16 bit colours while copying to output buffer. + for (uint16_t col = 0; col < w; col++) + { + b = *bptr++; + g = *bptr++; + r = *bptr++; + output_buffer[row][col] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); + } + } + + pushImage(0, y, w, h, (uint16_t *)output_buffer); + setSwapBytes(oldSwapBytes); + + bmpFS.close(); + return(true); + } + +public: + TFTs() : TFT_eSPI(), chip_select() + { for (uint8_t digit=0; digit < NUM_DIGITS; digit++) digits[digit] = 0; } + + // no == Do not send to TFT. yes == Send to TFT if changed. force == Send to TFT. + enum show_t { no, yes, force }; + // A digit of 0xFF means blank the screen. + const static uint8_t blanked = 255; + + void begin() { + pinMode(TFT_ENABLE_PIN, OUTPUT); + digitalWrite(TFT_ENABLE_PIN, HIGH); //enable displays on boot + + // Start with all displays selected. + chip_select.begin(); + chip_select.setAll(); + + // Initialize the super class. + init(); + } + + void showDigit(uint8_t digit) { + chip_select.setDigit(digit); + + if (digits[digit] == blanked) { + fillScreen(TFT_BLACK); + } + else { + // Filenames are no bigger than "255.bmp\0" + char file_name[10]; + sprintf(file_name, "/%d.bmp", digits[digit]); + if (WLED_FS.exists(file_name)) { + drawBmp(file_name); + } else { + sprintf(file_name, "/%d.bin", digits[digit]); + drawBin(file_name); + } + } + } + + void setDigit(uint8_t digit, uint8_t value, show_t show=yes) { + uint8_t old_value = digits[digit]; + digits[digit] = value; + + if (show != no && (old_value != value || show == force)) { + showDigit(digit); + } + } + uint8_t getDigit(uint8_t digit) { return digits[digit]; } + + void showAllDigits() { for (uint8_t digit=0; digit < NUM_DIGITS; digit++) showDigit(digit); } + + // Making chip_select public so we don't have to proxy all methods, and the caller can just use it directly. + ChipSelect chip_select; +}; + +#endif // TFTS_H diff --git a/usermods/EleksTube_IPS/User_Setup.h b/usermods/EleksTube_IPS/User_Setup.h new file mode 100644 index 00000000..b4b2edab --- /dev/null +++ b/usermods/EleksTube_IPS/User_Setup.h @@ -0,0 +1,47 @@ +/* + * This is intended to over-ride `User_Setup.h` that comes with the TFT_eSPI library. + * I hate having to modify the library code. + */ + +// ST7789 135 x 240 display with no chip select line + +#define ST7789_DRIVER // Configure all registers + +#define TFT_WIDTH 135 +#define TFT_HEIGHT 240 + +#define CGRAM_OFFSET // Library will add offsets required + +//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red + +//#define TFT_INVERSION_ON +//#define TFT_INVERSION_OFF + +// EleksTube IPS +#define TFT_SDA_READ // Read and write on the MOSI/SDA pin, no separate MISO pin +#define TFT_MOSI 23 +#define TFT_SCLK 18 +//#define TFT_CS -1 // Not connected +#define TFT_DC 25 // Data Command, aka Register Select or RS +#define TFT_RST 26 // Connect reset to ensure display initialises + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +//#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +//#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +//#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +//#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +//#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +//#define SMOOTH_FONT + + +//#define SPI_FREQUENCY 27000000 +#define SPI_FREQUENCY 40000000 + +/* + * To make the Library not over-write all this: + */ +#define USER_SETUP_LOADED diff --git a/usermods/EleksTube_IPS/readme.md b/usermods/EleksTube_IPS/readme.md new file mode 100644 index 00000000..a08d69d2 --- /dev/null +++ b/usermods/EleksTube_IPS/readme.md @@ -0,0 +1,31 @@ +# EleksTube IPS Clock usermod + +This usermod allows WLED to run on the EleksTube IPS clock. +It enables running all WLED effects on the background SK6812 lighting, while displaying digit bitmaps on the 6 IPS screens. +Code is largely based on https://github.com/SmittyHalibut/EleksTubeHAX by Mark Smith! + +Supported: +- Display with custom bitmaps or raw RGB565 images (.bin) from filesystem +- Background lighting +- Power button +- RTC (with RTC usermod) +- Standard WLED time features (NTP, DST, timezones) + +Not supported: +- 3 navigation buttons, on-device setup + +Your images must be exactly 135 pixels wide and 1-240 pixels high. + +## Installation + +Compile and upload to clock using the `elekstube_ips` PlatformIO environment +Once uploaded (the clock can be flashed like any ESP32 module), go to `[WLED-IP]/edit` and upload the 0-9.bin files from [here](https://github.com/Aircoookie/NixieThemes/tree/master/themes/RealisticNixie/bin). +You can find more clockfaces in the [NixieThemes](https://github.com/Aircoookie/NixieThemes/) repo. +Use LED pin 12, relay pin 27 and button pin 34. + +## Use of RGB565 images + +Binary 16-bit per pixel RGB565 format `.bin` images are now supported. This has the benefit of only using 2/3rds of the file size a `.bmp` has. +The drawback is that this format cannot be handled by common image programs and that an extra conversion step is needed. +You can use https://lvgl.io/tools/imageconverter to convert your .bmp to a .bin file (settings `True color` and `Binary RGB565`) +Thank you to @RedNax67 for adding .bin support. \ No newline at end of file diff --git a/usermods/EleksTube_IPS/usermod_elekstube_ips.h b/usermods/EleksTube_IPS/usermod_elekstube_ips.h new file mode 100644 index 00000000..f2ce8eb0 --- /dev/null +++ b/usermods/EleksTube_IPS/usermod_elekstube_ips.h @@ -0,0 +1,60 @@ +#pragma once +#include "TFTs.h" +#include "wled.h" + +//Large parts of the code are from https://github.com/SmittyHalibut/EleksTubeHAX + +class ElekstubeIPSUsermod : public Usermod { + private: + TFTs tfts; + void updateClockDisplay(TFTs::show_t show=TFTs::yes) { + bool set[6] = {false}; + for (uint8_t i = 0; i<6; i++) { + char c = cronixieDisplay[i]; + if (c >= '0' && c <= '9') { + tfts.setDigit(5-i, c - '0', show); set[i] = true; + } else if (c >= 'A' && c <= 'G') { + tfts.setDigit(5-i, c - 'A' + 10, show); set[i] = true; //10.bmp to 16.bmp static display + } else if (c == '-' || c == '_' || c == ' ') { + tfts.setDigit(5-i, 255, show); set[i] = true; //blank + } else { + set[i] = false; //display HHMMSS time + } + } + uint8_t hr = hour(localTime); + uint8_t hrTens = hr/10; + uint8_t mi = minute(localTime); + uint8_t mittens = mi/10; + uint8_t s = second(localTime); + uint8_t sTens = s/10; + if (!set[0]) tfts.setDigit(HOURS_TENS, hrTens, show); + if (!set[1]) tfts.setDigit(HOURS_ONES, hr - hrTens*10, show); + if (!set[2]) tfts.setDigit(MINUTES_TENS, mittens, show); + if (!set[3]) tfts.setDigit(MINUTES_ONES, mi - mittens*10, show); + if (!set[4]) tfts.setDigit(SECONDS_TENS, sTens, show); + if (!set[5]) tfts.setDigit(SECONDS_ONES, s - sTens*10, show); + } + unsigned long lastTime = 0; + public: + + void setup() { + tfts.begin(); + tfts.fillScreen(TFT_BLACK); + + for (int8_t i = 5; i >= 0; i--) { + tfts.setDigit(i, 255, TFTs::force); //turn all off + } + } + + void loop() { + if (toki.isTick()) { + updateLocalTime(); + updateClockDisplay(); + } + } + + uint16_t getId() + { + return USERMOD_ID_ELEKSTUBE_IPS; + } +}; \ No newline at end of file diff --git a/usermods/RTC/readme.md b/usermods/RTC/readme.md new file mode 100644 index 00000000..3aaa6091 --- /dev/null +++ b/usermods/RTC/readme.md @@ -0,0 +1,8 @@ +# DS1307/DS3231 Real time clock + +Gets the time from I2C RTC module on boot. This allows clocks to operate e.g. if temporarily no WiFi is available. +The stored time is updated each time NTP is synced. + +## Installation + +Add the build flag `-D USERMOD_RTC` to your platformio environment. diff --git a/usermods/RTC/usermod_rtc.h b/usermods/RTC/usermod_rtc.h new file mode 100644 index 00000000..75d91b31 --- /dev/null +++ b/usermods/RTC/usermod_rtc.h @@ -0,0 +1,35 @@ +#pragma once + +#include "src/dependencies/time/DS1307RTC.h" +#include "wled.h" + +//Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL)) + +class RTCUsermod : public Usermod { + private: + unsigned long lastTime = 0; + bool disabled = false; + public: + + void setup() { + time_t rtcTime = RTC.get(); + if (rtcTime) { + toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC); + updateLocalTime(); + } else { + if (!RTC.chipPresent()) disabled = true; //don't waste time if H/W error + } + } + + void loop() { + if (!disabled && toki.isTick()) { + time_t t = toki.second(); + if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value + } + } + + uint16_t getId() + { + return USERMOD_ID_RTC; + } +}; \ No newline at end of file diff --git a/usermods/SN_Photoresistor/platformio_override.ini b/usermods/SN_Photoresistor/platformio_override.ini new file mode 100644 index 00000000..91bc5de2 --- /dev/null +++ b/usermods/SN_Photoresistor/platformio_override.ini @@ -0,0 +1,16 @@ +; Options +; ------- +; USERMOD_SN_PHOTORESISTOR - define this to have this user mod included wled00\usermods_list.cpp +; USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds +; USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 20 seconds +; USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE - the voltage supplied to the sensor, defaults to 5v +; USERMOD_SN_PHOTORESISTOR_ADC_PRECISION - the ADC precision is the number of distinguishable ADC inputs, defaults to 1024.0 (10 bits) +; USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE - the resistor size, defaults to 10000.0 (10K hms) +; USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE - the offset value to report on, defaults to 25 +; +[env:usermod_sn_photoresistor_d1_mini] +extends = env:d1_mini +build_flags = + ${common.build_flags_esp8266} + -D USERMOD_SN_PHOTORESISTOR +lib_deps = ${env.lib_deps} diff --git a/usermods/SN_Photoresistor/readme.md b/usermods/SN_Photoresistor/readme.md new file mode 100644 index 00000000..4f3a36fb --- /dev/null +++ b/usermods/SN_Photoresistor/readme.md @@ -0,0 +1,30 @@ +# SN_Photoresistor usermod + +This usermod will read from an attached photoresistor sensor like the KY-018 sensor. +The luminance is displayed both in the Info section of the web UI as well as published to the `/luminance` MQTT topic if enabled. + +## Installation + +Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`. + +### Define Your Options + +* `USERMOD_SN_PHOTORESISTOR` - define this to have this user mod included wled00\usermods_list.cpp +* `USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL` - the number of milliseconds between measurements, defaults to 60 seconds +* `USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 20 seconds +* `USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE` - the voltage supplied to the sensor, defaults to 5v +* `USERMOD_SN_PHOTORESISTOR_ADC_PRECISION` - the ADC precision is the number of distinguishable ADC inputs, defaults to 1024.0 (10 bits) +* `USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE` - the resistor size, defaults to 10000.0 (10K hms) +* `USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE` - the offset value to report on, defaults to 25 + +All parameters can be configured at runtime using Usermods settings page. + +## Project link + +* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link + +### PlatformIO requirements + +If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:usermod_sn_photoresistor_d1_mini`. + +## Change Log diff --git a/usermods/SN_Photoresistor/usermod_sn_photoresistor.h b/usermods/SN_Photoresistor/usermod_sn_photoresistor.h new file mode 100644 index 00000000..465b22f7 --- /dev/null +++ b/usermods/SN_Photoresistor/usermod_sn_photoresistor.h @@ -0,0 +1,128 @@ +#pragma once + +#include "wled.h" + +//Pin defaults for QuinLed Dig-Uno +#define PHOTORESISTOR_PIN A0 + +// the frequency to check photoresistor, 10 seconds +#ifndef USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL +#define USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL 10000 +#endif + +// how many seconds after boot to take first measurement, 10 seconds +#ifndef USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT +#define USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT 10000 +#endif + +// supplied voltage +#ifndef USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE +#define USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE 5 +#endif + +// 10 bits +#ifndef USERMOD_SN_PHOTORESISTOR_ADC_PRECISION +#define USERMOD_SN_PHOTORESISTOR_ADC_PRECISION 1024.0 +#endif + +// resistor size 10K hms +#ifndef USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE +#define USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE 10000.0 +#endif + +// only report if differance grater than offset value +#ifndef USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE +#define USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE 5 +#endif + +class Usermod_SN_Photoresistor : public Usermod +{ +private: + // set last reading as "40 sec before boot", so first reading is taken after 20 sec + unsigned long lastMeasurement = UINT32_MAX - (USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT); + // flag to indicate we have finished the first getTemperature call + // allows this library to report to the user how long until the first + // measurement + bool getLuminanceComplete = false; + uint16_t lastLDRValue = -1000; + + bool checkBoundSensor(float newValue, float prevValue, float maxDiff) + { + return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff; + } + + uint16_t getLuminance() + { + // http://forum.arduino.cc/index.php?topic=37555.0 + // https://forum.arduino.cc/index.php?topic=185158.0 + float volts = analogRead(PHOTORESISTOR_PIN) * (USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE / USERMOD_SN_PHOTORESISTOR_ADC_PRECISION); + float amps = volts / USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE; + float lux = amps * 1000000 * 2.0; + + lastMeasurement = millis(); + getLuminanceComplete = true; + return uint16_t(lux); + } + +public: + void setup() + { + pinMode(PHOTORESISTOR_PIN, INPUT); + } + + void loop() + { + unsigned long now = millis(); + + // check to see if we are due for taking a measurement + // lastMeasurement will not be updated until the conversion + // is complete the the reading is finished + if (now - lastMeasurement < USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL) + { + return; + } + + uint16_t currentLDRValue = getLuminance(); + if (checkBoundSensor(currentLDRValue, lastLDRValue, USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE)) + { + lastLDRValue = currentLDRValue; + + if (WLED_MQTT_CONNECTED) + { + char subuf[45]; + strcpy(subuf, mqttDeviceTopic); + strcat_P(subuf, PSTR("/luminance")); + mqtt->publish(subuf, 0, true, String(lastLDRValue).c_str()); + } + else + { + DEBUG_PRINTLN("Missing MQTT connection. Not publishing data"); + } + } + } + + void addToJsonInfo(JsonObject &root) + { + JsonObject user = root[F("u")]; + if (user.isNull()) user = root.createNestedObject(F("u")); + + JsonArray lux = user.createNestedArray(F("Luminance")); + + if (!getLuminanceComplete) + { + // if we haven't read the sensor yet, let the user know + // that we are still waiting for the first measurement + lux.add((USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - millis()) / 1000); + lux.add(F(" sec until read")); + return; + } + + lux.add(lastLDRValue); + lux.add(F(" lux")); + } + + uint16_t getId() + { + return USERMOD_ID_SN_PHOTORESISTOR; + } +}; diff --git a/usermods/SN_Photoresistor/usermods_list.cpp b/usermods/SN_Photoresistor/usermods_list.cpp new file mode 100644 index 00000000..649e1973 --- /dev/null +++ b/usermods/SN_Photoresistor/usermods_list.cpp @@ -0,0 +1,14 @@ +#include "wled.h" +/* + * Register your v2 usermods here! + */ +#ifdef USERMOD_SN_PHOTORESISTOR +#include "../usermods/SN_Photoresistor/usermod_sn_photoresistor.h" +#endif + +void registerUsermods() +{ +#ifdef USERMOD_SN_PHOTORESISTOR + usermods.add(new Usermod_SN_Photoresistor()); +#endif +} \ No newline at end of file diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 4d2f2185..8364083f 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -42,30 +42,24 @@ uint16_t WS2812FX::mode_static(void) { * Blink/strobe function * Alternate between color1 and color2 * if(strobe == true) then create a strobe effect - * NOTE: Maybe re-rework without timer */ uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) { - uint16_t stateTime = SEGENV.aux1; uint32_t cycleTime = (255 - SEGMENT.speed)*20; - uint32_t onTime = 0; - uint32_t offTime = cycleTime; - - if (!strobe) { - onTime = (cycleTime * SEGMENT.intensity) >> 8; - offTime = cycleTime - onTime; + uint32_t onTime = FRAMETIME; + if (!strobe) onTime += ((cycleTime * SEGMENT.intensity) >> 8); + cycleTime += FRAMETIME*2; + uint32_t it = now / cycleTime; + uint32_t rem = now % cycleTime; + + bool on = false; + if (it != SEGENV.step //new iteration, force on state for one frame, even if set time is too brief + || rem <= onTime) { + on = true; } - stateTime = ((SEGENV.aux0 & 1) == 0) ? onTime : offTime; - stateTime += 20; - - if (now - SEGENV.step > stateTime) - { - SEGENV.aux0++; - SEGENV.aux1 = stateTime; - SEGENV.step = now; - } + SEGENV.step = it; //save previous iteration - uint32_t color = ((SEGENV.aux0 & 1) == 0) ? color1 : color2; + uint32_t color = on ? color1 : color2; if (color == color1 && do_palette) { for(uint16_t i = 0; i < SEGLEN; i++) { diff --git a/wled00/FX.h b/wled00/FX.h index e8416d5d..b3023198 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -73,7 +73,7 @@ #define SEGENV _segment_runtimes[_segment_index] #define SEGLEN _virtualSegmentLength #define SEGACT SEGMENT.stop -#define SPEED_FORMULA_L (uint16_t)(5 + (50*(255 - SEGMENT.speed))/SEGLEN) +#define SPEED_FORMULA_L 5U + (50U*(255U - SEGMENT.speed))/SEGLEN #define RESET_RUNTIME memset(_segment_runtimes, 0, sizeof(_segment_runtimes)) // some common colors diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index dd11d77e..3cbf5ea1 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -991,7 +991,7 @@ uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8 } uint8_t paletteIndex = i; - if (mapping) paletteIndex = (i*255)/(SEGLEN -1); + if (mapping && SEGLEN > 1) paletteIndex = (i*255)/(SEGLEN -1); 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); diff --git a/wled00/alexa.cpp b/wled00/alexa.cpp index 4f406a3f..83e7c25f 100644 --- a/wled00/alexa.cpp +++ b/wled00/alexa.cpp @@ -46,7 +46,10 @@ void onAlexaChange(EspalexaDevice* dev) bri = briLast; colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } - } else applyPreset(macroAlexaOn); + } else { + applyPreset(macroAlexaOn); + if (bri == 0) espalexaDevice->setValue(briLast); //stop Alexa from complaining if macroAlexaOn does not actually turn on + } } else if (m == EspalexaDeviceProperty::off) { if (!macroAlexaOff) @@ -57,7 +60,10 @@ void onAlexaChange(EspalexaDevice* dev) bri = 0; colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } - } else applyPreset(macroAlexaOff); + } else { + applyPreset(macroAlexaOff); + if (bri != 0) espalexaDevice->setValue(0); //stop Alexa from complaining if macroAlexaOff does not actually turn off + } } else if (m == EspalexaDeviceProperty::bri) { bri = espalexaDevice->getValue(); diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index bb7f33c7..3612a0bd 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -160,7 +160,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { } CJSON(touchThreshold,hw[F("btn")][F("tt")]); - #ifndef WLED_DISABLE_INFRARED int hw_ir_pin = hw["ir"]["pin"] | -2; // 4 if (hw_ir_pin > -2) { if (pinManager.allocatePin(hw_ir_pin,false)) { @@ -169,7 +168,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { irPin = -1; } } - #endif CJSON(irEnabled, hw["ir"]["type"]); JsonObject relay = hw[F("relay")]; diff --git a/wled00/const.h b/wled00/const.h index 8d73ce14..d44ad290 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -47,7 +47,7 @@ #define USERMOD_ID_FIXNETSERVICES 4 //Usermod "usermod_Fix_unreachable_netservices.h" #define USERMOD_ID_PIRSWITCH 5 //Usermod "usermod_PIR_sensor_switch.h" #define USERMOD_ID_IMU 6 //Usermod "usermod_mpu6050_imu.h" -#define USERMOD_ID_FOUR_LINE_DISP 7 //Usermod "usermod_v2_four_line_display.h +#define USERMOD_ID_FOUR_LINE_DISP 7 //Usermod "usermod_v2_four_line_display.h #define USERMOD_ID_ROTARY_ENC_UI 8 //Usermod "usermod_v2_rotary_encoder_ui.h" #define USERMOD_ID_AUTO_SAVE 9 //Usermod "usermod_v2_auto_save.h" #define USERMOD_ID_DHT 10 //Usermod "usermod_dht.h" @@ -55,6 +55,9 @@ #define USERMOD_ID_VL53L0X 12 //Usermod "usermod_vl53l0x_gestures.h" #define USERMOD_ID_MULTI_RELAY 13 //Usermod "usermod_multi_relay.h" #define USERMOD_ID_ANIMATED_STAIRCASE 14 //Usermod "Animated_Staircase.h" +#define USERMOD_ID_RTC 15 //Usermod "usermod_rtc.h" +#define USERMOD_ID_ELEKSTUBE_IPS 16 //Usermod "usermod_elekstube_ips.h" +#define USERMOD_ID_SN_PHOTORESISTOR 17 //Usermod "usermod_sn_photoresistor.h" //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot @@ -62,7 +65,7 @@ #define AP_BEHAVIOR_ALWAYS 2 //Always open #define AP_BEHAVIOR_BUTTON_ONLY 3 //Only when button pressed for 6 sec -//Notifier callMode +//Notifier callMode #define NOTIFIER_CALL_MODE_INIT 0 //no updates on init, can be used to disable updates #define NOTIFIER_CALL_MODE_DIRECT_CHANGE 1 #define NOTIFIER_CALL_MODE_BUTTON 2 @@ -79,7 +82,7 @@ #define RGBW_MODE_MANUAL_ONLY 0 //No automatic white channel calculation. Manual white channel slider #define RGBW_MODE_AUTO_BRIGHTER 1 //New algorithm. Adds as much white as the darkest RGBW channel #define RGBW_MODE_AUTO_ACCURATE 2 //New algorithm. Adds as much white as the darkest RGBW channel and subtracts this amount from each RGB channel -#define RGBW_MODE_DUAL 3 //Manual slider + auto calculation. Automatically calculates only if manual slider is set to off (0) +#define RGBW_MODE_DUAL 3 //Manual slider + auto calculation. Automatically calculates only if manual slider is set to off (0) #define RGBW_MODE_LEGACY 4 //Old floating algorithm. Too slow for realtime and palette support //realtime modes @@ -190,6 +193,9 @@ #define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed #define SEG_OPTION_TRANSITIONAL 7 +//Playlist option byte +#define PL_OPTION_SHUFFLE 0x01 + // WLED Error modes #define ERR_NONE 0 // All good :) #define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?) @@ -235,7 +241,11 @@ // string temp buffer (now stored in stack locally) #define OMAX 2048 -#define E131_MAX_UNIVERSE_COUNT 9 +#ifdef WLED_USE_ETHERNET +#define E131_MAX_UNIVERSE_COUNT 20 +#else +#define E131_MAX_UNIVERSE_COUNT 10 +#endif #define ABL_MILLIAMPS_DEFAULT 850 // auto lower brightness to stay close to milliampere limit diff --git a/wled00/data/404.htm b/wled00/data/404.htm index 1b7c95fc..87244a94 100644 --- a/wled00/data/404.htm +++ b/wled00/data/404.htm @@ -39,7 +39,7 @@ - +

404 Not Found

Akemi does not know where you are headed...

diff --git a/wled00/data/index.js b/wled00/data/index.js index f8db561d..47b130c3 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -1032,6 +1032,8 @@ function cmpP(a, b) function handleJson(s) { if (!s) return false; + var e1 = gId('fxlist'); + var e2 = gId('selectPalette'); isOn = s.on; gId('sliderBri').value= s.bri; @@ -1084,6 +1086,28 @@ function handleJson(s) selectedPal = i.pal; selectedFx = i.fx; +/*/--- AC addition ---// +// unfortunately this will trigger JSON request loop due to onchange event on input elements +// and it may not be necessary with websockes + + // Effects + var selFx = e1.querySelector(`input[name="fx"][value="${i.fx}"]`); + if (selFx) selFx.checked = true; + else location.reload(); //effect list is gone (e.g. if restoring tab). Reload. + + var selElement = e1.querySelector('.selected'); + if (selElement) selElement.classList.remove('selected'); + var selectedEffect = e1.querySelector(`.lstI[data-id="${i.fx}"]`); + if (selectedEffect) selectedEffect.classList.add('selected'); + + // Palettes + var selPa = e2.querySelector(`input[name="palette"][value="${i.pal}"]`); + if (selPa) selPa.checked = true; + selElement = e2.querySelector('.selected'); + if (selElement) selElement.classList.remove('selected'); + e2.querySelector(`.lstI[data-id="${i.pal}"]`).classList.add('selected'); +//--- AC addition ---/*/ + displayRover(lastinfo, s); clearErrorToast(); @@ -1097,9 +1121,6 @@ function requestJson(command, rinfo = true, verbose = true, callback = null) lastUpdate = new Date(); if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000); var req = null; - var e1 = gId('fxlist'); - var e2 = gId('selectPalette'); - var url = (loc?`http://${locip}`:'') + (rinfo ? '/json/si': (command ? '/json/state':'/json/si')); var type = command ? 'post':'get'; diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 1869850e..999d5cfe 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -283,11 +283,11 @@ Color Order:
-Pin: -Clock: - - - +Pin: +Clock: + + +
Start:  
@@ -311,7 +311,7 @@ Reverse (rotated 180°): var c = gId("btns").innerHTML; var bt = "BT" + i; var be = "BE" + i; - c += `Button ${i} pin: `; + c += `Button ${i} pin: `; c += `

Touch threshold:
- IR pin:
IR info
- Relay pin: invert  ×
+ Relay pin: invert  ×

Defaults

Turn LEDs on after power up/reset:
diff --git a/wled00/data/settings_sync.htm b/wled00/data/settings_sync.htm index cc57f6b9..84fb5ae4 100644 --- a/wled00/data/settings_sync.htm +++ b/wled00/data/settings_sync.htm @@ -19,28 +19,6 @@ function GetV(){var d=document;}

Sync setup

-

WLED Broadcast

UDP Port:
2nd Port:
diff --git a/wled00/data/settings_time.htm b/wled00/data/settings_time.htm index d53d2443..a2891a6d 100644 --- a/wled00/data/settings_time.htm +++ b/wled00/data/settings_time.htm @@ -76,11 +76,11 @@ } } function addRow(i,p,l,d) { - var t = gId("macros"); - var rCnt = t.rows.length; - var tr = t.insertRow(rCnt); + var t = gId("macros"); // table + var rCnt = t.rows.length; // get the number of rows. + var tr = t.insertRow(rCnt); // table row. - var td = document.createElement('td'); + var td = document.createElement('td'); // TABLE DEFINITION. td = tr.insertCell(0); td.innerHTML = `Button ${i}:`; td = tr.insertCell(1); @@ -179,17 +179,7 @@ -
For analog button set double to: -
    -
  • 250=global brightness
  • -
  • 249=effect speed
  • -
  • 248=effect intensity
  • -
  • 247=palette
  • -
  • 200=primary color hue
  • -
  • 0..32=segment X opacity
  • -
- More details: Analog Button setup -
+ Analog Button setup

Time-controlled presets

diff --git a/wled00/data/welcome.htm b/wled00/data/welcome.htm index f0e1b463..1b2bb60f 100644 --- a/wled00/data/welcome.htm +++ b/wled00/data/welcome.htm @@ -31,11 +31,11 @@ } img { - width: 999px; - max-width: 85%; + width: 950px; + max-width: 82%; image-rendering: pixelated; image-rendering: crisp-edges; - margin: 25px 0 -10px 0; + margin: 4vh 0 0 0; animation: fi 1s; } @@ -50,7 +50,7 @@ - +

Welcome to WLED!

Thank you for installing my application!

diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 878f01c5..90ebe65b 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -100,8 +100,8 @@ void handleIR(); #include "src/dependencies/json/AsyncJson-v6.h" #include "FX.h" -void deserializeSegment(JsonObject elem, byte it, bool fromPlaylist = false); -bool deserializeState(JsonObject root, bool fromPlaylist = false); +void deserializeSegment(JsonObject elem, byte it, byte presetId = 0); +bool deserializeState(JsonObject root, byte presetId = 0); void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true, uint8_t versionAPI = 1); void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true); void serializeInfo(JsonObject root); @@ -130,6 +130,7 @@ bool initMqtt(); void publishMqtt(); //ntp.cpp +void handleTime(); void handleNetworkTime(); void sendNTPPacket(); bool checkNTPResponse(); @@ -140,6 +141,7 @@ void setCountdown(); byte weekdayMondayFirst(); void checkTimers(); void calculateSunriseAndSunset(); +void setTimeFromAPI(uint32_t timein); //overlay.cpp void initCronixie(); @@ -156,11 +158,11 @@ void _drawOverlayCronixie(); //playlist.cpp void shufflePlaylist(); void unloadPlaylist(); -void loadPlaylist(JsonObject playlistObject); +void loadPlaylist(JsonObject playlistObject, byte presetId = 0); void handlePlaylist(); //presets.cpp -bool applyPreset(byte index, bool fromPlaylist = false); +bool applyPreset(byte index); void savePreset(byte index, bool persist = true, const char* pname = nullptr, JsonObject saveobj = JsonObject()); void deletePreset(byte index); diff --git a/wled00/html_other.h b/wled00/html_other.h index 5e14cc35..f16b2047 100644 --- a/wled00/html_other.h +++ b/wled00/html_other.h @@ -55,9 +55,9 @@ Please do not close or refresh the page :)
)====="; const char PAGE_welcome[] PROGMEM = R"=====(Welcome! +src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAG0AAAAfCAMAAADazLOuAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAABLUExURQAAAAB81gCU/zKq///mo7sWMN8bO+ZIYtZaAP9rAP+HMsCiG+TAIOnMS0KqNU7KPnLUZOrq6v///4CAgGhoaL+/v6CgoExMTAAAAAlm4O8AAAAZdFJOU////////////////////////////////wABNAq3AAAACXBIWXMAAA7DAAAOwwHHb6hkAAACN0lEQVRIS73VjVLCMBAEYIr8CYKkrdj3f1J37zaXFCpTO+piaDgbPq9px9VQ0qyrvKj4q6m0Zr1h+M7xF1zRmnWzqV9/0d2jttGotO1uv9dUObwej5oqp7fzWVPl8n69aprzoOUUbbvdIbV3OLwitXc6vSG1d7m8I3feSEN0j2CeNbOY4MxigjOLCc4sZsTV2l1cCyy4wIILLLjAxtykltq2rbTU+qi01N5rXNO2leaFORoija2l5MM5a02ac9Ya16Sk5tgaPrUpjZub0BL6YqSxKwbH77XUUmSkJXSl8QtaMuyJhq5maL5nTKVpZC13VmtMpTFT2g4vJjTuGfMzzXftiUZnhdtgb1xofvypRon5TjNnxYN9zJo6K5ruSIzQtGuVZn0x91rKvdHBvm39E7SyZ4y06Gz8BDBFKzsXmhcwyfsGZ9VpbhoiCinaxPNmGWmWWrNU2jB0q6HvOhN1JUtCixQtp2g51ZVUXIPS2RMAD++T2nY/DrDjOMDO4wC7jmNYj3d73nrXug8Yt9uNB8xNU1cKNXWlUFNXCjV1pZhGTE83m2vWfYf/NGj4Bg1zu5JD3/MnH5ZWfLOksbmGWGjgXMN5/C2GXYGFFW9Nmtle6Xut0Gm+JsayCj8z0nhjGvYJzVf4aSzmNYsr+u7Q2JIdoX3YOQjOslmsW1jJ3120nE9gfo79hTaNdcsqVR610lvO47pllae9ReZ805zKo2a3iaY5c75pTmVCA6dJ5H7N0sr/asPwBehb7ifEhusRAAAAAElFTkSuQmCC">

Welcome to WLED!

Thank you for installing my application!

Next steps:

Connect the module to your local WiFi here!
)====="; diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 6474abb6..606a9a5e 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -74,7 +74,7 @@ Do not enable if WiFi is working correctly, increases power consumption.
LED Settings