Die perfekte Ergänzung zur DIY Wake-Up Lampe ist natürlich eine Uhr.

Mit einem 32×8 LED-Dot-Matrix-Display für nicht mal 10 Euro kannst Du nicht nur eine schöne Uhr realisieren, sondern auch eine Laufschrift, mit der Informationen jeglicher Art darstellgestellt werden können. In meinem kleinen Beispiel nutze ich den Ticker, um das Datum, aktuelle Nachrichten oder meinen YouTube Abonnenten Counter anzuzeigen.

Uhr mit Rollenzähler Animation

Für die Ansteuerung der LED Matrix verwende ich die Bibliothek luma.led_matrix.

Jedes 8×8 Segment der LED-DotMatrix wird durch einen MAX7219 IC angesteuert. Dafür muss beim Raspberry Pi die SPI Schnittstelle aktiviert werden. Das geht durch Ausruf von

sudo raspi-config

Dort kann die SPI Schnittstelle im folgenden Menü aktiviert werden:

3 Interfacing Options
P4 SPI

Die luma.led_matrix Bibliothek benötigt außerdem einige andere Bibliotheken, von denen einige bereits auf dem Raspberry Pi vorinstalliert sein sollten:

sudo apt install build-essential
sudo apt install python3-dev
sudo apt install python3-pip
sudo apt install libfreetype6-dev
sudo apt install libjpeg-dev
sudo apt install libopenjp2-7
sudo apt install libtiff5

Die Installation der verwendeten Bibliothek luma.led_matrix auf dem Raspberry Pi und der Download des Sourcecodes, der auch das Beispiel enthält, erfolgt mit:

sudo -H pip3 install --upgrade luma.led_matrix
git clone https://github.com/rm-hull/luma.led_matrix luma.led_matrix

Anschluss LED Matrix

Mit dem Source Code kommt auch das Beispiel “silly_clock” von ttsiodras, welches mir als Inspiration für meine Uhr gedient hat. Meine Implementierung MyClock.py ist aber vollständig neu coderit und hat ein paar grundlegende Änderungen:

  • geänderte Ausrichtung: Stunden rechtsbündig zum Doppelpunkt ausgerichtet und Minuten Linksbündig zum Doppelpunkt ausgerichtet
  • Rollenzähler Animation für Stunden und Minuten
  • Ausführung in einem eigenen Thread, aus einem Hauptprogramm zum Beispiel LED-Strip und LED-Matrix Display gleichzeitig angesteuert werden können
  • Interface für die Anzeige einer beliebigen Laufschrift
  • Interface zum ausschalten der Uhr (z.B. Nachts)
Youtube Abonnenten Counter

Hier der Source Code für RollingClock.py:

#!/usr/bin/env python3

# RollingClock by joe703 / https://www.youtube.com/channel/UChMi8gAr52_jZXIpr9WXYQQ
# Inspired by luma.led_matrix/examples/silly_clock.py by ttsiodras
# https://github.com/rm-hull/luma.led_matrix/blob/master/examples/silly_clock.py

import threading
import time
from datetime import datetime

from luma.led_matrix.device import max7219
from luma.core.interface.serial import spi, noop
from luma.core.render import canvas
from luma.core.legacy import text, show_message
from luma.core.legacy.font import proportional, CP437_FONT, TINY_FONT

class RollingClock(threading.Thread):

    def __DrawNumber(self, draw, StringOld, StringNew, PosOld, PosNew, Iterator):
        # Draws and animates Number - Iterator has to be increased from 0 (old String) to 8 (NewString)
        if Iterator > 8:
            Iterator = 8
        if Iterator < 8:
            text(draw, (PosOld, 1 - Iterator), StringOld, fill="white", font=proportional(CP437_FONT))
        if Iterator > 0:
            text(draw, (PosNew, 9 - Iterator), StringNew, fill="white", font=proportional(CP437_FONT))
            
    def __DrawColon(self, draw, StringOld, StringNew, Iterator):
        # Draws and animates colon - Iterator has to be increased from 0 (old String) to 8 (NewString)
        if StringOld == ":":
            text(draw, (15, 1 - Iterator), ":", fill="white", font=proportional(TINY_FONT))
        if StringNew == ":":
            text(draw, (15, 9 - Iterator), ":", fill="white", font=proportional(TINY_FONT))
            
    def __GetHourPos(self, hours):
        # Returns position for hours with CP437_FONT
        if hours == 0 or hours == 4:
            return 7
        elif hours < 10:
            return 8
        elif hours == 10 or hours == 14 or hours == 20:
            return 0
        else:
            return 1
            
    def __init__(self):
        # Init Own Thread for Clock
        threading.Thread.__init__(self)
        # Init LED Matrix
        serial = spi(port=0, device=0, gpio=noop())
        self.device = max7219(serial, cascaded=4, block_orientation=90, blocks_arranged_in_reverse_order=True)
        self.device.contrast(16)
    
    def run(self):
        # Clock is running
        self.__RunClock = True
        self.__ShowClock = True
        self.__DisplayText=""

        # Toggle the second indicator every second
        toggle = False  

        while self.__RunClock:
            # Init Time
            CurrentTime = datetime.now()
            MinutesStr = CurrentTime.strftime('%M')
            MinutesStrOld = MinutesStr
            HoursStr = CurrentTime.strftime('%-H')
            HoursStrOld = HoursStr
            HoursPos = self.__GetHourPos(CurrentTime.hour)
            HoursPosOld = HoursPos
            
            # Scroll in Clock
            for i in range(0,9):
                with canvas(self.device) as draw:
                    self.__DrawNumber(draw, "", HoursStr, HoursPos, HoursPos, i)
                    self.__DrawColon(draw, "", ":", i)
                    self.__DrawNumber(draw, "", MinutesStr, 17, 17, i)
                time.sleep(0.1)

            while (self.__ShowClock==True and self.__DisplayText==""):
                # Get New Time
                CurrentTime = datetime.now()
                MinutesStr = CurrentTime.strftime('%M')
                HoursStr = CurrentTime.strftime('%-H')
                HoursPos = self.__GetHourPos(CurrentTime.hour)
                
                # Handle special cases for right alignemnt of hours in CP437_FONT
                HoursPos = self.__GetHourPos(CurrentTime.hour)
                if (MinutesStr != MinutesStrOld or HoursStr != HoursStrOld):
                    # Time changed
                    for i in range(0,9):
                        if i == 5:
                            # toggle colon
                            toggle = not toggle
                        if HoursStr != HoursStrOld:
                            # Animate Hours and Minutes
                            with canvas(self.device) as draw:
                                self.__DrawNumber(draw, HoursStrOld, HoursStr, HoursPosOld, HoursPos, i)
                                self.__DrawColon(draw, ":" if toggle else " ", " ", 0)
                                self.__DrawNumber(draw, MinutesStrOld, MinutesStr, 17, 17, i)
                        elif MinutesStr[0] != MinutesStrOld[0]:
                            # Animate 2 digit Minute Update
                            with canvas(self.device) as draw:
                                self.__DrawNumber(draw, HoursStrOld, HoursStr, HoursPos, HoursPos, 0)
                                self.__DrawColon(draw, ":" if toggle else " ", " ", 0)
                                self.__DrawNumber(draw, MinutesStrOld, MinutesStr, 17, 17, i)
                        else:
                            # Animate 1 digit Minute Update
                            with canvas(self.device) as draw:
                                self.__DrawNumber(draw, HoursStrOld, HoursStr, HoursPos, HoursPos, 0)
                                self.__DrawColon(draw, ":" if toggle else " ", " ", 0)
                                self.__DrawNumber(draw, MinutesStr[0],
                                                  MinutesStr[0], 17, 17, 0)
                                # If we don't draw digit 1 we need to check it for the position
                                if MinutesStr[0] == "0" or MinutesStr[0] == "4":
                                    self.__DrawNumber(draw, MinutesStrOld[1], MinutesStr[1], 25, 25, i)
                                else:
                                    self.__DrawNumber(draw, MinutesStrOld[1], MinutesStr[1], 24, 24, i)
                        time.sleep(0.1)
                else:
                    # Redraw Time to toggle colon
                    with canvas(self.device) as draw:
                        self.__DrawNumber(draw, HoursStr, HoursStr, HoursPos, HoursPos, 0)
                        self.__DrawColon(draw, ":" if toggle else " ", " ", 0)
                        self.__DrawNumber(draw, MinutesStr, MinutesStr, 17, 17, 0)
                    time.sleep(0.5)

                # Store Time
                MinutesStrOld = MinutesStr
                HoursStrOld = HoursStr
                HoursPosOld = HoursPos

                # toggle colon
                toggle = not toggle

            # Scroll out Clock
            for i in range(0,9):
                with canvas(self.device) as draw:
                    self.__DrawNumber(draw, HoursStr, "", HoursPos, HoursPos, i)
                    self.__DrawColon(draw, ":", "", i)
                    self.__DrawNumber(draw, MinutesStr, "", 17, 17, i)
                time.sleep(0.1)
                
            # Wait and show text
            while self.__RunClock and ((self.__ShowClock==False) or (self.__DisplayText!="")):
                if self.__DisplayText!="":
                    show_message(self.device, self.__DisplayText, fill="white", font=proportional(CP437_FONT))
                    self.__DisplayText=""
                time.sleep(0.5)

    def Show(self):
        self.__ShowClock = True

    def Hide(self):
        self.__ShowClock = False
        
    def ShowText(self, text):
        # Replace special characters, whch are not available in font
        chars = {'ö':'oe','ä':'ae','ü':'ue','Ö':'Oe','Ä':'Ae','Ü':'ue','ß':'ss'}
        for char in chars:
            text = text.replace(char,chars[char])
        # Check if text can be displayed and request output if not busy
        if self.__DisplayText=="":
            self.__DisplayText = text
            print("Textausgabe angefordert: "+text)
            return True
        else:
            print("Textausgabe nicht moeglich: "+text)
            return False
            
    def close(self):
        self.__ShowClock = False
        self.__RunClock = False
        
if __name__ == "__main__":
    Wochentag=["Sonntag ", "Montag ", "Dienstag ", "Mittwoch ", "Donnerstag ", "Freitag ", "Samstag "]

    # Main for Test
    Uhr = RollingClock() 
    Uhr.start()
    print("Uhr gestartet")
    print("Press Ctrl-C to quit.")
    try:
        while True:
            datum = datetime.now().strftime('%d.%m.%Y')
            tag = datetime.now().strftime('%w')
            sekunden = datetime.now().strftime('%S')

            if sekunden=="05":
                # show Date
                Uhr.ShowText(Wochentag[int(tag)] + datum)
                    
            time.sleep(1)
    except KeyboardInterrupt:
        pass    
    Uhr.close()
    print("Stop der Uhr angefordert")
    Uhr.join()
    print("Uhr gestoppt")

Mit start() wird der Thread und die Uhr gestartet. Mit Hide() wird die Uhr ausgeschaltet (Anzeige der Laufschrift ist weiter möglich) und mit Show() wieder eingeschaltet. Mit ShowText() kann ein Text als Laufschrift angezeigt werden. Mit close() wird der Thread und die Uhr beendet.

Mit dem folgenden Python Script wird die RollingClock aufgerufen und es werden verschiedene Informationen als Laufschrift angezeigt. Zum parsen von RSS Dateien (und vielen anderen Formaten) verwende ich feedparser. Feedparser wird mit

pip3 install feedparser

installiert.

Google stellt Basis Informationen für den Youtube Kanal als JSON-File zur Verfügung. Für den YouTube Abonnenten Counter sind “___YOUTUBE-KANAL-ID___” und “___GOOGLE-API-KEY___” durch die Kanal ID des YouTube Kanals mit dem zugehörigen API-Key und “Joe 703 / raspberry.py” durch den Kanalnamen zu ersetzen.

Hier der Source Code für RollingClockTicker.py, meiner Beispielimplementierung für einen Newsticker mit Schlagzeilen von Tagesschau.de und einem YouTube Abonnenten Counter.

#!/usr/bin/env python3

# RollingClockTicker by joe703 / https://www.youtube.com/channel/UChMi8gAr52_jZXIpr9WXYQQ

import time
from datetime import datetime
import json, urllib.request
import feedparser
import RollingClock

def ReadYoutubeSubscriberCounter():
    try:
        url = "https://www.googleapis.com/youtube/v3/channels?part=statistics&id=___YOUTUBE-KANAL-ID___&key=___GOOGLE-API-KEY___"
        res = urllib.request.urlopen(url).read().decode('utf-8')
        data = json.loads(res)   
        SubscriberText = data['items'][0]['statistics']['subscriberCount']
    except:
        SubscriberText = "???"
    return "Joe 703 / raspberry.py: " + SubscriberText + " Abonnenten"

def ReadNews():
    try:
        NewsFeed = feedparser.parse("https://www.tagesschau.de/xml/rss2_https/")
        NewsText = NewsFeed.entries[0].title+": "+NewsFeed.entries[0].content[1].value
    except:
        NewsText = "Keine News ???"
    return NewsText 

if __name__ == "__main__":
    Wochentag=["Sonntag ", "Montag ", "Dienstag ", "Mittwoch ", "Donnerstag ", "Freitag ", "Samstag "]
    OldNewsText=""
    OldSubscriberText=""

    # Main for Test
    Uhr = RollingClock.RollingClock() 
    Uhr.start()
    
    print("Uhr gestartet")
    print("Press Ctrl-C to quit.")
    try:
        while True:
            datum = datetime.now().strftime('%d.%m.%Y')
            tag = datetime.now().strftime('%w')
            sekunden = datetime.now().strftime('%S')
            if sekunden=="05":
                # show Youtube Subscriber Counter if changed
                SubscriberText = ReadYoutubeSubscriberCounter()
                if SubscriberText != OldSubscriberText:
                    if Uhr.ShowText(SubscriberText):
                        OldSubscriberText=SubscriberText

                # show feed if changed
                NewsText = ReadNews()
                if NewsText != OldNewsText:
                    if Uhr.ShowText(NewsText):
                        OldNewsText=NewsText

                # show Date (if not busy with showing something else)
                Uhr.ShowText(Wochentag[int(tag)] + datum)
                    
            time.sleep(1)
    except KeyboardInterrupt:
        pass    
    Uhr.close()
    print("Stop der Uhr angefordert")
    Uhr.join()
    print("Uhr gestoppt")

Youtube Video

Das Video dazu auf meinem YouTube-Kanal:

Raspberry Pi Tutorial – LED-Matrix Uhr

Amazon Links

*Werbung! Wenn Du auf einen der Amazon Links klickst und anschließend ein beliebiges Produkt auf Amazon kaufst, unterstützt Du meine Seite mit einem kleinen Anteil. Dir entstehen dadurch KEINE Mehrkosten und Du zahlst den ganz normalen Preis. Danke!

LED Dot Matrix Display
MAX7219 8×32 Dot Matrix LED Anzeigemodul – https://amzn.to/3izeRiG

Raspberry Pi 3
Raspberry Pi 3 Model B – https://amzn.to/3o5kWou
Netzteil 3A mit Schalter – https://amzn.to/3pcK6D2

Alternativ: Raspberry Pi 3 Starter Kit
Raspberry Pi 3 Model B Starter Kit inkl. SD-Karte vorinstalliert mit Raspbian OS – https://amzn.to/2M9Zt0b

Raspberry Pi Zubehör
Raspberry Pi Gehäuse – https://amzn.to/399Te5P
16GB microSD Karte – https://amzn.to/2KAdzaK
Steckbrett (3 Stück) – https://amzn.to/3pjimN7