Merge branch 'master' into multi-button
@ -2,6 +2,11 @@
|
||||
|
||||
### Builds after release 0.12.0
|
||||
|
||||
#### Build 2105230
|
||||
|
||||
- No longer retain MQTT `/v` topic to alleviate storage loads on MQTT broker
|
||||
- Fixed Sunrise calculation (atan_t approx. used outside of value range)
|
||||
|
||||
#### Build 2105200
|
||||
|
||||
- Fixed WS281x output on ESP32
|
||||
|
@ -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.
|
63
usermods/EleksTube_IPS/usermod_elekstube_ips.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include "TFTs.h"
|
||||
#include "wled.h"
|
||||
|
||||
//Large parts of the code are from https://github.com/SmittyHalibut/EleksTubeHAX
|
||||
|
||||
class ElekstubeIPSUsermod : public Usermod {
|
||||
private:
|
||||
TFTs tfts;
|
||||
void updateClockDisplay(TFTs::show_t show=TFTs::yes) {
|
||||
bool set[6] = {false};
|
||||
for (uint8_t i = 0; i<6; i++) {
|
||||
char c = cronixieDisplay[i];
|
||||
if (c >= '0' && c <= '9') {
|
||||
tfts.setDigit(5-i, c - '0', show); set[i] = true;
|
||||
} else if (c >= 'A' && c <= 'G') {
|
||||
tfts.setDigit(5-i, c - 'A' + 10, show); set[i] = true; //10.bmp to 16.bmp static display
|
||||
} else if (c == '-' || c == '_' || c == ' ') {
|
||||
tfts.setDigit(5-i, 255, show); set[i] = true; //blank
|
||||
} else {
|
||||
set[i] = false; //display HHMMSS time
|
||||
}
|
||||
}
|
||||
uint8_t hr = hour(localTime);
|
||||
uint8_t hrTens = hr/10;
|
||||
uint8_t mi = minute(localTime);
|
||||
uint8_t mittens = mi/10;
|
||||
uint8_t s = second(localTime);
|
||||
uint8_t sTens = s/10;
|
||||
if (!set[0]) tfts.setDigit(HOURS_TENS, hrTens, show);
|
||||
if (!set[1]) tfts.setDigit(HOURS_ONES, hr - hrTens*10, show);
|
||||
if (!set[2]) tfts.setDigit(MINUTES_TENS, mittens, show);
|
||||
if (!set[3]) tfts.setDigit(MINUTES_ONES, mi - mittens*10, show);
|
||||
if (!set[4]) tfts.setDigit(SECONDS_TENS, sTens, show);
|
||||
if (!set[5]) tfts.setDigit(SECONDS_ONES, s - sTens*10, show);
|
||||
}
|
||||
unsigned long lastTime = 0;
|
||||
public:
|
||||
|
||||
void setup() {
|
||||
tfts.begin();
|
||||
tfts.fillScreen(TFT_BLACK);
|
||||
tfts.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
tfts.setCursor(0, 0, 2);
|
||||
tfts.println("<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;
|
||||
}
|
||||
};
|
@ -9,7 +9,7 @@ build_unflags = ${common.build_unflags}
|
||||
build_flags =
|
||||
${common.build_flags_esp32}
|
||||
-D USERMOD_MODE_SORT
|
||||
-D USERMOD_FOUR_LINE_DISLAY -D FLD_PIN_SCL=22 -D FLD_PIN_SDA=21
|
||||
-D USERMOD_FOUR_LINE_DISPLAY -D FLD_PIN_SCL=22 -D FLD_PIN_SDA=21
|
||||
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=18 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=19
|
||||
-D USERMOD_AUTO_SAVE -D AUTOSAVE_PRESET_NUM=1
|
||||
-D LEDPIN=16 -D BTNPIN=13
|
||||
@ -28,7 +28,7 @@ build_unflags = ${common.build_unflags}
|
||||
build_flags =
|
||||
${common.build_flags_esp8266}
|
||||
-D USERMOD_MODE_SORT
|
||||
-D USERMOD_FOUR_LINE_DISLAY -D FLD_PIN_SCL=5 -D FLD_PIN_SDA=4
|
||||
-D USERMOD_FOUR_LINE_DISPLAY -D FLD_PIN_SCL=5 -D FLD_PIN_SDA=4
|
||||
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=12 -D ENCODER_CLK_PIN=14 -D ENCODER_SW_PIN=13
|
||||
-D USERMOD_AUTO_SAVE -D AUTOSAVE_PRESET_NUM=1
|
||||
-D LEDPIN=3 -D BTNPIN=0
|
||||
|
@ -16,7 +16,7 @@ This file should be placed in the same directory as `platformio.ini`.
|
||||
### Define Your Options
|
||||
|
||||
* `USERMOD_ROTARY_ENCODER_UI` - define this to have this user mod included wled00\usermods_list.cpp
|
||||
* `USERMOD_FOUR_LINE_DISLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp - also tells this usermod that the display is available (see the Four Line Display usermod `readme.md` for more details)
|
||||
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp - also tells this usermod that the display is available (see the Four Line Display usermod `readme.md` for more details)
|
||||
* `ENCODER_DT_PIN` - The encoders DT pin, defaults to 12
|
||||
* `ENCODER_CLK_PIN` - The encoders CLK pin, defaults to 14
|
||||
* `ENCODER_SW_PIN` - The encoders SW pin, defaults to 13
|
||||
|
@ -37,7 +37,7 @@
|
||||
#define ENCODER_SW_PIN 13
|
||||
#endif
|
||||
|
||||
#ifndef USERMOD_FOUR_LINE_DISLAY
|
||||
#ifndef USERMOD_FOUR_LINE_DISPLAY
|
||||
// These constants won't be defined if we aren't using FourLineDisplay.
|
||||
#define FLD_LINE_3_BRIGHTNESS 0
|
||||
#define FLD_LINE_3_EFFECT_SPEED 0
|
||||
@ -62,7 +62,7 @@ private:
|
||||
unsigned char button_state = HIGH;
|
||||
unsigned char prev_button_state = HIGH;
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
FourLineDisplayUsermod *display;
|
||||
#else
|
||||
void* display = nullptr;
|
||||
@ -96,7 +96,7 @@ public:
|
||||
modes_alpha_indexes = modeSortUsermod->getModesAlphaIndexes();
|
||||
palettes_alpha_indexes = modeSortUsermod->getPalettesAlphaIndexes();
|
||||
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
// This Usermod uses FourLineDisplayUsermod for the best experience.
|
||||
// But it's optional. But you want it.
|
||||
display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
|
||||
@ -248,7 +248,7 @@ public:
|
||||
}
|
||||
|
||||
boolean changeState(const char *stateName, byte lineThreeMode, byte markedLine) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display != nullptr) {
|
||||
if (display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
@ -272,7 +272,7 @@ public:
|
||||
}
|
||||
|
||||
void changeBrightness(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
@ -288,7 +288,7 @@ public:
|
||||
}
|
||||
|
||||
void changeEffect(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
@ -305,7 +305,7 @@ public:
|
||||
}
|
||||
|
||||
void changeEffectSpeed(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
@ -321,7 +321,7 @@ public:
|
||||
}
|
||||
|
||||
void changeEffectIntensity(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
@ -337,7 +337,7 @@ public:
|
||||
}
|
||||
|
||||
void changePalette(bool increase) {
|
||||
#ifdef USERMOD_FOUR_LINE_DISLAY
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
if (display && display->wakeDisplay()) {
|
||||
// Throw away wake up input
|
||||
return;
|
||||
|
@ -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
|
||||
|
@ -55,6 +55,8 @@
|
||||
#define USERMOD_ID_VL53L0X 12 //Usermod "usermod_vl53l0x_gestures.h"
|
||||
#define USERMOD_ID_MULTI_RELAY 13 //Usermod "usermod_multi_relay.h"
|
||||
#define USERMOD_ID_ANIMATED_STAIRCASE 14 //Usermod "Animated_Staircase.h"
|
||||
#define USERMOD_ID_RTC 15 //Usermod "usermod_rtc.h"
|
||||
#define USERMOD_ID_ELEKSTUBE_IPS 16 //Usermod "usermod_elekstube_ips.h"
|
||||
|
||||
//Access point behavior
|
||||
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
||||
|
@ -270,6 +270,10 @@ bool deserializeState(JsonObject root)
|
||||
}
|
||||
}
|
||||
|
||||
if (root["nx"].is<const char*>()) {
|
||||
strncpy(cronixieDisplay, root["nx"], 6);
|
||||
}
|
||||
|
||||
usermods.readFromJsonState(root);
|
||||
|
||||
int ps = root[F("psave")] | -1;
|
||||
|
@ -137,7 +137,7 @@ void publishMqtt()
|
||||
XML_response(nullptr, apires);
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat_P(subuf, PSTR("/v"));
|
||||
mqtt->publish(subuf, 0, true, apires);
|
||||
mqtt->publish(subuf, 0, false, apires);
|
||||
}
|
||||
|
||||
|
||||
|
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
|
||||
}
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
#include <Arduino.h> //PI constant
|
||||
|
||||
//#define WLED_DEBUG_MATH
|
||||
|
||||
#define modd(x, y) ((x) - (int)((x) / (y)) * (y))
|
||||
|
||||
float cos_t(float x)
|
||||
@ -24,38 +26,58 @@ float cos_t(float x)
|
||||
}
|
||||
float xx = x * x;
|
||||
|
||||
return sign * (1 - ((xx) / (2)) + ((xx * xx) / (24)) - ((xx * xx * xx) / (720)) + ((xx * xx * xx * xx) / (40320)) - ((xx * xx * xx * xx * xx) / (3628800)) + ((xx * xx * xx * xx * xx * xx) / (479001600)));
|
||||
float res = sign * (1 - ((xx) / (2)) + ((xx * xx) / (24)) - ((xx * xx * xx) / (720)) + ((xx * xx * xx * xx) / (40320)) - ((xx * xx * xx * xx * xx) / (3628800)) + ((xx * xx * xx * xx * xx * xx) / (479001600)));
|
||||
#ifdef WLED_DEBUG_MATH
|
||||
Serial.printf("cos: %f,%f\n",res,cos(x));
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
float sin_t(float x) {
|
||||
return cos_t(HALF_PI - x);
|
||||
float res = cos_t(HALF_PI - x);
|
||||
#ifdef WLED_DEBUG_MATH
|
||||
Serial.printf("sin: %f,%f\n",res,sin(x));
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
float tan_t(float x) {
|
||||
float c = cos_t(x);
|
||||
if (c==0.0) return 0;
|
||||
return sin_t(x) / c;
|
||||
float res = sin_t(x) / c;
|
||||
#ifdef WLED_DEBUG_MATH
|
||||
Serial.printf("tan: %f,%f\n",res,tan(x));
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
//https://stackoverflow.com/questions/3380628
|
||||
// Absolute error <= 6.7e-5
|
||||
float acos_t(float x) {
|
||||
float negate = float(x < 0);
|
||||
x = std::abs(x);
|
||||
float xabs = std::abs(x);
|
||||
float ret = -0.0187293;
|
||||
ret = ret * x;
|
||||
ret = ret * xabs;
|
||||
ret = ret + 0.0742610;
|
||||
ret = ret * x;
|
||||
ret = ret * xabs;
|
||||
ret = ret - 0.2121144;
|
||||
ret = ret * x;
|
||||
ret = ret * xabs;
|
||||
ret = ret + HALF_PI;
|
||||
ret = ret * sqrt(1.0-x);
|
||||
ret = ret * sqrt(1.0-xabs);
|
||||
ret = ret - 2 * negate * ret;
|
||||
return negate * PI + ret;
|
||||
float res = negate * PI + ret;
|
||||
#ifdef WLED_DEBUG_MATH
|
||||
Serial.printf("acos,%f,%f,%f\n",x,res,acos(x));
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
float asin_t(float x) {
|
||||
return HALF_PI - acos_t(x);
|
||||
float res = HALF_PI - acos_t(x);
|
||||
#ifdef WLED_DEBUG_MATH
|
||||
Serial.printf("asin,%f,%f,%f\n",x,res,asin(x));
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
//https://stackoverflow.com/a/42542593
|
||||
@ -63,21 +85,54 @@ float asin_t(float x) {
|
||||
#define B -0.287434475393028
|
||||
#define C ((HALF_PI/2) - A - B)
|
||||
|
||||
//polynominal factors for approximation between 1 and 5
|
||||
#define C0 0.089494f
|
||||
#define C1 0.974207f
|
||||
#define C2 -0.326175f
|
||||
#define C3 0.05375f
|
||||
#define C4 -0.003445f
|
||||
|
||||
float atan_t(float x) {
|
||||
bool neg = (x < 0);
|
||||
#ifdef WLED_DEBUG_MATH
|
||||
float xinput = x;
|
||||
#endif
|
||||
x = std::abs(x);
|
||||
float res;
|
||||
if (x > 5.0f) { //atan(x) converges to pi/2 - (1/x) for large values
|
||||
res = HALF_PI - (1.0f/x);
|
||||
}
|
||||
else if (x > 1.0f) { //1 < x < 5
|
||||
float xx = x * x;
|
||||
return ((A*xx + B)*xx + C)*x;
|
||||
res = (C4*xx*xx)+(C3*xx*x)+(C2*xx)+(C1*x)+C0;
|
||||
} else { //this approximation is only for x <= 1
|
||||
float xx = x * x;
|
||||
res = ((A*xx + B)*xx + C)*x;
|
||||
}
|
||||
if (neg) res = -res;
|
||||
#ifdef WLED_DEBUG_MATH
|
||||
Serial.printf("atan,%f,%f,%f\n",xinput,res,atan(xinput));
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
float floor_t(float x) {
|
||||
bool neg = x < 0;
|
||||
int val = x;
|
||||
if (neg) val--;
|
||||
#ifdef WLED_DEBUG_MATH
|
||||
Serial.printf("floor: %f,%f\n",val,floor(x));
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
float fmod_t(float num, float denom) {
|
||||
int tquot = num / denom;
|
||||
return num - tquot * denom;
|
||||
float res = num - tquot * denom;
|
||||
#ifdef WLED_DEBUG_MATH
|
||||
Serial.printf("fmod: %f,%f\n",res,fmod(num,denom));
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|