Hello everyone,
I am an absolute beginner with little knowledge. I built a linear clock based on the Kassel model.
The circuit is based on an ESP Wroom-32 connected to 14 WS2811, which have the following tasks:
WS2811 #0-2: single-digit seconds (9 LEDs)
WS2811 #3-4: ten-digit seconds (5 LEDs); Output "Blue" of the last WS2811 is not used
WS2811 #5-7: single-digit minutes (9 LEDs)
WS2811 #8-9: ten-digit minutes (5 LEDs); Output "Blue" of the last WS2811 is not used
WS2811 #10-12: single-digit hours (9 LEDs)
WS2811 #13: ten-digit hours (2 LEDs); Output "Blue" of the last WS2811 is not used
I used the FastLED library to control the individual LEDs. To decompose the seconds, minutes and hours into single digits and ten-digits, I used the Modulo (%) operation. The LEDs are activated and deactivated with approx. 40 if > else links. There's probably an easier way, but so far everything works as expected.
If I now disconnect the ESP32 from the power and then reconnect it, some LEDs in almost all areas are not activated immediately (blocks of three LEDs are missing). Only after these are regularly activated by the time they light up again. This can take up to six hours.
If I were you I would put the code that sets the state of all of the LEDs to the current time in a function. That function could then be called whenever you want to display the time, including from setup()
It will do no harm to update the state of all of the LEDs every second
Hi,
thank you very much. That would also be my goal, but unfortunately I don't know what to look for to create such a function. Could you maybe give me a tip?
There are 14 addressable WS2811 chips with 39 connected LEDs. A strip doesn't really help me with this, because the watch will end up being installed in a 1/87 scale model. The watch is then equipped with 117 LEDs of type 0603.
Add a new function to the sketch. Let's call it displayTime(). In that function put the code that uses the current time to determine which LEDs should be on or off and which colour they should be
Remove all of the code from loop() that controls the LEDs and instead call displayTime() every time that the value of the second changes
You don't even need to call displayTime() from setup() as it will only take one second in loop() before it is called anyway
Might take a bit of rewiring to get the colors in the correct order, but it would be a bit simpler code-wise to directly address individual bytes in the LEDs.
As an example, this should set the seconds and 10's of seconds LEDs
//note - set entire strip to black (0x00) first
byte seconds_1s = seconds % 10;
byte seconds_10s = seconds / 10;
byte* ptr = (byte*)&leds;
for (byte i = 0; i < seconds_1s; i++){
*ptr++ = 0xFF;
}
ptr = (byte*)&leds + 9; //start of 10's of seconds LEDs
for (byte i = 0; i < seconds_10s; i++){
*ptr++ = 0xFF;
}
In the end, however, I would need 15 WS2811. Then I could control the seconds with red, the minutes with green and the hours with blue. The final board has not yet been ordered. Did I get that right?
Driving pixels with the WS2811 chip and using FastLED to set the colors is way easier than cobbling up enough shifting registers to do the job, even if all you were doing was on/off.
If you add PWM colour control it only makes the smart LED approach look better.
@david_2018's idea is a good one, wire 'em up and sort it out in software at the byte level.
I would think that if were convenient, a few more or less WS2811 would t be a large deal.
Mostly, though, it is nearly working now, and is (should be) already a matter of getting the code right.
This
FastLED.delay(1000);
is nothing you want to do in code like this. If once per second is a thing, just watch for the RTC seconds to change, then publish. As @UKHeliBob sez, with only this many pixels, rendering them all every second is not a bad thing.
The code should need but one call in the code to show the new contents of the pixel buffer.
you‘re my hero for today! A big and fat thank you to Flensburg.
I have added a few little things. Seconds 1-3 went out when the fourth started. But the clear structure was so easy to read that changes were made quickly.
Now it works smoothly:
/*
Project: Linearuhr von NTC Zeit Server mit ESP32
Author: Michael Todtenbier
Date: Created 02.07.2024
Version: V1.0
IDE: Arduino IDE 1.8.19
Required libraries (sketch -> include library -> manage libraries)
-Grüße aus Flensburg
*/
#include <WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
//#include <Adafruit_NeoPixel.h>
#include <FastLED.h>
#define DATA_PIN 27 // Pin für LED-Streifen
const byte NUM_LEDS = 14; // Anzahl der WS2811-Chips
// This is an array of leds. One item for each led in your strip.
CRGB leds[NUM_LEDS];
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 7200, 60000);
// Wi-Fi Settings
const char *wifi_ssid = "YourSSID";
const char *wifi_password = "YourPassword";
const char* NTP_SERVER = "de.pool.ntp.org";
const char* TZ_INFO = "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00";
/*
time_t now;
tm tm;
*/
void setup () {
Serial.begin(115200);
// Verbindung zu WiFi herstellen
delay(10);
WiFi.begin(wifi_ssid, wifi_password);
int counter = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(500);
if (++counter > 100) ESP.restart();
Serial.print(".");
}
Serial.println("WiFi connected");
//FastLED aktivieren
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS);
// NTP Client starten
timeClient.begin();
}
void loop () {
timeClient.update();
byte hoursH = timeClient.getHours() / 10;
byte hoursL = timeClient.getHours() % 10;
byte minutesH = timeClient.getMinutes() / 10;
byte minutesL = timeClient.getMinutes() % 10;
byte secondsH = timeClient.getSeconds() / 10;
byte secondsL = timeClient.getSeconds() % 10;
// Serial.printf(" \tUhrzeit: %02d:%02d:%02d \n", hours, minutes, seconds);
for (int i = 0; i < NUM_LEDS; i++)leds[i] = CRGB::Black;
//Zehnerstunden
if (hoursH > 1) leds[13] = CRGB::Yellow;
else if (hoursH ) leds[13] = CRGB::Red;
//Einerstunden
if (hoursL > 8) {leds[12] = CRGB::White; leds[11] = CRGB::White; leds[10] = CRGB::White;}
else if (hoursL > 7) {leds[12] = CRGB::Yellow; leds[11] = CRGB::White; leds[10] = CRGB::White;}
else if (hoursL > 6) {leds[12] = CRGB::Red; leds[11] = CRGB::White; leds[10] = CRGB::White;}
else if (hoursL > 5) {leds[11] = CRGB::White; leds[10] = CRGB::White;}
else if (hoursL > 4) {leds[11] = CRGB::Yellow; leds[10] = CRGB::White;}
else if (hoursL > 3) {leds[11] = CRGB::Red; leds[10] = CRGB::White;}
else if (hoursL > 2) leds[10] = CRGB::White;
else if (hoursL > 1) leds[10] = CRGB::Yellow;
else if (hoursL )leds[10] = CRGB::Red;
//Zehnerminuten
if ( minutesH > 4) {leds[9] = CRGB::Yellow; leds[8] = CRGB::White;}
else if ( minutesH > 3) {leds[9] = CRGB::Red; leds[8] = CRGB::White;}
else if ( minutesH > 2) leds[8] = CRGB::White;
else if ( minutesH > 1) leds[8] = CRGB::Yellow;
else if ( minutesH ) leds[8] = CRGB::Red;
//Einerminuten
if ( minutesL > 8) {leds[7] = CRGB::White; leds[6] = CRGB::White; leds[5] = CRGB::White;}
else if ( minutesL > 7) {leds[7] = CRGB::Yellow; leds[6] = CRGB::White; leds[5] = CRGB::White;}
else if ( minutesL > 6) {leds[7] = CRGB::Red; leds[6] = CRGB::White; leds[5] = CRGB::White;}
else if ( minutesL > 5) {leds[6] = CRGB::White; leds[5] = CRGB::White;}
else if ( minutesL > 4) {leds[6] = CRGB::Yellow; leds[5] = CRGB::White;}
else if ( minutesL > 3) {leds[6] = CRGB::Red; leds[5] = CRGB::White;}
else if ( minutesL > 2) leds[5] = CRGB::White;
else if ( minutesL > 1) leds[5] = CRGB::Yellow;
else if ( minutesL )leds[5] = CRGB::Red;
//Zehnersekunden
if (secondsH > 4) {leds[4] = CRGB::Yellow; leds[3] = CRGB::White;}
else if (secondsH > 3) {leds[4] = CRGB::Red; leds[3] = CRGB::White;}
else if (secondsH > 2) leds[3] = CRGB::White;
else if (secondsH > 1) leds[3] = CRGB::Yellow;
else if (secondsH) leds[3] = CRGB::Red;
//Einersekunden
if (secondsL > 8) {leds[2] = CRGB::White; leds[1] = CRGB::White; leds[0] = CRGB::White;}
else if (secondsL > 7) {leds[2] = CRGB::Yellow; leds[1] = CRGB::White; leds[0] = CRGB::White;}
else if (secondsL > 6) {leds[2] = CRGB::Red; leds[1] = CRGB::White; leds[0] = CRGB::White;}
else if (secondsL > 5) {leds[1] = CRGB::White; leds[0] = CRGB::White;}
else if (secondsL > 4) {leds[1] = CRGB::Yellow; leds[0] = CRGB::White;}
else if (secondsL > 3) {leds[1] = CRGB::Red; leds[0] = CRGB::White;}
else if (secondsL > 2) leds[0] = CRGB::White;
else if (secondsL > 1) leds[0] = CRGB::Yellow;
else if (secondsL) leds[0] = CRGB::Red;
FastLED.show();
FastLED.delay(1000);
}
The 74hc595 is certainly the better solution, but I am absolutely not familiar with it. On the other hand, I am very familiar with the WS2811. I have installed hundreds of them since 2020. Since the sketch is now running, there is no longer any reason to change the finished board layout.
at the end of the loop. There will otherwise be, I feel sure, a glitch from time to time as the unstnchronized delay falls unfortunately with respect to the actual increment.
void loop () {
timeClient.update();
static int lastDisplayed = -1; // impossible initial value
int secondsNow = timeClient.getSeconds();
if (secondsNow == lastDisplayed) return; // not time yet
lastDisplayed = secondsNow;
byte hoursH = timeClient.getHours() / 10;
byte hoursL = timeClient.getHours() % 10;
byte minutesH = timeClient.getMinutes() / 10;
byte minutesL = timeClient.getMinutes() % 10;
// &c.
Hi,
this is a self-developed board with ten WS2811 chips. It is designed to solder LEDs directly to the outputs and has four input pins to connect the board to an Arduino.
So each single LED is connectet to one of these WS2811 starting from bottom to top of the linear clock. For example: the seconds are connectet to the first three WS2811 and their RG and B-Outputs.
When I remove the delay, the sketch requests the time about 30 times per second (you can see this on the serial monitor). With Delay, the time is only queried once per second. Currently, I still wonder what impact this has on my WLAN and whether the use of an RTC module would not be better.