First working Elekstube IPS and RTC mod
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,52 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Define the hardware for the EleksTube IPS clock.  Mostly pin definitions
 | 
			
		||||
 */
 | 
			
		||||
#ifndef ELEKSTUBEHAX_HARDWARE_H
 | 
			
		||||
#define ELEKSTUBEHAX_HARDWARE_H
 | 
			
		||||
 | 
			
		||||
#include <stdint.h> 
 | 
			
		||||
#include <Arduino.h> // 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
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,169 @@
 | 
			
		|||
#ifndef TFTS_H
 | 
			
		||||
#define TFTS_H
 | 
			
		||||
 | 
			
		||||
#include <FS.h>
 | 
			
		||||
 | 
			
		||||
#include <TFT_eSPI.h>
 | 
			
		||||
#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, int16_t x, int16_t y) {
 | 
			
		||||
    // Nothing to do.
 | 
			
		||||
    if ((x >= width()) || (y >= height())) return(true);
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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(x, 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, 0, 0);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
		 Po Szerokość: | Wysokość: | Rozmiar: 96 KiB  | 
| 
		 Po Szerokość: | Wysokość: | Rozmiar: 96 KiB  | 
| 
		 Po Szerokość: | Wysokość: | Rozmiar: 96 KiB  | 
| 
		 Po Szerokość: | Wysokość: | Rozmiar: 96 KiB  | 
| 
		 Po Szerokość: | Wysokość: | Rozmiar: 96 KiB  | 
| 
		 Po Szerokość: | Wysokość: | Rozmiar: 96 KiB  | 
| 
		 Po Szerokość: | Wysokość: | Rozmiar: 96 KiB  | 
| 
		 Po Szerokość: | Wysokość: | Rozmiar: 96 KiB  | 
| 
		 Po Szerokość: | Wysokość: | Rozmiar: 96 KiB  | 
| 
		 Po Szerokość: | Wysokość: | Rozmiar: 96 KiB  | 
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "TFTs.h"
 | 
			
		||||
#include "wled.h"
 | 
			
		||||
 | 
			
		||||
class ElekstubeIPSUsermod : public Usermod {
 | 
			
		||||
  private:
 | 
			
		||||
    TFTs tfts;
 | 
			
		||||
    void updateClockDisplay(TFTs::show_t show=TFTs::yes) {
 | 
			
		||||
      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;
 | 
			
		||||
      tfts.setDigit(HOURS_TENS, hrTens, show);
 | 
			
		||||
      tfts.setDigit(HOURS_ONES, hr - hrTens*10, show);
 | 
			
		||||
      tfts.setDigit(MINUTES_TENS, mittens, show);
 | 
			
		||||
      tfts.setDigit(MINUTES_ONES, mi - mittens*10, show);
 | 
			
		||||
      tfts.setDigit(SECONDS_TENS, sTens, show);
 | 
			
		||||
      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("<STARTUP>");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +45,8 @@
 | 
			
		|||
#define USERMOD_ID_DHT           10            //Usermod "usermod_dht.h"
 | 
			
		||||
#define USERMOD_ID_MODE_SORT     11            //Usermod "usermod_v2_mode_sort.h"
 | 
			
		||||
#define USERMOD_ID_VL53L0X       12            //Usermod "usermod_vl53l0x_gestures.h"
 | 
			
		||||
#define USERMOD_ID_RTC           13            //Usermod "usermod_rtc.h"
 | 
			
		||||
#define USERMOD_ID_ELEKSTUBE_IPS 14            //Usermod "usermod_elekstube_ips.h"
 | 
			
		||||
#define USERMOD_ID_MULTI_RELAY  101            //Usermod "usermod_multi_relay.h"
 | 
			
		||||
#define USERMOD_ID_ANIMATED_STAIRCASE 102      //Usermod "Animated_Staircase.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 <TinyWireM.h>
 | 
			
		||||
#define Wire TinyWireM
 | 
			
		||||
#else
 | 
			
		||||
#include <Wire.h>
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||