diff --git a/platformio.ini b/platformio.ini index f57c4cc73..d5c936f18 100644 --- a/platformio.ini +++ b/platformio.ini @@ -473,3 +473,37 @@ platform_packages = ${common.platform_packages} board_build.ldscript = ${common.ldscript_4m1m} build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp8266} + +# ------------------------------------------------------------------------------ +# EleksTube-IPS +# ------------------------------------------------------------------------------ +[env:elekstube_ips] +board = esp32dev +platform = espressif32@3.2 +upload_speed = 921600 +lib_deps = ${env.lib_deps} + TFT_eSPI +build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED + -D USERMOD_RTC + -D USERMOD_ELEKSTUBE_IPS + -D LEDPIN=12 + -D RLYPIN=27 + -D BTNPIN=34 + -D WLED_DISABLE_INFRARED + -D DEFAULT_LED_COUNT=6 + # Display config + -D ST7789_DRIVER + -D TFT_WIDTH=135 + -D TFT_HEIGHT=240 + -D CGRAM_OFFSET + -D TFT_SDA_READ + -D TFT_MOSI=23 + -D TFT_SCLK=18 + -D TFT_DC=25 + -D TFT_RST=26 + -D SPI_FREQUENCY=40000000 + -D USER_SETUP_LOADED +monitor_filters = esp32_exception_decoder +lib_ignore = + ESPAsyncTCP + ESPAsyncUDP diff --git a/usermods/EleksTube_IPS/ChipSelect.h b/usermods/EleksTube_IPS/ChipSelect.h new file mode 100644 index 000000000..c58b28549 --- /dev/null +++ b/usermods/EleksTube_IPS/ChipSelect.h @@ -0,0 +1,70 @@ +#ifndef CHIP_SELECT_H +#define CHIP_SELECT_H + +#include "Hardware.h" + +/* + * `digit`s are as defined in Hardware.h, 0 == seconds ones, 5 == hours tens. + */ + +class ChipSelect { +private: + uint8_t digits_map; + const uint8_t all_on = 0x3F; + const uint8_t all_off = 0x00; +public: + ChipSelect() : digits_map(all_off) {} + + void update() { + // Documented in README.md. Q7 and Q6 are unused. Q5 is Seconds Ones, Q0 is Hours Tens. + // Q7 is the first bit written, Q0 is the last. So we push two dummy bits, then start with + // Seconds Ones and end with Hours Tens. + // CS is Active Low, but digits_map is 1 for enable, 0 for disable. So we bit-wise NOT first. + + uint8_t to_shift = (~digits_map) << 2; + + digitalWrite(CSSR_LATCH_PIN, LOW); + shiftOut(CSSR_DATA_PIN, CSSR_CLOCK_PIN, LSBFIRST, to_shift); + digitalWrite(CSSR_LATCH_PIN, HIGH); + } + + void begin() + { + pinMode(CSSR_LATCH_PIN, OUTPUT); + pinMode(CSSR_DATA_PIN, OUTPUT); + pinMode(CSSR_CLOCK_PIN, OUTPUT); + + digitalWrite(CSSR_DATA_PIN, LOW); + digitalWrite(CSSR_CLOCK_PIN, LOW); + digitalWrite(CSSR_LATCH_PIN, LOW); + update(); + } + + // These speak the indexes defined in Hardware.h. + // So 0 is disabled, 1 is enabled (even though CS is active low, this gets mapped.) + // So bit 0 (LSB), is index 0, is SECONDS_ONES + // Translation to what the 74HC595 uses is done in update() + void setDigitMap(uint8_t map, bool update_=true) { digits_map = map; if (update_) update(); } + uint8_t getDigitMap() { return digits_map; } + + // Helper functions + // Sets just the one digit by digit number + void setDigit(uint8_t digit, bool update_=true) { setDigitMap(0x01 << digit, update_); } + void setAll(bool update_=true) { setDigitMap(all_on, update_); } + void clear(bool update_=true) { setDigitMap(all_off, update_); } + void setSecondsOnes() { setDigit(SECONDS_ONES); } + void setSecondsTens() { setDigit(SECONDS_TENS); } + void setMinutesOnes() { setDigit(MINUTES_ONES); } + void setMinutesTens() { setDigit(MINUTES_TENS); } + void setHoursOnes() { setDigit(HOURS_ONES); } + void setHoursTens() { setDigit(HOURS_TENS); } + bool isSecondsOnes() { return (digits_map&SECONDS_ONES_MAP > 0); } + bool isSecondsTens() { return (digits_map&SECONDS_TENS_MAP > 0); } + bool isMinutesOnes() { return (digits_map&MINUTES_ONES_MAP > 0); } + bool isMinutesTens() { return (digits_map&MINUTES_TENS_MAP > 0); } + bool isHoursOnes() { return (digits_map&HOURS_ONES_MAP > 0); } + bool isHoursTens() { return (digits_map&HOURS_TENS_MAP > 0); } +}; + + +#endif // CHIP_SELECT_H diff --git a/usermods/EleksTube_IPS/Hardware.h b/usermods/EleksTube_IPS/Hardware.h new file mode 100644 index 000000000..e4f793053 --- /dev/null +++ b/usermods/EleksTube_IPS/Hardware.h @@ -0,0 +1,52 @@ +/* + * Define the hardware for the EleksTube IPS clock. Mostly pin definitions + */ +#ifndef ELEKSTUBEHAX_HARDWARE_H +#define ELEKSTUBEHAX_HARDWARE_H + +#include +#include // for HIGH and LOW + +// Common indexing scheme, used to identify the digit +#define SECONDS_ONES (0) +#define SECONDS_TENS (1) +#define MINUTES_ONES (2) +#define MINUTES_TENS (3) +#define HOURS_ONES (4) +#define HOURS_TENS (5) +#define NUM_DIGITS (6) + +#define SECONDS_ONES_MAP (0x01 << SECONDS_ONES) +#define SECONDS_TENS_MAP (0x01 << SECONDS_TENS) +#define MINUTES_ONES_MAP (0x01 << MINUTES_ONES) +#define MINUTES_TENS_MAP (0x01 << MINUTES_TENS) +#define HOURS_ONES_MAP (0x01 << HOURS_ONES) +#define HOURS_TENS_MAP (0x01 << HOURS_TENS) + +// WS2812 (or compatible) LEDs on the back of the display modules. +#define BACKLIGHTS_PIN (12) + +// Buttons, active low, externally pulled up (with actual resistors!) +#define BUTTON_LEFT_PIN (33) +#define BUTTON_MODE_PIN (32) +#define BUTTON_RIGHT_PIN (35) +#define BUTTON_POWER_PIN (34) + +// I2C to DS3231 RTC. +#define RTC_SCL_PIN (22) +#define RTC_SDA_PIN (21) + +// Chip Select shift register, to select the display +#define CSSR_DATA_PIN (14) +#define CSSR_CLOCK_PIN (16) +#define CSSR_LATCH_PIN (17) + +// SPI to displays +// DEFINED IN User_Setup.h +// Look for: TFT_MOSI, TFT_SCLK, TFT_CS, TFT_DC, and TFT_RST + +// Power for all TFT displays are grounded through a MOSFET so they can all be turned off. +// Active HIGH. +#define TFT_ENABLE_PIN (27) + +#endif // ELEKSTUBEHAX_HARDWARE_H diff --git a/usermods/EleksTube_IPS/TFTs.h b/usermods/EleksTube_IPS/TFTs.h new file mode 100644 index 000000000..c0c35a796 --- /dev/null +++ b/usermods/EleksTube_IPS/TFTs.h @@ -0,0 +1,169 @@ +#ifndef TFTS_H +#define TFTS_H + +#include + +#include +#include "Hardware.h" +#include "ChipSelect.h" + +class TFTs : public TFT_eSPI { +private: + uint8_t digits[NUM_DIGITS]; + + // These read 16- and 32-bit types from the SD card file. + // BMP data is stored little-endian, Arduino is little-endian too. + // May need to reverse subscript order if porting elsewhere. + + uint16_t read16(fs::File &f) { + uint16_t result; + ((uint8_t *)&result)[0] = f.read(); // LSB + ((uint8_t *)&result)[1] = f.read(); // MSB + return result; + } + + uint32_t read32(fs::File &f) { + uint32_t result; + ((uint8_t *)&result)[0] = f.read(); // LSB + ((uint8_t *)&result)[1] = f.read(); + ((uint8_t *)&result)[2] = f.read(); + ((uint8_t *)&result)[3] = f.read(); // MSB + return result; + } + + uint16_t output_buffer[TFT_HEIGHT][TFT_WIDTH]; + + // These BMP functions are stolen directly from the TFT_SPIFFS_BMP example in the TFT_eSPI library. + // Unfortunately, they aren't part of the library itself, so I had to copy them. + // I've modified drawBmp to buffer the whole image at once instead of doing it line-by-line. + + //// BEGIN STOLEN CODE + + bool drawBmp(const char *filename, 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 diff --git a/usermods/EleksTube_IPS/User_Setup.h b/usermods/EleksTube_IPS/User_Setup.h new file mode 100644 index 000000000..b4b2edab0 --- /dev/null +++ b/usermods/EleksTube_IPS/User_Setup.h @@ -0,0 +1,47 @@ +/* + * This is intended to over-ride `User_Setup.h` that comes with the TFT_eSPI library. + * I hate having to modify the library code. + */ + +// ST7789 135 x 240 display with no chip select line + +#define ST7789_DRIVER // Configure all registers + +#define TFT_WIDTH 135 +#define TFT_HEIGHT 240 + +#define CGRAM_OFFSET // Library will add offsets required + +//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue +//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red + +//#define TFT_INVERSION_ON +//#define TFT_INVERSION_OFF + +// EleksTube IPS +#define TFT_SDA_READ // Read and write on the MOSI/SDA pin, no separate MISO pin +#define TFT_MOSI 23 +#define TFT_SCLK 18 +//#define TFT_CS -1 // Not connected +#define TFT_DC 25 // Data Command, aka Register Select or RS +#define TFT_RST 26 // Connect reset to ensure display initialises + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +//#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +//#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +//#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +//#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +//#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +//#define SMOOTH_FONT + + +//#define SPI_FREQUENCY 27000000 +#define SPI_FREQUENCY 40000000 + +/* + * To make the Library not over-write all this: + */ +#define USER_SETUP_LOADED diff --git a/usermods/EleksTube_IPS/bmp/0.bmp b/usermods/EleksTube_IPS/bmp/0.bmp new file mode 100644 index 000000000..cd6e6ff57 Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/0.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/1.bmp b/usermods/EleksTube_IPS/bmp/1.bmp new file mode 100644 index 000000000..b1dbefc7a Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/1.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/2.bmp b/usermods/EleksTube_IPS/bmp/2.bmp new file mode 100644 index 000000000..a35e5e293 Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/2.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/3.bmp b/usermods/EleksTube_IPS/bmp/3.bmp new file mode 100644 index 000000000..a7e1f99ed Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/3.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/4.bmp b/usermods/EleksTube_IPS/bmp/4.bmp new file mode 100644 index 000000000..549138f7d Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/4.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/5.bmp b/usermods/EleksTube_IPS/bmp/5.bmp new file mode 100644 index 000000000..fd2e05e5b Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/5.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/6.bmp b/usermods/EleksTube_IPS/bmp/6.bmp new file mode 100644 index 000000000..a74b77ffc Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/6.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/7.bmp b/usermods/EleksTube_IPS/bmp/7.bmp new file mode 100644 index 000000000..f97944206 Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/7.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/8.bmp b/usermods/EleksTube_IPS/bmp/8.bmp new file mode 100644 index 000000000..49a2325c4 Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/8.bmp differ diff --git a/usermods/EleksTube_IPS/bmp/9.bmp b/usermods/EleksTube_IPS/bmp/9.bmp new file mode 100644 index 000000000..6251afc1d Binary files /dev/null and b/usermods/EleksTube_IPS/bmp/9.bmp differ diff --git a/usermods/EleksTube_IPS/readme.md b/usermods/EleksTube_IPS/readme.md new file mode 100644 index 000000000..ee9c212dc --- /dev/null +++ b/usermods/EleksTube_IPS/readme.md @@ -0,0 +1,21 @@ +# EleksTube IPS Clock usermod + +This usermod allows WLED to run on the EleksTube IPS clock. +It enables running all WLED effects on the background SK6812 lighting, while displaying digit bitmaps on the 6 IPS screens. +Code is largely based on https://github.com/SmittyHalibut/EleksTubeHAX by Mark Smith + +Supported: +- Display with custom bitmaps from filesystem +- Background lighting +- Power button +- RTC (with RTC usermod) +- Standard WLED time features (NTP, DST, timezones) + +Not supported: +- 3 navigation buttons, on-device setup + +## Installation + +Compile and upload to clock using the `elekstube_ips` PlatformIO environment +Once uploaded (the clock can be flashed like any ESP32 module), go to `[WLED-IP]/edit` and upload the 0-9.bmp files from the bmp folder. +Use LED pin 12, relay pin 27 and button pin 34. \ No newline at end of file diff --git a/usermods/EleksTube_IPS/usermod_elekstube_ips.h b/usermods/EleksTube_IPS/usermod_elekstube_ips.h new file mode 100644 index 000000000..afd0e9efc --- /dev/null +++ b/usermods/EleksTube_IPS/usermod_elekstube_ips.h @@ -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(""); + } + + void loop() { + if (lastTime == 0) { + tfts.fillScreen(TFT_BLACK); + updateClockDisplay(TFTs::force); + } + if (millis() - lastTime > 100) { + updateClockDisplay(); + lastTime = millis(); + } + } + + uint16_t getId() + { + return USERMOD_ID_ELEKSTUBE_IPS; + } +}; \ No newline at end of file diff --git a/usermods/RTC/readme.md b/usermods/RTC/readme.md new file mode 100644 index 000000000..3aaa60911 --- /dev/null +++ b/usermods/RTC/readme.md @@ -0,0 +1,8 @@ +# DS1307/DS3231 Real time clock + +Gets the time from I2C RTC module on boot. This allows clocks to operate e.g. if temporarily no WiFi is available. +The stored time is updated each time NTP is synced. + +## Installation + +Add the build flag `-D USERMOD_RTC` to your platformio environment. diff --git a/usermods/RTC/usermod_rtc.h b/usermods/RTC/usermod_rtc.h new file mode 100644 index 000000000..bf0047a72 --- /dev/null +++ b/usermods/RTC/usermod_rtc.h @@ -0,0 +1,37 @@ +#pragma once + +#include "src/dependencies/time/DS1307RTC.h" +#include "wled.h" + +//Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL)) + +class RTCUsermod : public Usermod { + private: + unsigned long lastTime = 0; + bool disabled = false; + public: + + void setup() { + time_t rtcTime = RTC.get(); + if (rtcTime) { + setTime(rtcTime); + updateLocalTime(); + } else { + if (!RTC.chipPresent()) disabled = true; //don't waste time if H/W error + } + } + + void loop() { + if (!disabled && millis() - lastTime > 500) { + time_t t = now(); + if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value + + lastTime = millis(); + } + } + + uint16_t getId() + { + return USERMOD_ID_RTC; + } +}; \ No newline at end of file diff --git a/wled00/const.h b/wled00/const.h index 8acb57a75..fc555092a 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -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" diff --git a/wled00/src/dependencies/time/DS1307RTC.cpp b/wled00/src/dependencies/time/DS1307RTC.cpp new file mode 100644 index 000000000..2c16a8a33 --- /dev/null +++ b/wled00/src/dependencies/time/DS1307RTC.cpp @@ -0,0 +1,218 @@ +/* + * DS1307RTC.h - library for DS1307 RTC + + Copyright (c) Michael Margolis 2009 + This library is intended to be uses with Arduino Time library functions + + The library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + 30 Dec 2009 - Initial release + 5 Sep 2011 updated for Arduino 1.0 + */ + + +#if defined (__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || (__AVR_ATtiny2313__) +#include +#define Wire TinyWireM +#else +#include +#endif +#include "DS1307RTC.h" + +#define DS1307_CTRL_ID 0x68 + +DS1307RTC::DS1307RTC() +{ + Wire.begin(); +} + +// PUBLIC FUNCTIONS +time_t DS1307RTC::get() // Aquire data from buffer and convert to time_t +{ + tmElements_t tm; + if (read(tm) == false) return 0; + return(makeTime(tm)); +} + +bool DS1307RTC::set(time_t t) +{ + tmElements_t tm; + breakTime(t, tm); + return write(tm); +} + +// Aquire data from the RTC chip in BCD format +bool DS1307RTC::read(tmElements_t &tm) +{ + uint8_t sec; + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x00); +#else + Wire.send(0x00); +#endif + if (Wire.endTransmission() != 0) { + exists = false; + return false; + } + exists = true; + + // request the 7 data fields (secs, min, hr, dow, date, mth, yr) + Wire.requestFrom(DS1307_CTRL_ID, tmNbrFields); + if (Wire.available() < tmNbrFields) return false; +#if ARDUINO >= 100 + sec = Wire.read(); + tm.Second = bcd2dec(sec & 0x7f); + tm.Minute = bcd2dec(Wire.read() ); + tm.Hour = bcd2dec(Wire.read() & 0x3f); // mask assumes 24hr clock + tm.Wday = bcd2dec(Wire.read() ); + tm.Day = bcd2dec(Wire.read() ); + tm.Month = bcd2dec(Wire.read() ); + tm.Year = y2kYearToTm((bcd2dec(Wire.read()))); +#else + sec = Wire.receive(); + tm.Second = bcd2dec(sec & 0x7f); + tm.Minute = bcd2dec(Wire.receive() ); + tm.Hour = bcd2dec(Wire.receive() & 0x3f); // mask assumes 24hr clock + tm.Wday = bcd2dec(Wire.receive() ); + tm.Day = bcd2dec(Wire.receive() ); + tm.Month = bcd2dec(Wire.receive() ); + tm.Year = y2kYearToTm((bcd2dec(Wire.receive()))); +#endif + if (sec & 0x80) return false; // clock is halted + return true; +} + +bool DS1307RTC::write(tmElements_t &tm) +{ + // To eliminate any potential race conditions, + // stop the clock before writing the values, + // then restart it after. + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x00); // reset register pointer + Wire.write((uint8_t)0x80); // Stop the clock. The seconds will be written last + Wire.write(dec2bcd(tm.Minute)); + Wire.write(dec2bcd(tm.Hour)); // sets 24 hour format + Wire.write(dec2bcd(tm.Wday)); + Wire.write(dec2bcd(tm.Day)); + Wire.write(dec2bcd(tm.Month)); + Wire.write(dec2bcd(tmYearToY2k(tm.Year))); +#else + Wire.send(0x00); // reset register pointer + Wire.send(0x80); // Stop the clock. The seconds will be written last + Wire.send(dec2bcd(tm.Minute)); + Wire.send(dec2bcd(tm.Hour)); // sets 24 hour format + Wire.send(dec2bcd(tm.Wday)); + Wire.send(dec2bcd(tm.Day)); + Wire.send(dec2bcd(tm.Month)); + Wire.send(dec2bcd(tmYearToY2k(tm.Year))); +#endif + if (Wire.endTransmission() != 0) { + exists = false; + return false; + } + exists = true; + + // Now go back and set the seconds, starting the clock back up as a side effect + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x00); // reset register pointer + Wire.write(dec2bcd(tm.Second)); // write the seconds, with the stop bit clear to restart +#else + Wire.send(0x00); // reset register pointer + Wire.send(dec2bcd(tm.Second)); // write the seconds, with the stop bit clear to restart +#endif + if (Wire.endTransmission() != 0) { + exists = false; + return false; + } + exists = true; + return true; +} + +unsigned char DS1307RTC::isRunning() +{ + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x00); +#else + Wire.send(0x00); +#endif + Wire.endTransmission(); + + // Just fetch the seconds register and check the top bit + Wire.requestFrom(DS1307_CTRL_ID, 1); +#if ARDUINO >= 100 + return !(Wire.read() & 0x80); +#else + return !(Wire.receive() & 0x80); +#endif +} + +void DS1307RTC::setCalibration(char calValue) +{ + unsigned char calReg = abs(calValue) & 0x1f; + if (calValue >= 0) calReg |= 0x20; // S bit is positive to speed up the clock + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x07); // Point to calibration register + Wire.write(calReg); +#else + Wire.send(0x07); // Point to calibration register + Wire.send(calReg); +#endif + Wire.endTransmission(); +} + +char DS1307RTC::getCalibration() +{ + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x07); +#else + Wire.send(0x07); +#endif + Wire.endTransmission(); + + Wire.requestFrom(DS1307_CTRL_ID, 1); +#if ARDUINO >= 100 + unsigned char calReg = Wire.read(); +#else + unsigned char calReg = Wire.receive(); +#endif + char out = calReg & 0x1f; + if (!(calReg & 0x20)) out = -out; // S bit clear means a negative value + return out; +} + +// PRIVATE FUNCTIONS + +// Convert Decimal to Binary Coded Decimal (BCD) +uint8_t DS1307RTC::dec2bcd(uint8_t num) +{ + return ((num/10 * 16) + (num % 10)); +} + +// Convert Binary Coded Decimal (BCD) to Decimal +uint8_t DS1307RTC::bcd2dec(uint8_t num) +{ + return ((num/16 * 10) + (num % 16)); +} + +bool DS1307RTC::exists = false; + +DS1307RTC RTC = DS1307RTC(); // create an instance for the user + diff --git a/wled00/src/dependencies/time/DS1307RTC.h b/wled00/src/dependencies/time/DS1307RTC.h new file mode 100644 index 000000000..2f3181632 --- /dev/null +++ b/wled00/src/dependencies/time/DS1307RTC.h @@ -0,0 +1,40 @@ +/* + * DS1307RTC.h - library for DS1307 RTC + * This library is intended to be uses with Arduino Time library functions + */ + +#ifndef DS1307RTC_h +#define DS1307RTC_h + +#include "TimeLib.h" + +// library interface description +class DS1307RTC +{ + // user-accessible "public" interface + public: + DS1307RTC(); + static time_t get(); + static bool set(time_t t); + static bool read(tmElements_t &tm); + static bool write(tmElements_t &tm); + static bool chipPresent() { return exists; } + static unsigned char isRunning(); + static void setCalibration(char calValue); + static char getCalibration(); + + private: + static bool exists; + static uint8_t dec2bcd(uint8_t num); + static uint8_t bcd2dec(uint8_t num); +}; + +#ifdef RTC +#undef RTC // workaround for Arduino Due, which defines "RTC"... +#endif + +extern DS1307RTC RTC; + +#endif + + diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 1835148fd..77af14139 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -61,6 +61,14 @@ #include "../usermods/multi_relay/usermod_multi_relay.h" #endif +#ifdef USERMOD_RTC +#include "../usermods/RTC/usermod_rtc.h" +#endif + +#ifdef USERMOD_ELEKSTUBE_IPS +#include "../usermods/EleksTube_IPS/usermod_elekstube_ips.h" +#endif + void registerUsermods() { /* @@ -118,4 +126,12 @@ void registerUsermods() #ifdef USERMOD_MULTI_RELAY usermods.add(new MultiRelay()); #endif + + #ifdef USERMOD_RTC + usermods.add(new RTCUsermod()); + #endif + + #ifdef USERMOD_ELEKSTUBE_IPS + usermods.add(new ElekstubeIPSUsermod()); + #endif }