diff --git a/CHANGELOG.md b/CHANGELOG.md index 56c722e22..a989eaf32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ### Builds after release 0.12.0 +#### 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 diff --git a/platformio.ini b/platformio.ini index f57c4cc73..d5c936f18 100644 --- a/platformio.ini +++ b/platformio.ini @@ -473,3 +473,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/usermods/EleksTube_IPS/ChipSelect.h b/usermods/EleksTube_IPS/ChipSelect.h new file mode 100644 index 000000000..c58b28549 --- /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 000000000..e4f793053 --- /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 000000000..e96299047 --- /dev/null +++ b/usermods/EleksTube_IPS/TFTs.h @@ -0,0 +1,169 @@ +#ifndef TFTS_H +#define TFTS_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 + + bool drawBmp(const char *filename) { + fs::File bmpFS; + + // Open requested file on SD card + bmpFS = WLED_FS.open(filename, "r"); + + if (!bmpFS) + { + Serial.println(F("File not found")); + 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]; + + // row is decremented as the BMP image is drawn bottom up + for (row = h-1; row >= 0; row--) { + if (row & 0b00000111 == 7) 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]); + drawBmp(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 000000000..b4b2edab0 --- /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/bmp/0.bmp b/usermods/EleksTube_IPS/bmp/0.bmp new file mode 100644 index 000000000..e7d778560 Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/0.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/1.bmp b/usermods/EleksTube_IPS/bmp/1.bmp new file mode 100644 index 000000000..209529f9a Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/1.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/2.bmp b/usermods/EleksTube_IPS/bmp/2.bmp new file mode 100644 index 000000000..d94a55d5f Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/2.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/3.bmp b/usermods/EleksTube_IPS/bmp/3.bmp new file mode 100644 index 000000000..401961172 Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/3.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/4.bmp b/usermods/EleksTube_IPS/bmp/4.bmp new file mode 100644 index 000000000..7b663d8ad Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/4.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/5.bmp b/usermods/EleksTube_IPS/bmp/5.bmp new file mode 100644 index 000000000..b27766486 Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/5.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/6.bmp b/usermods/EleksTube_IPS/bmp/6.bmp new file mode 100644 index 000000000..04984b5c0 Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/6.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/7.bmp b/usermods/EleksTube_IPS/bmp/7.bmp new file mode 100644 index 000000000..83d8e9458 Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/7.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/8.bmp b/usermods/EleksTube_IPS/bmp/8.bmp new file mode 100644 index 000000000..565980e89 Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/8.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/9.bmp b/usermods/EleksTube_IPS/bmp/9.bmp new file mode 100644 index 000000000..88cc3004c Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/9.bmp differ diff --git a/usermods/EleksTube_IPS/readme.md b/usermods/EleksTube_IPS/readme.md new file mode 100644 index 000000000..a510ad6e1 --- /dev/null +++ b/usermods/EleksTube_IPS/readme.md @@ -0,0 +1,21 @@ +# 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 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 + +## 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.bmp files from the bmp folder. +Use LED pin 12, relay pin 27 and button pin 34. \ 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 000000000..4f820c268 --- /dev/null +++ b/usermods/EleksTube_IPS/usermod_elekstube_ips.h @@ -0,0 +1,63 @@ +#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); + tfts.setTextColor(TFT_WHITE, TFT_BLACK); + tfts.setCursor(0, 0, 2); + tfts.println(""); + } + + void loop() { + if (lastTime == 0) { + tfts.fillScreen(TFT_BLACK); + updateClockDisplay(TFTs::force); + } + if (millis() - lastTime > 100) { + updateClockDisplay(); + lastTime = millis(); + } + } + + 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 000000000..3aaa60911 --- /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 000000000..bf0047a72 --- /dev/null +++ b/usermods/RTC/usermod_rtc.h @@ -0,0 +1,37 @@ +#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) { + setTime(rtcTime); + updateLocalTime(); + } else { + if (!RTC.chipPresent()) disabled = true; //don't waste time if H/W error + } + } + + void loop() { + if (!disabled && millis() - lastTime > 500) { + time_t t = now(); + if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value + + lastTime = millis(); + } + } + + uint16_t getId() + { + return USERMOD_ID_RTC; + } +}; \ No newline at end of file diff --git a/usermods/usermod_v2_rotary_encoder_ui/platformio_override.ini.sample b/usermods/usermod_v2_rotary_encoder_ui/platformio_override.ini.sample index cc39d65cf..4b537a8f7 100644 --- a/usermods/usermod_v2_rotary_encoder_ui/platformio_override.ini.sample +++ b/usermods/usermod_v2_rotary_encoder_ui/platformio_override.ini.sample @@ -9,7 +9,7 @@ build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp32} -D USERMOD_MODE_SORT - -D USERMOD_FOUR_LINE_DISLAY -D FLD_PIN_SCL=22 -D FLD_PIN_SDA=21 + -D USERMOD_FOUR_LINE_DISPLAY -D FLD_PIN_SCL=22 -D FLD_PIN_SDA=21 -D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=18 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=19 -D USERMOD_AUTO_SAVE -D AUTOSAVE_PRESET_NUM=1 -D LEDPIN=16 -D BTNPIN=13 @@ -28,7 +28,7 @@ build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp8266} -D USERMOD_MODE_SORT - -D USERMOD_FOUR_LINE_DISLAY -D FLD_PIN_SCL=5 -D FLD_PIN_SDA=4 + -D USERMOD_FOUR_LINE_DISPLAY -D FLD_PIN_SCL=5 -D FLD_PIN_SDA=4 -D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=12 -D ENCODER_CLK_PIN=14 -D ENCODER_SW_PIN=13 -D USERMOD_AUTO_SAVE -D AUTOSAVE_PRESET_NUM=1 -D LEDPIN=3 -D BTNPIN=0 diff --git a/usermods/usermod_v2_rotary_encoder_ui/readme.md b/usermods/usermod_v2_rotary_encoder_ui/readme.md index 4477590b5..9bbcd6a6c 100644 --- a/usermods/usermod_v2_rotary_encoder_ui/readme.md +++ b/usermods/usermod_v2_rotary_encoder_ui/readme.md @@ -16,7 +16,7 @@ This file should be placed in the same directory as `platformio.ini`. ### Define Your Options * `USERMOD_ROTARY_ENCODER_UI` - define this to have this user mod included wled00\usermods_list.cpp -* `USERMOD_FOUR_LINE_DISLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp - also tells this usermod that the display is available (see the Four Line Display usermod `readme.md` for more details) +* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp - also tells this usermod that the display is available (see the Four Line Display usermod `readme.md` for more details) * `ENCODER_DT_PIN` - The encoders DT pin, defaults to 12 * `ENCODER_CLK_PIN` - The encoders CLK pin, defaults to 14 * `ENCODER_SW_PIN` - The encoders SW pin, defaults to 13 diff --git a/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h b/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h index 6dc2a1be3..5ff1090d2 100644 --- a/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h +++ b/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h @@ -37,7 +37,7 @@ #define ENCODER_SW_PIN 13 #endif -#ifndef USERMOD_FOUR_LINE_DISLAY +#ifndef USERMOD_FOUR_LINE_DISPLAY // These constants won't be defined if we aren't using FourLineDisplay. #define FLD_LINE_3_BRIGHTNESS 0 #define FLD_LINE_3_EFFECT_SPEED 0 @@ -62,7 +62,7 @@ private: unsigned char button_state = HIGH; unsigned char prev_button_state = HIGH; -#ifdef USERMOD_FOUR_LINE_DISLAY +#ifdef USERMOD_FOUR_LINE_DISPLAY FourLineDisplayUsermod *display; #else void* display = nullptr; @@ -96,7 +96,7 @@ public: modes_alpha_indexes = modeSortUsermod->getModesAlphaIndexes(); palettes_alpha_indexes = modeSortUsermod->getPalettesAlphaIndexes(); -#ifdef USERMOD_FOUR_LINE_DISLAY +#ifdef USERMOD_FOUR_LINE_DISPLAY // This Usermod uses FourLineDisplayUsermod for the best experience. // But it's optional. But you want it. display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP); @@ -248,7 +248,7 @@ public: } boolean changeState(const char *stateName, byte lineThreeMode, byte markedLine) { -#ifdef USERMOD_FOUR_LINE_DISLAY +#ifdef USERMOD_FOUR_LINE_DISPLAY if (display != nullptr) { if (display->wakeDisplay()) { // Throw away wake up input @@ -272,7 +272,7 @@ public: } void changeBrightness(bool increase) { -#ifdef USERMOD_FOUR_LINE_DISLAY +#ifdef USERMOD_FOUR_LINE_DISPLAY if (display && display->wakeDisplay()) { // Throw away wake up input return; @@ -288,7 +288,7 @@ public: } void changeEffect(bool increase) { -#ifdef USERMOD_FOUR_LINE_DISLAY +#ifdef USERMOD_FOUR_LINE_DISPLAY if (display && display->wakeDisplay()) { // Throw away wake up input return; @@ -305,7 +305,7 @@ public: } void changeEffectSpeed(bool increase) { -#ifdef USERMOD_FOUR_LINE_DISLAY +#ifdef USERMOD_FOUR_LINE_DISPLAY if (display && display->wakeDisplay()) { // Throw away wake up input return; @@ -321,7 +321,7 @@ public: } void changeEffectIntensity(bool increase) { -#ifdef USERMOD_FOUR_LINE_DISLAY +#ifdef USERMOD_FOUR_LINE_DISPLAY if (display && display->wakeDisplay()) { // Throw away wake up input return; @@ -337,7 +337,7 @@ public: } void changePalette(bool increase) { -#ifdef USERMOD_FOUR_LINE_DISLAY +#ifdef USERMOD_FOUR_LINE_DISPLAY if (display && display->wakeDisplay()) { // Throw away wake up input return; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 8e937d741..4d08bc144 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -49,7 +49,7 @@ //#define DEFAULT_LED_TYPE TYPE_WS2812_RGB #ifndef PIXEL_COUNTS - #define PIXEL_COUNTS 30 + #define PIXEL_COUNTS DEFAULT_LED_COUNT #endif #ifndef DATA_PINS diff --git a/wled00/const.h b/wled00/const.h index 36c55c7e8..19c2853ee 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -55,6 +55,8 @@ #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" //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot diff --git a/wled00/json.cpp b/wled00/json.cpp index 5bd76ec2e..a2cf126da 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -270,6 +270,10 @@ bool deserializeState(JsonObject root) } } + if (root["nx"].is()) { + strncpy(cronixieDisplay, root["nx"], 6); + } + usermods.readFromJsonState(root); int ps = root[F("psave")] | -1; diff --git a/wled00/mqtt.cpp b/wled00/mqtt.cpp index 79de0afb8..8540335ba 100644 --- a/wled00/mqtt.cpp +++ b/wled00/mqtt.cpp @@ -137,7 +137,7 @@ void publishMqtt() XML_response(nullptr, apires); strcpy(subuf, mqttDeviceTopic); strcat_P(subuf, PSTR("/v")); - mqtt->publish(subuf, 0, true, apires); + mqtt->publish(subuf, 0, false, apires); } diff --git a/wled00/src/dependencies/time/DS1307RTC.cpp b/wled00/src/dependencies/time/DS1307RTC.cpp new file mode 100644 index 000000000..2c16a8a33 --- /dev/null +++ b/wled00/src/dependencies/time/DS1307RTC.cpp @@ -0,0 +1,218 @@ +/* + * DS1307RTC.h - library for DS1307 RTC + + Copyright (c) Michael Margolis 2009 + This library is intended to be uses with Arduino Time library functions + + The library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + 30 Dec 2009 - Initial release + 5 Sep 2011 updated for Arduino 1.0 + */ + + +#if defined (__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || (__AVR_ATtiny2313__) +#include +#define Wire TinyWireM +#else +#include +#endif +#include "DS1307RTC.h" + +#define DS1307_CTRL_ID 0x68 + +DS1307RTC::DS1307RTC() +{ + Wire.begin(); +} + +// PUBLIC FUNCTIONS +time_t DS1307RTC::get() // Aquire data from buffer and convert to time_t +{ + tmElements_t tm; + if (read(tm) == false) return 0; + return(makeTime(tm)); +} + +bool DS1307RTC::set(time_t t) +{ + tmElements_t tm; + breakTime(t, tm); + return write(tm); +} + +// Aquire data from the RTC chip in BCD format +bool DS1307RTC::read(tmElements_t &tm) +{ + uint8_t sec; + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x00); +#else + Wire.send(0x00); +#endif + if (Wire.endTransmission() != 0) { + exists = false; + return false; + } + exists = true; + + // request the 7 data fields (secs, min, hr, dow, date, mth, yr) + Wire.requestFrom(DS1307_CTRL_ID, tmNbrFields); + if (Wire.available() < tmNbrFields) return false; +#if ARDUINO >= 100 + sec = Wire.read(); + tm.Second = bcd2dec(sec & 0x7f); + tm.Minute = bcd2dec(Wire.read() ); + tm.Hour = bcd2dec(Wire.read() & 0x3f); // mask assumes 24hr clock + tm.Wday = bcd2dec(Wire.read() ); + tm.Day = bcd2dec(Wire.read() ); + tm.Month = bcd2dec(Wire.read() ); + tm.Year = y2kYearToTm((bcd2dec(Wire.read()))); +#else + sec = Wire.receive(); + tm.Second = bcd2dec(sec & 0x7f); + tm.Minute = bcd2dec(Wire.receive() ); + tm.Hour = bcd2dec(Wire.receive() & 0x3f); // mask assumes 24hr clock + tm.Wday = bcd2dec(Wire.receive() ); + tm.Day = bcd2dec(Wire.receive() ); + tm.Month = bcd2dec(Wire.receive() ); + tm.Year = y2kYearToTm((bcd2dec(Wire.receive()))); +#endif + if (sec & 0x80) return false; // clock is halted + return true; +} + +bool DS1307RTC::write(tmElements_t &tm) +{ + // To eliminate any potential race conditions, + // stop the clock before writing the values, + // then restart it after. + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x00); // reset register pointer + Wire.write((uint8_t)0x80); // Stop the clock. The seconds will be written last + Wire.write(dec2bcd(tm.Minute)); + Wire.write(dec2bcd(tm.Hour)); // sets 24 hour format + Wire.write(dec2bcd(tm.Wday)); + Wire.write(dec2bcd(tm.Day)); + Wire.write(dec2bcd(tm.Month)); + Wire.write(dec2bcd(tmYearToY2k(tm.Year))); +#else + Wire.send(0x00); // reset register pointer + Wire.send(0x80); // Stop the clock. The seconds will be written last + Wire.send(dec2bcd(tm.Minute)); + Wire.send(dec2bcd(tm.Hour)); // sets 24 hour format + Wire.send(dec2bcd(tm.Wday)); + Wire.send(dec2bcd(tm.Day)); + Wire.send(dec2bcd(tm.Month)); + Wire.send(dec2bcd(tmYearToY2k(tm.Year))); +#endif + if (Wire.endTransmission() != 0) { + exists = false; + return false; + } + exists = true; + + // Now go back and set the seconds, starting the clock back up as a side effect + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x00); // reset register pointer + Wire.write(dec2bcd(tm.Second)); // write the seconds, with the stop bit clear to restart +#else + Wire.send(0x00); // reset register pointer + Wire.send(dec2bcd(tm.Second)); // write the seconds, with the stop bit clear to restart +#endif + if (Wire.endTransmission() != 0) { + exists = false; + return false; + } + exists = true; + return true; +} + +unsigned char DS1307RTC::isRunning() +{ + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x00); +#else + Wire.send(0x00); +#endif + Wire.endTransmission(); + + // Just fetch the seconds register and check the top bit + Wire.requestFrom(DS1307_CTRL_ID, 1); +#if ARDUINO >= 100 + return !(Wire.read() & 0x80); +#else + return !(Wire.receive() & 0x80); +#endif +} + +void DS1307RTC::setCalibration(char calValue) +{ + unsigned char calReg = abs(calValue) & 0x1f; + if (calValue >= 0) calReg |= 0x20; // S bit is positive to speed up the clock + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x07); // Point to calibration register + Wire.write(calReg); +#else + Wire.send(0x07); // Point to calibration register + Wire.send(calReg); +#endif + Wire.endTransmission(); +} + +char DS1307RTC::getCalibration() +{ + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x07); +#else + Wire.send(0x07); +#endif + Wire.endTransmission(); + + Wire.requestFrom(DS1307_CTRL_ID, 1); +#if ARDUINO >= 100 + unsigned char calReg = Wire.read(); +#else + unsigned char calReg = Wire.receive(); +#endif + char out = calReg & 0x1f; + if (!(calReg & 0x20)) out = -out; // S bit clear means a negative value + return out; +} + +// PRIVATE FUNCTIONS + +// Convert Decimal to Binary Coded Decimal (BCD) +uint8_t DS1307RTC::dec2bcd(uint8_t num) +{ + return ((num/10 * 16) + (num % 10)); +} + +// Convert Binary Coded Decimal (BCD) to Decimal +uint8_t DS1307RTC::bcd2dec(uint8_t num) +{ + return ((num/16 * 10) + (num % 16)); +} + +bool DS1307RTC::exists = false; + +DS1307RTC RTC = DS1307RTC(); // create an instance for the user + diff --git a/wled00/src/dependencies/time/DS1307RTC.h b/wled00/src/dependencies/time/DS1307RTC.h new file mode 100644 index 000000000..2f3181632 --- /dev/null +++ b/wled00/src/dependencies/time/DS1307RTC.h @@ -0,0 +1,40 @@ +/* + * DS1307RTC.h - library for DS1307 RTC + * This library is intended to be uses with Arduino Time library functions + */ + +#ifndef DS1307RTC_h +#define DS1307RTC_h + +#include "TimeLib.h" + +// library interface description +class DS1307RTC +{ + // user-accessible "public" interface + public: + DS1307RTC(); + static time_t get(); + static bool set(time_t t); + static bool read(tmElements_t &tm); + static bool write(tmElements_t &tm); + static bool chipPresent() { return exists; } + static unsigned char isRunning(); + static void setCalibration(char calValue); + static char getCalibration(); + + private: + static bool exists; + static uint8_t dec2bcd(uint8_t num); + static uint8_t bcd2dec(uint8_t num); +}; + +#ifdef RTC +#undef RTC // workaround for Arduino Due, which defines "RTC"... +#endif + +extern DS1307RTC RTC; + +#endif + + diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 1835148fd..77af14139 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -61,6 +61,14 @@ #include "../usermods/multi_relay/usermod_multi_relay.h" #endif +#ifdef USERMOD_RTC +#include "../usermods/RTC/usermod_rtc.h" +#endif + +#ifdef USERMOD_ELEKSTUBE_IPS +#include "../usermods/EleksTube_IPS/usermod_elekstube_ips.h" +#endif + void registerUsermods() { /* @@ -118,4 +126,12 @@ void registerUsermods() #ifdef USERMOD_MULTI_RELAY usermods.add(new MultiRelay()); #endif + + #ifdef USERMOD_RTC + usermods.add(new RTCUsermod()); + #endif + + #ifdef USERMOD_ELEKSTUBE_IPS + usermods.add(new ElekstubeIPSUsermod()); + #endif } diff --git a/wled00/wled_math.h b/wled00/wled_math.h index 88d241797..8629ea096 100644 --- a/wled00/wled_math.h +++ b/wled00/wled_math.h @@ -11,6 +11,8 @@ #include //PI constant +//#define WLED_DEBUG_MATH + #define modd(x, y) ((x) - (int)((x) / (y)) * (y)) float cos_t(float x) @@ -24,38 +26,58 @@ float cos_t(float x) } float xx = x * x; - return sign * (1 - ((xx) / (2)) + ((xx * xx) / (24)) - ((xx * xx * xx) / (720)) + ((xx * xx * xx * xx) / (40320)) - ((xx * xx * xx * xx * xx) / (3628800)) + ((xx * xx * xx * xx * xx * xx) / (479001600))); + float res = sign * (1 - ((xx) / (2)) + ((xx * xx) / (24)) - ((xx * xx * xx) / (720)) + ((xx * xx * xx * xx) / (40320)) - ((xx * xx * xx * xx * xx) / (3628800)) + ((xx * xx * xx * xx * xx * xx) / (479001600))); + #ifdef WLED_DEBUG_MATH + Serial.printf("cos: %f,%f\n",res,cos(x)); + #endif + return res; } float sin_t(float x) { - return cos_t(HALF_PI - x); + float res = cos_t(HALF_PI - x); + #ifdef WLED_DEBUG_MATH + Serial.printf("sin: %f,%f\n",res,sin(x)); + #endif + return res; } float tan_t(float x) { float c = cos_t(x); if (c==0.0) return 0; - return sin_t(x) / c; + float res = sin_t(x) / c; + #ifdef WLED_DEBUG_MATH + Serial.printf("tan: %f,%f\n",res,tan(x)); + #endif + return res; } //https://stackoverflow.com/questions/3380628 // Absolute error <= 6.7e-5 float acos_t(float x) { float negate = float(x < 0); - x = std::abs(x); + float xabs = std::abs(x); float ret = -0.0187293; - ret = ret * x; + ret = ret * xabs; ret = ret + 0.0742610; - ret = ret * x; + ret = ret * xabs; ret = ret - 0.2121144; - ret = ret * x; + ret = ret * xabs; ret = ret + HALF_PI; - ret = ret * sqrt(1.0-x); + ret = ret * sqrt(1.0-xabs); ret = ret - 2 * negate * ret; - return negate * PI + ret; + float res = negate * PI + ret; + #ifdef WLED_DEBUG_MATH + Serial.printf("acos,%f,%f,%f\n",x,res,acos(x)); + #endif + return res; } float asin_t(float x) { - return HALF_PI - acos_t(x); + float res = HALF_PI - acos_t(x); + #ifdef WLED_DEBUG_MATH + Serial.printf("asin,%f,%f,%f\n",x,res,asin(x)); + #endif + return res; } //https://stackoverflow.com/a/42542593 @@ -63,21 +85,54 @@ float asin_t(float x) { #define B -0.287434475393028 #define C ((HALF_PI/2) - A - B) +//polynominal factors for approximation between 1 and 5 +#define C0 0.089494f +#define C1 0.974207f +#define C2 -0.326175f +#define C3 0.05375f +#define C4 -0.003445f + float atan_t(float x) { - float xx = x * x; - return ((A*xx + B)*xx + C)*x; + bool neg = (x < 0); + #ifdef WLED_DEBUG_MATH + float xinput = x; + #endif + x = std::abs(x); + float res; + if (x > 5.0f) { //atan(x) converges to pi/2 - (1/x) for large values + res = HALF_PI - (1.0f/x); + } + else if (x > 1.0f) { //1 < x < 5 + float xx = x * x; + res = (C4*xx*xx)+(C3*xx*x)+(C2*xx)+(C1*x)+C0; + } else { //this approximation is only for x <= 1 + float xx = x * x; + res = ((A*xx + B)*xx + C)*x; + } + if (neg) res = -res; + #ifdef WLED_DEBUG_MATH + Serial.printf("atan,%f,%f,%f\n",xinput,res,atan(xinput)); + #endif + return res; } float floor_t(float x) { bool neg = x < 0; int val = x; if (neg) val--; + #ifdef WLED_DEBUG_MATH + Serial.printf("floor: %f,%f\n",val,floor(x)); + #endif return val; } float fmod_t(float num, float denom) { int tquot = num / denom; - return num - tquot * denom; + float res = num - tquot * denom; + #ifdef WLED_DEBUG_MATH + Serial.printf("fmod: %f,%f\n",res,fmod(num,denom)); + #endif + return res; } #endif \ No newline at end of file