Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
b1ae6e456d | |||
7a729863b2 | |||
7cacedcf81 | |||
d525635b14 | |||
42d39f2548 | |||
ac691e196a | |||
12ce5ad02f | |||
b3869b7314 | |||
007407d2a1 | |||
6168a43384 | |||
86a7d401bd | |||
e4dfc3e6fe | |||
85ebffce1e | |||
e20dd310f9 | |||
e6a7f59f5c | |||
cbf6b79e73 | |||
|
bc38a5b777 | ||
|
69cded525a | ||
|
45a7ffb513 | ||
|
4d35218695 |
25805
Hardware/laser_template.svg
Normal file
25805
Hardware/laser_template.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 1.3 MiB |
20
README.md
20
README.md
@@ -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/
|
||||
|
96
config.h
96
config.h
@@ -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
99
constants.h
Normal 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
|
@@ -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
|
||||
|
Reference in New Issue
Block a user