20 Commits
v1.0 ... main

Author SHA1 Message Date
b1ae6e456d Added link to 2nd blog post in README. 2019-03-03 21:12:08 +01:00
7a729863b2 Reordered objects to be engraved to a single sublayer of 'frontplate'. 2019-02-21 12:34:16 +01:00
7cacedcf81 Removed hole from backplate - enough space in mid-layer (barrel) 2019-02-13 13:07:32 +01:00
d525635b14 Fixed minor issues in svg: repositioned numbers, fixed holes for input devices.
Added intermediate Files for direct laser usage - probably specific for the warpzone laser software.
2019-02-11 08:16:21 +01:00
42d39f2548 Moved input area out away from frontplate numbers. 2019-02-09 13:46:01 +01:00
ac691e196a Fixed error with barrel. 2019-02-08 15:03:35 +01:00
12ce5ad02f Added frontplate numbers, moved logos. 2019-02-08 14:58:54 +01:00
b3869b7314 Added logos for engraving. Still needs mounting holes/places for button/poti. 2019-02-07 15:33:11 +01:00
007407d2a1 Added Backplate and Arduino mounting holes as well as hidden (=green) Arduino Uno shapes. 2019-02-07 13:53:18 +01:00
6168a43384 Initial Form with "barrel" and frontplate. 2019-02-06 22:21:46 +01:00
86a7d401bd Added useful information and removed legacy stuff. 2019-02-05 18:43:23 +01:00
e4dfc3e6fe Fixed formatting of library links. 2019-02-03 20:16:08 +01:00
85ebffce1e Changed LED ring size back to sane default of 60. 2019-02-03 20:06:22 +01:00
e20dd310f9 Fixed disappearing hour / minute handles the dirty way. 2019-02-03 19:41:25 +01:00
e6a7f59f5c Removed RTClib reference 2019-02-03 00:10:36 +01:00
cbf6b79e73 Exchanged RTClib with NtpClientLib (currently branch develop)
added additional variables to constants.h
2019-02-02 23:54:59 +01:00
jackw01
bc38a5b777 fix up trail and glow code 2018-09-09 19:12:13 -07:00
jackw01
69cded525a improving clock modes 2018-09-09 16:31:26 -07:00
jackw01
45a7ffb513 moving colored clock modes into special color schemes 2018-09-08 23:02:50 -07:00
jackw01
4d35218695 enhanced blending modes and antialiasing 2018-09-08 22:21:43 -07:00
5 changed files with 26124 additions and 227 deletions

25805
Hardware/laser_template.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -1,13 +1,25 @@
# led-ring-clock
An Arduino-based analog clock using an WS2812B addressable RGB LED ring as a display.
This sketch requires the FastLED library, the Wire library (built-in), the EEPROM library (built-in), and the Adafruit RTClib.
This sketch requires the FastLED library, the Wire library (built-in), the EEPROM library (built-in), and the NtpClientLib
(currently requires develop branch).
https://github.com/FastLED/FastLED
https://github.com/adafruit/RTClib
### 3D-printable enclosure
https://github.com/gmag11/NtpClient/tree/develop
### 3D-printable enclosure for NeoPixel Ring (24 LEDs)
Design files available on Thingiverse: https://www.thingiverse.com/thing:2730265
### Electronics information
The LED ring is connected to digital pin 3. Momentary N/O button is connected to digital pin 4 with 10K pullup resistor. 10K potentiometer is connected across power and ground and the wiper is connected to Analog 0. DS1307 RTC breakout is connected to the board's default I2C pins. This code has only been tested with an Adafruit 24 Neopixel Ring, but should (hopefully) work with any type or size of WS2812B ring, and with minimal changes it should work with rings using other types of RGB LEDs. The code should work on any ATmega328 based board.
The LED ring is connected to digital pin 3. Momentary N/O button is connected to digital pin 5.
10K potentiometer is connected across power and ground and the wiper is connected to Analog 0.
This code has been tested with WS2812B LED Strips of up to 120 LEDs, but more should
be possible. With minimal changes it should work with rings using other types of RGB LEDs.
The code should work on any ATmega328 based board.
### Blog-Post with howto and more details (German)
https://www.commander1024.de/wordpress/2019/02/analoge-arduino-led-uhr/
### Blog-Post with hardware build instructions (German)
https://www.commander1024.de/wordpress/2019/02/bau-der-analogen-uhr/

View File

@@ -1,96 +0,0 @@
//
// WS2812 LED Analog Clock Firmware
// Copyright (c) 2016-2018 jackw01
// This code is distrubuted under the MIT License, see LICENSE for details
//
#ifndef CONFIG_H
#define CONFIG_H
#include <Arduino.h>
#include <FastLED.h>
// IO Pin Assignments
const uint8_t pinLeds = 3;
const uint8_t pinButton = 4;
const uint8_t pinBrightness = 0;
// Number of LEDs in ring
const int ledRingSize = 24;
// Default colors - tweaked to look right on WS2812Bs
CRGB red = CRGB(255, 0, 0);
CRGB orange = CRGB(255, 78, 0);
CRGB yellow = CRGB(255, 237, 0);
CRGB green = CRGB(0, 255, 23);
CRGB cyan = CRGB(0, 247, 255);
CRGB blue = CRGB(0, 21, 255);
CRGB magenta = CRGB(190, 0, 255);
CRGB white = CRGB(255, 255, 255);
CRGB off = CRGB(0, 0, 0);
// Default clock face colors
// red, orange, yellow, green, cyan, blue, magenta, and white are acceptable, along with CRGB(r, g, b)
const int colorSchemeCount = 7;
const CRGB colorSchemes[colorSchemeCount][4] = {
{red, // Color for hour display
green, // Color for minute display
blue}, // Color for second display
{ CRGB(255, 255, 255), CRGB(255, 255, 255), CRGB( 0, 130, 255) },
{ CRGB(255, 255, 255), CRGB(255, 255, 255), CRGB(255, 25, 0) },
{ CRGB( 64, 0, 128), CRGB(255, 72, 0), CRGB(255, 164, 0) },
{ CRGB(255, 25, 0), CRGB(255, 84, 0), CRGB(255, 224, 0) },
{ CRGB( 0, 0, 255), CRGB( 0, 84, 255), CRGB( 0, 255, 255) },
{ CRGB(255, 0, 96), CRGB(255, 84, 0), CRGB( 0, 255, 164) }
};
// Clock settings
const int buttonClickRepeatDelayMs = 1500;
const int buttonLongPressDelayMs = 300;
const bool showSecondHand = true;
const bool twelveHour = true;
// Serial
const int serialPortBaudRate = 115200;
const int debugMessageIntervalMs = 5000;
// Clock modes
typedef enum {
ClockModeRingClock,
ClockModeDotClock,
ClockModeDotClockColorChange,
ClockModeDotClockTimeColor,
ClockModeGlowClock,
ClockModeCount
} ClockMode;
// Brightness
const uint8_t minBrightness = 4;
// Run loop
const int runLoopIntervalMs = 30;
// EEPROM Addresses
const uint16_t eepromAddrColorScheme = 0;
const uint16_t eepromAddrClockMode = 1;
// Gamma correction values
const uint8_t PROGMEM gamma[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
#endif

99
constants.h Normal file
View File

@@ -0,0 +1,99 @@
//
// WS2812 LED Analog Clock Firmware
// Copyright (c) 2016-2018 jackw01
// NTP Changes: 2019 Commander1024
// This code is distrubuted under the MIT License, see LICENSE for details
//
#ifndef CONFIG_H
#define CONFIG_H
#include <Arduino.h>
#include <FastLED.h>
// IO Pin Assignments
const uint8_t pinLeds = 3;
const uint8_t pinButton = 5;
const uint8_t pinBrightness = 0;
// Define MAC Address
byte mac[] = {
0xA8, 0x61, 0x0A, 0x10, 0x24, 0x01
};
// NTP Server to use
char* ntp_server = "warpfire.warpzone";
// Number of LEDs in ring
const int ledRingSize = 60;
// Default colors - tweaked to look right on WS2812Bs
const CRGB red = CRGB(255, 0, 0);
const CRGB orange = CRGB(255, 78, 0);
const CRGB yellow = CRGB(255, 237, 0);
const CRGB green = CRGB(0, 255, 23);
const CRGB cyan = CRGB(0, 247, 255);
const CRGB blue = CRGB(0, 21, 255);
const CRGB magenta = CRGB(190, 0, 255);
const CRGB white = CRGB(255, 255, 255);
const CRGB off = CRGB(0, 0, 0);
// Default clock face colors
// red, orange, yellow, green, cyan, blue, magenta, and white are acceptable, along with CRGB(r, g, b)
const int colorSchemeCount = 7;
const CRGB colorSchemes[colorSchemeCount][3] = {
{red, // Color for hour display
green, // Color for minute display
blue}, // Color for second display
{ CRGB(255, 255, 255), CRGB(255, 255, 255), CRGB( 0, 130, 255) },
{ CRGB(255, 255, 255), CRGB(255, 255, 255), CRGB(255, 25, 0) },
{ CRGB( 64, 0, 128), CRGB(255, 72, 0), CRGB(255, 164, 0) },
{ CRGB(255, 25, 0), CRGB(255, 84, 0), CRGB(255, 224, 0) },
{ CRGB( 0, 0, 255), CRGB( 0, 84, 255), CRGB( 0, 255, 255) },
{ CRGB(255, 0, 96), CRGB(255, 84, 0), CRGB( 0, 255, 164) }
};
// Clock settings
const bool useEnhancedRenderer = true;
const bool showSecondHand = true;
const bool twelveHour = true;
const int hourGlowWidth = 4; // Pixels in each direction
const int minuteGlowWidth = 2; // Pixels in each direction
const int secondGlowWidth = 1; // Pixels in each direction
const int hourTrailLength = 1;
const int minuteTrailLength = 2;
const int secondTrailLength = 3;
const int buttonClickRepeatDelayMs = 1500;
const int buttonLongPressDelayMs = 300;
// Serial
const long serialPortBaudRate = 9600;
const int debugMessageIntervalMs = 5000;
// Clock modes
typedef enum {
ClockModeRingClock,
ClockModeDotClock,
ClockModeDotClockTrail,
ClockModeDotClockGlow,
ClockModeCount
} ClockMode;
// Brightness
const uint8_t minBrightness = 4;
// Run loop
const int runLoopIntervalMs = 30;
// EEPROM addresses
const uint16_t eepromAddrColorScheme = 0;
const uint16_t eepromAddrClockMode = 1;
// LED blend modes
typedef enum {
BlendModeOver,
BlendModeAlpha,
BlendModeAdd
} BlendMode;
#endif

View File

@@ -1,43 +1,71 @@
//
// WS2812 LED Analog Clock Firmware
// Copyright (c) 2016-2018 jackw01
// NTP Changes: 2019 Commander1024
// This code is distrubuted under the MIT License, see LICENSE for details
//
#include <math.h>
#include <FastLED.h>
#include <Wire.h>
#include <EEPROM.h>
#include <RTClib.h>
#include <TimeLib.h>
#include <NtpClientLib.h>
#include <SPI.h>
#include <EthernetUdp.h>
#include <Ethernet.h>
#include <Dns.h>
#include <Dhcp.h>
#include "config.h"
#include "constants.h"
// LED ring size
CRGB leds[ledRingSize];
RTC_DS1307 rtc;
// Globals to keep track of state
int clockMode, colorScheme;
int lastLoopTime = 0;
int lastButtonClickTime = 0;
int lastDebugMessageTime = 0;
uint32_t lastLoopTime = 0;
uint32_t lastButtonClickTime = 0;
uint32_t lastDebugMessageTime = 0;
uint8_t currentBrightness;
uint8_t previousBrightness[16];
DateTime now;
int lastSecondsValue = 0;
uint32_t lastMillisecondsSetTime = 0;
int milliseconds;
void setup() {
// Begin serial port
Serial.begin(serialPortBaudRate);
// Initialize Network and NTP
if (Ethernet.begin (mac) == 0) {
Serial.println ("Failed to configure Ethernet using DHCP");
// no point in carrying on, so do nothing forevermore:
for (;;)
;
}
NTP.onNTPSyncEvent ([](NTPSyncEvent_t error) {
if (error) {
Serial.print ("Time Sync error: ");
if (error == noResponse)
Serial.println ("NTP server not reachable");
else if (error == invalidAddress)
Serial.println ("Invalid NTP server address");
} else {
Serial.print ("Got NTP time: ");
Serial.println (NTP.getTimeDateString (NTP.getLastNTPSync ()));
}
});
NTP.setInterval (60, 900);
NTP.setNTPTimeout (1000);
NTP.begin (ntp_server, 1, true);
// Init FastLED
FastLED.addLeds<NEOPIXEL, pinLeds>(leds, ledRingSize);
FastLED.setTemperature(Halogen);
FastLED.show();
// Connect to the RTC
Wire.begin();
rtc.begin();
// Set button pin
pinMode(pinButton, INPUT);
pinMode(pinButton, INPUT_PULLUP);
// Read saved config from EEPROM
colorScheme = EEPROM.read(eepromAddrColorScheme);
@@ -47,26 +75,30 @@ void setup() {
if (digitalRead(pinButton) == LOW) {
for (int i = 0; i < ledRingSize; i++) leds[i] = white;
FastLED.show();
delay(60000);
delay(10000);
}
}
static int i = 0;
static int last = 0;
void loop() {
int currentTime = millis();
uint32_t currentTime = millis();
if (currentTime - lastLoopTime > runLoopIntervalMs) {
lastLoopTime = millis();
lastLoopTime = currentTime;
// Handle button
if (digitalRead(pinButton) == LOW && currentTime - lastButtonClickTime > buttonClickRepeatDelayMs) {
delay(buttonLongPressDelayMs);
// Long press: clock mode, short press: color scheme
if (digitalRead(pinButton) == LOW) {
lastButtonClickTime = currentTime;
colorScheme ++;
if (colorScheme >= colorSchemeCount) colorScheme = 0;
EEPROM.write(0, colorScheme);
if (colorScheme >= colorSchemeCount + 2) colorScheme = 0; // 2 special color schemes
EEPROM.write(eepromAddrColorScheme, colorScheme);
} else {
clockMode ++;
if (clockMode >= ClockModeCount) clockMode = 0;
EEPROM.write(1, clockMode);
EEPROM.write(eepromAddrClockMode, clockMode);
}
}
@@ -87,10 +119,22 @@ void loop() {
currentBrightness = sum / 16;
FastLED.setBrightness(currentBrightness);
// Get time and calculate milliseconds value that is synced with the RTC's second count
int currentSeconds = second(now());
if (currentSeconds != lastSecondsValue) {
lastSecondsValue = currentSeconds;
milliseconds = 0;
}
currentTime = millis();
milliseconds = (milliseconds + currentTime - lastMillisecondsSetTime);
lastMillisecondsSetTime = currentTime;
// Show clock
now = rtc.now();
clearLeds();
showClock();
// Check/Renew DHCP
Ethernet.maintain ();
}
}
@@ -98,19 +142,16 @@ void loop() {
void showClock() {
switch (clockMode) {
case ClockModeRingClock:
ringClock();
drawRingClock();
break;
case ClockModeDotClock:
dotClock();
drawDotClock();
break;
case ClockModeDotClockColorChange:
rainbowDotClock();
case ClockModeDotClockTrail:
drawDotClockTrail();
break;
case ClockModeDotClockTimeColor:
timeColorClock();
break;
case ClockModeGlowClock:
glowClock();
case ClockModeDotClockGlow:
drawDotClockGlow();
break;
}
}
@@ -118,18 +159,17 @@ void showClock() {
// Print debugging info over serial
void printDebugMessage() {
Serial.print("Current date/time: ");
DateTime now = rtc.now();
Serial.print(now.year(), DEC);
Serial.print(year(now()), DEC);
Serial.print("/");
Serial.print(now.month(), DEC);
Serial.print(month(now()), DEC);
Serial.print("/");
Serial.print(now.day(), DEC);
Serial.print(day(now()), DEC);
Serial.print(" ");
Serial.print(now.hour(), DEC);
Serial.print(hour(now()), DEC);
Serial.print(":");
Serial.print(now.minute(), DEC);
Serial.print(minute(now()), DEC);
Serial.print(":");
Serial.print(now.second(), DEC);
Serial.print(second(now()), DEC);
Serial.println();
Serial.print("Display mode: ");
Serial.println(clockMode);
@@ -141,130 +181,130 @@ void printDebugMessage() {
}
// Show a ring clock
void ringClock() {
void drawRingClock() {
int h = hourPosition();
int m = minutePosition();
int s = secondPosition();
float s = secondPosition();
if (m > h) {
for (int i = 0; i < m; i++) leds[i] = minuteColor();
for (int i = 0; i < h; i++) leds[i] = hourColor();
for (int i = 0; i < m; i++) setLed(i, minuteColor(), BlendModeOver, 1.0);
for (int i = 0; i < h; i++) setLed(i, hourColor(), BlendModeOver, 1.0);
} else {
for (int i = 0; i < h; i++) leds[i] = hourColor();
for (int i = 0; i < m; i++) leds[i] = minuteColor();
for (int i = 0; i < h; i++) setLed(i, hourColor(), BlendModeOver, 1.0);
for (int i = 0; i < m; i++) setLed(i, minuteColor(), BlendModeOver, 1.0);
}
if (showSecondHand) leds[s] = secondColor();
if (showSecondHand) setLed(s, secondColor(), BlendModeAlpha, 1.0);
FastLED.show();
}
// Show a more traditional dot clock
void dotClock() {
int h = hourPosition();
int m = minutePosition();
int s = secondPosition();
void drawDotClock() {
float h = hourPosition();
float m = minutePosition();
float s = secondPosition();
for (int i = h - 1; i < h + 2; i++) leds[wrap(i)] = hourColor();
leds[m] = minuteColor();
if (showSecondHand) [s] = secondColor();
for (float i = h - 1.0; i < h + 2.0; i++) setLed(i, hourColor(), BlendModeAlpha, 1.0);
setLed(m, minuteColor(), BlendModeAlpha, 1.0);
if (showSecondHand) setLed(s, secondColor(), BlendModeAlpha, 1.0);
FastLED.show();
}
// Show a dot clock with hands that change color based on their position
void rainbowDotClock() {
int h = hourPosition();
int m = minutePosition();
int s = secondPosition();
// Show a dot clock where the hands have a glowing trail behing them
void drawDotClockTrail() {
float h = hourPosition();
float m = minutePosition();
float s = secondPosition();
CRGB newHourColor = CHSV(map(now.hour(), 0, 24, 0, 255), 255, 255);
CRGB newMinuteColor = CHSV(map(now.minute(), 0, 59, 0, 255), 255, 255);
CRGB newSecondColor = CHSV(map(now.second(), 0, 59, 0, 255), 255, 255);
for (int i = h - 1; i < h + 2; i++) leds[wrap(i)] = newHourColor;
leds[m] = newMinuteColor;
if (showSecondHand) leds[s] = newSecondColor;
FastLED.show();
}
// Show a dot clock where the color is based on the time
void timeColorClock() {
int h = hourPosition();
int m = minutePosition();
int s = secondPosition();
float decHour = decimalHour();
CRGB pixelColor = CHSV((uint8_t)mapFloat(fmod(20.0 - decHour, 24.0), 0.0, 24.0, 0.0, 255.0), 255, 255);
for (int i = h - 1; i < h + 2; i++) leds[wrap(i)] = pixelColor;
leds[m] = pixelColor;
if (showSecondHand) leds[s] = pixelColor;
FastLED.show();
}
// Show a dot clock where the hands overlap with additive blending
void glowClock() {
int h = hourPosition();
int m = minutePosition();
int s = secondPosition();
for (int i = -6; i < ledRingSize + 6; i++) {
int j;
for (j = 0; j <= 4; j++) {
if (h + j == i || h - j == i) blendAdd(wrap(i), CRGB(255, 0, 0), 1 - mapFloat(j, 0.0, 6.0, 0.1, 0.99));
for (float i = -hourTrailLength; i < 1.0; i++) setLed(h + i, hourColor(), BlendModeAdd, mapFloat(i, -hourTrailLength, 1.0, 0.1, 1.0));
for (float i = -minuteTrailLength; i < 1.0; i++) setLed(m + i, minuteColor(), BlendModeAdd, mapFloat(i, -minuteTrailLength, 1.0, 0.1, 1.0));
if (showSecondHand) {
for (float i = -secondTrailLength; i < 1.0; i++) setLed(s + i, secondColor(), BlendModeAdd, mapFloat(i, -secondTrailLength, 1.0, 0.1, 1.0));
}
for (j = 0; j <= 2; j++) {
if (m + j == i || m - j == i) blendAdd(wrap(i), CRGB(0, 255, 0), 1 - mapFloat(j, 0.0, 3.0, 0.1, 0.99));
FastLED.show();
}
// Show a dot clock where the hands glow outwards from their position
void drawDotClockGlow() {
float h = hourPosition();
float m = minutePosition();
float s = secondPosition();
for (float i = h - hourGlowWidth; i <= h + hourGlowWidth; i++) {
setLed(i, hourColor(), BlendModeAdd, mapFloat(fabs(h - i), 0.0, hourGlowWidth, 1.0, 0.1));
}
for (float i = m - minuteGlowWidth; i <= m + minuteGlowWidth; i++) {
setLed(i, minuteColor(), BlendModeAdd, mapFloat(fabs(m - i), 0.0, minuteGlowWidth, 1.0, 0.1));
}
if (showSecondHand) {
for (j = 0; j <= 1; j++) {
if (s + j == i || s - j == i) blendAdd(wrap(i), CRGB(0, 0, 255), 1 - mapFloat(j, 0.0, 1.0, 0.1, 0.65));
}
for (float i = s - secondGlowWidth; i <= s + secondGlowWidth; i++) {
setLed(i, secondColor(), BlendModeAdd, mapFloat(fabs(s - i), 0.0, secondGlowWidth, 1.0, 0.1));
}
}
FastLED.show();
}
// Get floating point hour representation
float floatHour() {
return (float)hour(now()) + mapFloat(minute(now()) + mapFloat(second(now()), 0.0, 59.0, 0.0, 1.0), 0.0, 59.0, 0.0, 1.0);
}
// Get positions mapped to ring size
int hourPosition() {
float hourPosition() {
if (twelveHour) {
int hour;
if (now.hour() > 12) hour = (now.hour() - 12) * (ledRingSize / 12);
else hour = now.hour() * (ledRingSize / 12);
return hour + int(map(now.minute(), 0, 59, 0, (ledRingSize / 12) - 1));;
int hourt;
if (hour(now()) > 12) hourt = (hour(now()) - 12) * (ledRingSize / 12);
else hourt = hour(now()) * (ledRingSize / 12);
return hourt + mapFloat(minute(now()) + 0.001, 0.0, 59.0, 0.0, (ledRingSize / 12.0) - 1.0);
} else {
int hour = now.hour() * (ledRingSize / 24);
return hour + int(map(now.minute(), 0, 59, 0, (ledRingSize / 24) - 1));;
int hourt = hour(now()) * (ledRingSize / 24);
return hourt + mapFloat(minute(now()) + 0.001, 0, 59, 0, (ledRingSize / 24.0) - 1.0);
}
}
int minutePosition() {
return map(now.minute(), 0, 59, 0, ledRingSize - 1);
float minutePosition() {
return mapFloat(
(float)minute(now()) + ((0.001 + 1.0 / 60.0) * (float)second(now())), 0.0, 59.0, 0.0, (float)ledRingSize
);
}
int secondPosition() {
return map(now.second(), 0, 59, 0, ledRingSize - 1);
}
float decimalHour() {
return (float)now.hour() + mapFloat(now.minute() + mapFloat(now.second(), 0.0, 59.0, 0.0, 1.0), 0.0, 59.0, 0.0, 1.0);
float secondPosition() {
return mapFloat(
second(now()) + (0.001 * milliseconds), 0.0, 60.0, 0.0, (float)ledRingSize
);
}
// Get colors
CRGB hourColor() {
return colorSchemes[colorScheme][0];
if (colorScheme < colorSchemeCount) return colorSchemes[colorScheme][0];
else if (colorScheme == colorSchemeCount + 0) {
return CHSV(map(hour(now()), 0, 24, 0, 255), 255, 255);
} else if (colorScheme == colorSchemeCount + 1) {
return CHSV((uint8_t)mapFloat(fmod(20.0 - floatHour(), 24.0), 0.0, 24.0, 0.0, 255.0), 255, 255);
}
}
CRGB minuteColor() {
return colorSchemes[colorScheme][1];
if (colorScheme < colorSchemeCount) return colorSchemes[colorScheme][1];
else if (colorScheme == colorSchemeCount + 0) {
return CHSV(map(minute(now()), 0, 59, 0, 255), 255, 255);
} else if (colorScheme == colorSchemeCount + 1) {
return CHSV((uint8_t)mapFloat(fmod(20.0 - floatHour(), 24.0), 0.0, 24.0, 0.0, 255.0), 255, 255);
}
}
CRGB secondColor() {
return colorSchemes[colorScheme][2];
if (colorScheme < colorSchemeCount) return colorSchemes[colorScheme][2];
else if (colorScheme == colorSchemeCount + 0) {
return CHSV(map(second(now()), 0, 59, 0, 255), 255, 255);
} else if (colorScheme == colorSchemeCount + 1) {
return CHSV((uint8_t)mapFloat(fmod(20.0 - floatHour(), 24.0), 0.0, 24.0, 0.0, 255.0), 255, 255);
}
}
// Clear the LED ring
@@ -272,11 +312,48 @@ void clearLeds() {
for (int i = 0; i < ledRingSize; i++) leds[i] = CRGB(0, 0, 0);
}
// Enhanced additive blending
// Set LED(s) at a position with enhanced rendering
void setLed(float position, CRGB color, BlendMode blendMode, float factor) {
if (useEnhancedRenderer) {
int low = floor(position);
int high = ceil(position);
float lowFactor = ((float)high - position);
float highFactor = (position - (float)low);
if (blendMode == BlendModeAdd) {
blendAdd(wrap(low), color, lowFactor * factor);
blendAdd(wrap(high), color, highFactor * factor);
} else if (blendMode == BlendModeAlpha) {
blendAlpha(wrap(low), color, lowFactor * factor);
blendAlpha(wrap(high), color, highFactor * factor);
} else if (blendMode == BlendModeOver) {
blendOver(wrap(low), color, lowFactor * factor);
blendOver(wrap(high), color, highFactor * factor);
}
} else {
leds[wrap((int)position)] = color;
}
}
// Additive blending
void blendAdd(int position, CRGB color, float factor) {
leds[position].r += color.r * factor;
leds[position].g += color.g * factor;
leds[position].b += color.b * factor;
leds[position].r += min(color.r * factor, 255 - leds[position].r);
leds[position].g += min(color.g * factor, 255 - leds[position].g);
leds[position].b += min(color.b * factor, 255 - leds[position].b);
}
// Alpha blending (factor is the alpha value)
void blendAlpha(int position, CRGB color, float factor) {
leds[position].r = (uint8_t)mapFloat(factor, 0.0, 1.0, leds[position].r, color.r);
leds[position].g = (uint8_t)mapFloat(factor, 0.0, 1.0, leds[position].g, color.g);
leds[position].b = (uint8_t)mapFloat(factor, 0.0, 1.0, leds[position].b, color.b);
}
// Overlay/replace blending
void blendOver(int position, CRGB color, float factor) {
leds[position].r = color.r * factor;
leds[position].g = color.g * factor;
leds[position].b = color.b * factor;
leds[position] = color;
}
// Wrap around LED ring