Merge pull request #1979 from Aircoookie/elekstube_usermod
Elekstube IPS and RTC usermods
@ -473,3 +473,37 @@ platform_packages = ${common.platform_packages}
|
|||||||
board_build.ldscript = ${common.ldscript_4m1m}
|
board_build.ldscript = ${common.ldscript_4m1m}
|
||||||
build_unflags = ${common.build_unflags}
|
build_unflags = ${common.build_unflags}
|
||||||
build_flags = ${common.build_flags_esp8266}
|
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
|
||||||
|
70
usermods/EleksTube_IPS/ChipSelect.h
Normal file
@ -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
|
52
usermods/EleksTube_IPS/Hardware.h
Normal file
@ -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
|
169
usermods/EleksTube_IPS/TFTs.h
Normal file
@ -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) {
|
||||||
|
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
|
47
usermods/EleksTube_IPS/User_Setup.h
Normal file
@ -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
|
BIN
usermods/EleksTube_IPS/bmp/0.bmp
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
usermods/EleksTube_IPS/bmp/1.bmp
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
usermods/EleksTube_IPS/bmp/2.bmp
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
usermods/EleksTube_IPS/bmp/3.bmp
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
usermods/EleksTube_IPS/bmp/4.bmp
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
usermods/EleksTube_IPS/bmp/5.bmp
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
usermods/EleksTube_IPS/bmp/6.bmp
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
usermods/EleksTube_IPS/bmp/7.bmp
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
usermods/EleksTube_IPS/bmp/8.bmp
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
usermods/EleksTube_IPS/bmp/9.bmp
Normal file
After Width: | Height: | Size: 84 KiB |
21
usermods/EleksTube_IPS/readme.md
Normal file
@ -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.
|
50
usermods/EleksTube_IPS/usermod_elekstube_ips.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
8
usermods/RTC/readme.md
Normal file
@ -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.
|
37
usermods/RTC/usermod_rtc.h
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
};
|
@ -49,7 +49,7 @@
|
|||||||
//#define DEFAULT_LED_TYPE TYPE_WS2812_RGB
|
//#define DEFAULT_LED_TYPE TYPE_WS2812_RGB
|
||||||
|
|
||||||
#ifndef PIXEL_COUNTS
|
#ifndef PIXEL_COUNTS
|
||||||
#define PIXEL_COUNTS 30
|
#define PIXEL_COUNTS DEFAULT_LED_COUNT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DATA_PINS
|
#ifndef DATA_PINS
|
||||||
|
@ -45,6 +45,8 @@
|
|||||||
#define USERMOD_ID_DHT 10 //Usermod "usermod_dht.h"
|
#define USERMOD_ID_DHT 10 //Usermod "usermod_dht.h"
|
||||||
#define USERMOD_ID_MODE_SORT 11 //Usermod "usermod_v2_mode_sort.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_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_MULTI_RELAY 101 //Usermod "usermod_multi_relay.h"
|
||||||
#define USERMOD_ID_ANIMATED_STAIRCASE 102 //Usermod "Animated_Staircase.h"
|
#define USERMOD_ID_ANIMATED_STAIRCASE 102 //Usermod "Animated_Staircase.h"
|
||||||
|
|
||||||
|
218
wled00/src/dependencies/time/DS1307RTC.cpp
Normal file
@ -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
|
||||||
|
|
40
wled00/src/dependencies/time/DS1307RTC.h
Normal file
@ -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"
|
#include "../usermods/multi_relay/usermod_multi_relay.h"
|
||||||
#endif
|
#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()
|
void registerUsermods()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -118,4 +126,12 @@ void registerUsermods()
|
|||||||
#ifdef USERMOD_MULTI_RELAY
|
#ifdef USERMOD_MULTI_RELAY
|
||||||
usermods.add(new MultiRelay());
|
usermods.add(new MultiRelay());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USERMOD_RTC
|
||||||
|
usermods.add(new RTCUsermod());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USERMOD_ELEKSTUBE_IPS
|
||||||
|
usermods.add(new ElekstubeIPSUsermod());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|