5566

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

This project might have a bit of local flavor
elektor


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:
elektor

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

img-8911.jpeg


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

img-8914.jpeg


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 '!!!!!!!!!!!!!!!!!!