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}
|
||||
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
|
||||
|
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
|
||||
|
||||
#ifndef PIXEL_COUNTS
|
||||
#define PIXEL_COUNTS 30
|
||||
#define PIXEL_COUNTS DEFAULT_LED_COUNT
|
||||
#endif
|
||||
|
||||
#ifndef DATA_PINS
|
||||
|
@ -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"
|
||||
|
||||
|
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"
|
||||
#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
|
||||
}
|
||||
|