Rheinturm-Clock
The World‘s largest Digital Clock at Düsseldorf, now in a living room.

In Düsseldorf, Germany, there is the world’s largest decimal clock on the local TV tower.
https://en.wikipedia.org/wiki/Rheinturm
A friend of mine, who is an experienced maker, has now built a “small” replica for his living room using a 3D printer and a NeoPixel strip. I supported this with an ANNEX32 scrict code for an ESP32 controller.
The result is now located in a living room and looks like this:

This is the Web interface to control the colors and brightnes of the NEoPixels in the tower.

As usual, I chose an Annex32 script to quickly get a prototype up and running, and to adjust the colors and correct distribution of the LEDs.
To adapt it to our needs and try it out, I first tested the script in the WOKWI’s Annex32 emulator, and so my friend was able to access it quickly.
https://wokwi.com/projects/429294505945321473

The final result, now in the living room, is certainly something my friend can be proud of!
' KOPFZEILE1$ = "- R H E I N T U R M - Z E I T P E G E L -"
KOPFZEILE2$ = " mit 60er-NEOPIXEL-Strip"
KOPFZEILE3$ = " - V2.1 -"
' Peter.Neufeld@gmx.de 03.2017 // 04.2025
S1_pos = 0 'Position der ersten Sekunden-LED im Streifen (Anfang bei 0)
LED_PIN = 27 'GPIO Daten-Pin des ESP32 für die NeoPixel-data
LED_TOP_PIN = 26 'EINZELNE klassische 3mm-LED an Turmspitze, blinkt 1 pro Sekunde
LED_NUM = 70 'Anzahl der NeoPixel-LEDs im Streifen
LED_SEC_E = 1 'Anzahl der Trenn-LEDs nach den Sekunden-Einern
LED_SEC_Z = 2 'Anzahl der Trenn-LEDs nach den Sekunden-Zehnern
LED_MIN_E = 1 'Anzahl der Trenn-LEDs nach den MINUTEN-Einern
LED_MIN_Z = 2 'Anzahl der Trenn-LEDs nach den MINUTEN-Zehnern
LED_H_E = 1 'Anzahl der Trenn-LEDs nach den STUNDEN-Einern
LED_H_Z = 1 'Anzahl der Trenn-LEDs nach den STUNDEN-Zehnern
LED_BLINK_NUM = 4 'Anzahl der blinkenden LEDs in der Spitze(=letzte LEDs am Streifenende)
LED_BLINK_POS = LED_NUM - LED_BLINK_NUM 'Blinkende LED an der Turmspitze LED_DIMM = 200 'Helligkeit der Trenn-LEDs
LED_blink_DIMM = LED_DIMM 'Helligkeit der blinkenden LEDs
STATUS$ = "Anfang" 'Variable zum Testen
R = 200 'R G B -Werte der UHR-LEDs falls bisher noch nicht im EEPROM gespeichert
G = 200
B = 200
ZEIT$ = "00:00:00"
TOGGLE = 0
pin.mode LED_TOP_PIN,output
gosub einstellungen_lesen
' Startposition der jeweils ersten LED fuer die
' einzelnen Stellen der Sekunden, Minuten und Stunden
' pos + LED-Anzahl + Trenner-LEDs
s2_pos = s1_pos + 9 + LED_SEC_E 'Sekunden-Zehner
m1_pos = s2_pos + 5 + LED_SEC_Z 'Minuten-Einer
m2_pos = m1_pos + 9 + LED_MIN_E 'Minuten-Zehner
h1_pos = m2_pos + 5 + LED_MIN_Z 'Stunden-Einer
h2_pos = h1_pos + 9 + LED_H_E 'Stunden-Zehner
LED_BLINK_NUN = 3
R_alt = R + 1 ' zum Erkennen neuer R G B Werte aus der GUI
G_alt = G
B_alt = B
save_stat$ = "" 'Status fuer Werte in EEProm gesichert
gosub NEOPIXEL_start
' --Hauptseite mit 1s_timer aus WEBPAGE----
STATUS$ = "Hauptschleife"
m1_alt = 99
h1_alt = 99
gosub Zeit_zerlegen
onHTMLreload WEBPAGE
onHTMLchange WEBPAGE
gosub WEBPAGE
gosub TIMER_AN
'####
WAIT
'####
end
' ############################################################## ' ## JETZT KOMMEN DIE UNTERPROGRAMME ########################### ' ##############################################################
TIMER_AN:
'Timer aktualisiert einmal pro Sekunde die NEOPIXEL-LEDs
STATUS$ = "TIMER_AN"
timer0 1000, AKTUELLE_ZEIT_ANZEIGEN
return
' ##############################################################
AKTUELLE_ZEIT_ANZEIGEN:
'####### Wird vom Timer einmal pro Sekunde aufgerufen
STATUS$ = "AKTUELLE_ZEIT_ANZEIGEN"
gosub Zeit_zerlegen
WLOG ZEIT$
print ZEIT$
gosub LEDs_setzen
STATUS$ = "AKTUELLE_ZEIT_ANZEIGEN_WAIT"
return
' ##############################################################
Zeit_zerlegen:
STATUS$ = "Zeit_zerlegen"
ZEIT$ = time$
h2 = val(mid$(ZEIT$,1,1))
h1 = val(mid$(ZEIT$,2,1))
m2 = val(mid$(ZEIT$,4,1))
m1 = val(mid$(ZEIT$,5,1))
s2 = val(mid$(ZEIT$,7,1))
s1 = val(mid$(ZEIT$,8,1))
STATUS$ = "Zeit_zerlegt"
return
' ############################################################## LEDs_setzen:
STATUS$ = "LEDs_setzen_A"
'neo.strip 0,LED_NUM,130,130,130,1
if ( R <> R_alt ) or ( G <> G_alt ) or ( B <> B_alt ) then
m1_alt = 99
h1_alt = 99
R_alt = R
G_alt = G
B_alt = B
end if
'sekunden
'--------
if s1 = 0 then
neo.strip s1_pos,s1_pos + 8,0,0,0,1
else
neo.strip s1_pos,s1_pos + s1 -1,R,G,B,1
end if
if s2 = 0 then
neo.strip s2_pos,s2_pos + 4,0,0,0,1
else neo.strip s2_pos,s2_pos + s2 -1,R,G,B,1
end if
'minuten
'-------
if m1_alt <> m1 then m1_alt = m1
if m1 = 0 then
neo.strip m1_pos,m1_pos + 8,0,0,0,1
else
neo.strip m1_pos,m1_pos + m1 -1,R,G,B,1
end if
if m2 = 0 then
neo.strip m2_pos,m2_pos + 4,0,0,0,1
else
neo.strip m2_pos,m2_pos + m2 -1,R,G,B,1
end if
end if
'Stunden
'-------
if h1_alt <> h1 then h1_alt = h1
if h1 = 0 then neo.strip h1_pos,h1_pos + 8,0,0,0,1 else neo.strip h1_pos,h1_pos + h1 -1,R,G,B,1 end if if h2 = 0 then neo.strip h2_pos,h2_pos + 4,0,0,0,1 else neo.strip h2_pos,h2_pos + h2 -1,R,G,B,1 end if end if
'#### Trenner-LEDs und Turmspitze ###### 'Trenner-LED innerhalb Sekunde (rot) neo.strip s2_pos - LED_SEC_E,s2_pos-1,LED_DIMM,0,0,1
'Trenner Sekunde//Minute (rot) neo.strip m1_pos - LED_SEC_Z, m1_pos - 1,LED_DIMM,0,0,1
'Trenner-LED innerhalb Minute (rot) neo.strip m2_pos - LED_MIN_E, m2_pos -1,LED_DIMM,0,0,1
'Trenner-LED Minute//Stunde (rot) neo.strip h1_pos - LED_MIN_Z, h1_pos -1,LED_DIMM,0,0,1
'Trenner-LED innerhalb Stunde (rot) neo.strip h2_pos - LED_H_E, h2_pos -1,LED_DIMM,0,0,1
'Trenner-LED oberhalb Stunden (rot) neo.strip h2_pos +2, h2_pos + 1 + LED_H_Z ,LED_DIMM,0,0,1
'fixe blaue LEDs ganz oben neo.strip h2_pos + 1 + LED_H_Z, LED_NUM,0,0,LED_DIMM,1
'rot blinkende Turmspitze pin(LED_TOP_PIN) = 1 - pin(LED_TOP_PIN) 'einzelne klassische 3mm-LED an Turmspitze TOGGLE=1-TOGGLE if (TOGGLE =0) then 'Helligkeit der blinkenden LEDs = AN LED_blink_DIMM = LED_DIMM save_stat$ = "" 'Loeschen des "OK" 1s nach erfolgreichem [einstellungen_schreiben] 'save_stat = "ramfree:" & ramfree() else 'Helligkeit der blinkenden LEDs = AUS LED_blink_DIMM = LED_DIMM/2 end if
'!!!Erst hier schreibt neo den bisher aufgebauten Puffer!!! neo.strip LED_BLINK_pos , LED_BLINK_POS + LED_BLINK_NUM -1,LED_BLINK_DIMM,0,0,0 '!!!ERST HIER WIRD DER NeoPixel-STREIFEN ANGESTEUERT
STATUS$ = "LEDs_setzen_E"
return
' ############################################################## ' ##############################################################
NEOPIXEL_start: '--------------- ' Initialisieren und Test der Neopixel ' !!!!!!!!!!! MIT NEOPIXEL an GPIO2 !!!!!!!!!!!!!!! STATUS$ = "NEOPIXEL_start_A" neo.setup LED_PIN,LED_NUM for i = 0 to LED_NUM neo.pixel i,200,200,200,0 pause 20 next i pause 1000 neo.strip 0,LED_NUM,0,0,0 STATUS$ = "NEOPIXEL_start_E" return
' ############################################################## WEBPAGE: STATUS$ = "WEBPAGE_A" cls autorefresh 1000 A$ = "" A$ = A$ + |<table><tbody><tr>| A$ = A$ + "<b><center>" & KOPFZEILE1$ & |</b><br>| A$ = A$ + KOPFZEILE3$ + |</center><th>| 'A$ = A$ + "<br><br>Internet-Zeit:>" + textbox$(ZEIT$,"cssTB") A$ = A$ + "<br>" + textbox$(ZEIT$,"cssTB") A$ = A$ + "<br><br><small>" A$ = A$ + "Helligkeit der Trenner-LEDs:<br>"
A$ = A$ + "->"+ slider$(LED_DIMM,0,155,"cssSL") + textbox$(LED_DIMM,"cssTB1") A$ = A$ + "<br><br>" A$ = A$ + "Farbe der Uhr-LEDs <br>" A$ = A$ + "R:" + slider$(R,0,240,"cssSL") + textbox$(R,"cssTB1") A$ = A$ + "<br>" A$ = A$ + "G:" + slider$(G,0,240,"cssSL") + textbox$(G,"cssTB1") A$ = A$ + "<br>" A$ = A$ + "B:" + slider$(B,0,240,"cssSL") + textbox$(B,"cssTB1") A$ = A$ + "<br><br>" + button$("RGB-Werte sichern", einstellungen_schreiben,"cssBT") A$ = A$ + "<br><br>" + textbox$(save_stat$,"cssTB") A$ = A$ + |</small>| A$ = A$ + |</th>| A$ = A$ + |<th width = "50%">| A$ = A$ + |<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/D%C3%BCsseldorf_Rheinturm_-_Lichtzeitpegel.jpg/500px-D%C3%BCsseldorf_Rheinturm_-_Lichtzeitpegel.jpg" alt="Bild" width=100%>| A$ = A$ + |</th>| A$ = A$ + |</tr></tbody></table>| a$ = a$ + cssid$("cssSL"," width:110px;height:2em; font-size:0.8em; ") a$ = a$ + cssid$("cssTB1"," width:40px;height:2em; text-align:center;font-size:0.8em; ") a$ = a$ + cssid$("cssTB"," width:100px;height:2em; text-align:center;font-size:1.0em; ") a$ = a$ + cssid$("cssBT"," width:180px;height:2em; text-align:center;font-size:1.0em; border-radius:1.1em; padding:.5em")
HTML a$ return
' ############################################################## ' ############################################################## ' ############################################################## ' einstellungen_lesen: STATUS$ = "Einstellungen_lesen" IF file.exists("/Rheinturm_settings.txt") = 1 then settings$ = file.read$("/Rheinturm_settings.txt") if settings$ <> "" then R = val(file.read$("/Rheinturm_R.txt")) G = val(file.read$("/Rheinturm_G.txt")) B = val(file.read$("/Rheinturm_B.txt")) LED_DIMM = val(file.read$("/Rheinturm_L.txt")) endif endif STATUS$ = "einstellungen_lesen_E" return
' ##############################################################
einstellungen_schreiben: ' schreibt die Einstellungen in einzelne Dateien im Flash STATUS$ = "Einstellungen_schreiben"
timer0 0 xxx$ = "Rheinturmuhr-Daten" file.save "/Rheinturm_settings.txt",xxx$ file.save "/Rheinturm_R.txt", str$(R) file.write "/Rheinturm_G.txt", str$(G) file.write"/Rheinturm_B.txt", str$(B) 'file.write "Rheinturm_T", str$(T) file.write "/Rheinturm_L.txt",str$(LED_DIMM) save_stat$ = "RGB gesichert" STATUS$ = "Einstellungen_geschrieben" gosub TIMER_AN
return
' ############################################################## ' ##############################################################
TestExit: timer0 0 STATUS$ = "--- E N D E ---" A$ = A$ + "<br>" & STATUS$ save_stat$ = " E N D E "
neo.strip 0,59,10,0,0 for i = 59 to 2 step -1 neo.pixel i,0,0,0,0 next i
neo.strip 0,1,100,100,100 PAUSE 500 neo.strip 0,1,0,0,5 '!!!!!!!!!!!!!!!!!! END '!!!!!!!!!!!!!!!!!!
Build This Project
Bring this design to life with the Elektor PCB Service, powered by Eurocircuits. Upload the project files and order professionally manufactured PCBs or assembled boards through a proven European production platform.
Supporting KiCad, Eagle, Gerber, and ODB++ formats, the service is suitable for everything from prototypes and validation builds to series production and volume manufacturing.
Made in Europe. Fast. Reliable. Professional.

Diskussion (0 Kommentare)