Wie man die Zeit von Time Server aus dem Internet lädt und mit dem neuen Raspberry Pi Pico W auf einem LCD-Display per I²C anzeigen lassen kann, möchte ich dir in diesem ausführlichen Beitrag zeigen.
Den neuen Raspberry Pi Pico W mit WiFi Support habe ich dir bereits in folgenden Beiträgen vorgestellt und einige kleine Projekte gezeigt.
- Raspberry Pi Pico W mit Wi-Fi Support
- Raspberry Pi Pico W – Webserver programmieren
- Raspberry Pi Pico W mit Infineon CYW43438 Chip
- Raspberry Pi Pico W – anzeigen von Sensordaten auf einer Webseite
- Raspberry Pi Pico W – Webseite ins Internet veröffentlichen
Benötigte Ressourcen für dieses Projekt
Für dieses Projekt benötigst du, bzw. habe ich verwendet:
- einen Raspberry Pi Pico W*,
- ein Micro-USB Datenkabel*,
- ein 2×16 Zeichen LCD-Display* mit I²C Schnittstelle,
- diverse Breadboardkabel*
Hinweis von mir: Die mit einem Sternchen (*) markierten Links sind Affiliate-Links. Wenn du über diese Links einkaufst, erhalte ich eine kleine Provision, die dazu beiträgt, diesen Blog zu unterstützen. Der Preis für dich bleibt dabei unverändert. Vielen Dank für deine Unterstützung!
Aufbau der Schaltung, Raspberry Pi Pico W mit LCD-Display
In diesem Beitrag verwende ich ein einfaches 2×16 Zeichen LCD-Display mit I²C Schnittstelle. Es gibt jedoch auch größere Displays mit mehr Zeilen & Zeichen, wo du dann auch mehr Informationen anzeigen lassen kannst.
Der Raspberry Pi Pico W hat 2 I²C Schnittstellen, welche du über die nachfolgenden Pins erreichen kannst:
- I²C 0
- SDA – GP0, GP8, GP12, GP16, GP20
- SCL – GP1, GP9, GP13, GP17, GP21
- I²C 1
- SDA – GP2, GP6, GP10, GP14, GP18, GP26
- SCL – GP3, GP7, GP11, GP15, GP19, GP27
Pinout des Raspberry Pi Pico (W)
Der neue Raspberry Pi Pico W ist mit dem Vorgängermodell von den Pins identisch, d.h. du kannst deine bestehenden Projekte recht einfach um die WiFi Fähigkeit erweitern.
Erweiterungsplatine für den Raspberry Pi Pico
Für das Vorgängermodell dem Raspberry Pi Pico von 2021 habe ich ein kleines Board entwickelt welches mit die I²C Pins sowie ein paar GPIOs auf Groove Konnektoren bereitstellt.
I²C Adapter für LCD-Display
Das verwendete LCD-Display hat einen kleinen I²C Adapter auf der Rückseite verbaut. Dieser Adapter verfügt über die Pins SDA, SCL, VCC und GND sowie über einen Drehpotentiometer (blaues Bauteil im Bild) über welchen die Helligkeit der Schrift gesteuert werden kann.
Programmieren des LCD-Displays in der Thonny IDE mit MicroPython
Für die Programmierung nutze ich die Thonny IDE welche kostenfrei unter https://thonny.org/ für Microsoft Windows, macOS & auch Linux heruntergeladen werden kann.
Download & kopieren des Modules für das LCD-Display
Für die Programmierung des LCD-Displays verwende ich ein Modul, welches vom GitHub Repository dhylands/python_lcd geladen werden kann.
Von diesem Repository benötigen wir jedoch nur die beiden Dateien:
- https://github.com/dhylands/python_lcd/blob/master/lcd/lcd_api.py
- https://github.com/dhylands/python_lcd/blob/master/lcd/machine_i2c_lcd.py
Diese beiden Dateien müssen wir auf dem Pi Pico im Ordner “lib” speichern. Da wir jedoch keinen direkten Zugriff auf diesem Ordner haben, müssen wir die beiden Dateien in einem Editor öffnen (zbsp. Notepad++). Im geöffneten Editor markieren und kopieren, wir den gesamten Text in einem geöffneten Tab in der Thonny IDE und speichern dieses in den Ordner “lib” unter den jeweiligen Dateinamen.
Troubleshooting
Beim Programmieren sind mir einige Fehlermeldungen ausgegeben worden, welche ich dir hier gerne zeigen möchte.
OSError 28
Diesen Fehler erhältst du, wenn der Speicher des Mikrocontrollers aufgebraucht ist. D.h. du musst Speicher freigeben, um diesen Fehler zu beheben.
Dieses können zbsp. nicht benötigte Bibliotheken sein, welche auf dem Mikrocontroller abgelegt wurden.
ValueError: bad SCL pin / bad SDA pin
Die konfigurierte I²C Verbindung ist nicht lesbar. Hier musst du deine Verkabelung und die verwendete Adresse prüfen.
Quellcode – “Hello World!” auf dem LCD-Display
Schreiben wir zunächst in jede Zeile des Displays ein paar Zeichen.
from time import sleep from machine import I2C, Pin from machine_i2c_lcd import I2cLcd i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=100000) lcd = I2cLcd(i2c, 0x27, 2, 16) zeile1 = 'Hello World!'; zeile2 = 'from Pi Pico W' lcd.putstr(zeile1 + "\n" + zeile2) sleep(2)
Auslesen einer Zeit vom Server
Als Dienst nutze ich den Server http://worldtimeapi.org welchen wir in der URL die Zeitzone übergeben wie zbsp. http://worldtimeapi.org/api/timezone/Europe/Berlin und als Antwort ein JSON mit den Daten erhalten.
Damit wir diesen HTTP Request absenden und einen Response vom Server empfangen können, müssen wir uns zu einem bestehenden WLAN Netzwerk verbunden haben. Wie du das machst, habe ich dir bereits im Beitrag Raspberry Pi Pico W – Webserver programmieren gezeigt.
Hier der Vollständigkeit der Code zum Herstellen einer Verbindung zu einem WLAN Netzwerk.
import network import time ssid = '*****' password = '*******' wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) print("Waiting to connect:") while not wlan.isconnected() and wlan.status() >= 0: print(".", end="") time.sleep(1) print("") print(wlan.ifconfig())
Wenn diese Verbindung aufgebaut ist, dann müssen wir einen Request an die oben genannte Adresse senden und den Response (ein JSON) auswerten.
Die Funktion zum Absenden des HTTP Requests und Empfangen des HTTP Response habe ich aus der offiziellen Dokumentation unter https://docs.micropython.org/en/latest/esp8266/tutorial/network_tcp.html entnommen.
# Funktion zum absenden eines HTTP Request und # Rückgabe des HTTP Response # Quelle: https://docs.micropython.org/en/latest/esp8266/tutorial/network_tcp.html def http_get(url): result = '' _, _, host, path = url.split('/', 3) addr = socket.getaddrinfo(host, 80)[0][-1] s = socket.socket() s.connect(addr) s.send(bytes('GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (path, host), 'utf8')) while True: data = s.recv(100) if data: result = result + str(data, 'utf8') else: break s.close() return result # Ermitteln des JSONs aus dem HTTP Response def findJson(response): txt = 'abbreviation' return response[response.find(txt)-2:]
Aus diesem JSON Response können wir nun diverse Informationen entnehmen.
In meinem Fall interessiere ich mich für den UNIX Timestamp “unixtime”.
{
"abbreviation":"CEST",
"client_ip":"80.128.77.199",
"datetime":"2022-07-25T14:22:21.415593+02:00",
"day_of_week":1,
"day_of_year":206,
"dst":true,
"dst_from":"2022-03-27T01:00:00+00:00",
"dst_offset":3600,
"dst_until":"2022-10-30T01:00:00+00:00",
"raw_offset":3600,
"timezone":"Europe/Berlin",
"unixtime":1658751741,
"utc_datetime":"2022-07-25T12:22:21.415593+00:00",
"utc_offset":"+02:00",
"week_number":30
}
Damit die Daten aus dem JSON (eigentlich ein einfacher String) ausgelesen werden kann, wandeln wir dieses in ein Dictionary um.
import json aDict = json.loads(jsonData)
Nun können wir auf die Werte recht einfach mit dem entsprechenden Key zugreifen und den UNIX Timestamp in ein Tupel mit den Datum & Zeit Werten umwandeln.
# parsen des Zeitstempels # Parameter ist das JSON als Dictionary def parseDateTimeStr(responeDict): # umwandeln des UNIX Timestamp in eine Liste aus Datum & Zeit Werten dateTime = time.localtime(int(responeDict['unixtime'])) year = dateTime[0] month = dateTime[1] dayOfMonth = dateTime[2] # Wenn der Monat kleiner als 10 ist, # dann eine führende Null anhängen if month < 10: month = str('0' + str(month)) if dayOfMonth < 10: dayOfMonth = str('0' + str(dayOfMonth)) dateStr = str(dayOfMonth)+'.'+str(month)+'.'+str(year) hour = dateTime[3] minutes = dateTime[4] # offset für die Uhrzeit auslesen timeOffset = responeDict['utc_offset'] # Wenn der Offset mit einem Minus beginnt # dann soll der Wert abgezogen werden if timeOffset[0:1] == '-': hour = hour - int(timeOffset[1:3]) elif timeOffset[0:1] == '+': # beginnt der Offset mit einem "+" # dann soll die Zeit addiert werden hour = hour + int(timeOffset[1:3]) if hour < 10: hour = str('0' + str(hour)) if minutes < 10: minutes = str('0' + str(minutes)) timeStr = str(hour)+':'+str(minutes) # auslesen der Zeitzone timezone = responeDict['timezone'] # zurückgeben der Zeitzone, des Datums sowie der Uhrzeit return timezone, dateStr, timeStr
Die ermittelten Werte für timezone, dateStr und timeStr speichern wir uns in einer Variable welche wir auf dem Display dann mit der Funktion “displayDateTime” anzeigen lassen wollen.
In der Funktion wird die Zeichenkette für die Timezone in min. 16 Zeichen umgewandelt bzw. es werden entsprechende Leerzeichen ergänzt.
Der Hintergrund ist das wenn es weniger Zeichen sind, dann wird kein vernünftiger & zuverlässiger Zeilenumbruch gemacht. Das ist vor allem beim Aktualisieren des LCD-Displays aufgefallen.
# parsen des Zeitstempels aus dem Dictionary timezone, dateStr, timeStr = parseDateTimeStr(aDict) # ausgeben der Werte auf dem LCD-Display displayDateTime(timezone, dateStr, timeStr) # Anzeigen der Daten auf dem Display # Hinweis: Wenn die Zeichen der Zeitzone # länger als 16 Zeichen ist, dann wird # diese automatisch umgebrochen! def displayDateTime(timezone, dateStr, timeStr): timezone = "{:<16}".format(timezone) lcd.putstr(timezone +"\n" +dateStr + " " + timeStr) time.sleep(2)
Die Main Funktion mit den Aufrufen der einzelnen Funktionen und die Endlosschleife.
# Main Funktion def main(): # Aufbau der Wifi-Verbindung wifiConnect() # Auslesen des HTTP Response response = http_get(url) # ermitteln des JSONs jsonData = findJson(response) # umwandeln des JSONs in ein Dictionary aDict = json.loads(jsonData) # parsen des Zeitstempels aus dem Dictionary timezone, dateStr, timeStr = parseDateTimeStr(aDict) # ausgeben der Werte auf dem LCD-Display displayDateTime(timezone, dateStr, timeStr) # Aufrufen der Funktion main() while True: main() # eine Pause von 60 Sekunden einlegen # Der Server leht die Verbindung ab wenn der Intervall # des Zugriffs zu klein ist. time.sleep(60)
Fertiges Programm – leser der Zeit aus dem Internet und anzeigen mit einem Raspberry Pi Pico W
Hier nun das fertige Programm zum einfachen Download:
import network import socket import time import json from machine import I2C, Pin from machine_i2c_lcd import I2cLcd #Zugangsdaten zum WLAN Netzwerk ssid = 'FRITZBox7590GI_EXT' password = '22894580214767401850' #LCD-Display lines = 2 cols = 16 i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=100000) lcd = I2cLcd(i2c, 0x27, lines, cols) #Adresse welche uns das JSON mit den Zeitdaten liefert service = 'http://worldtimeapi.org/api/timezone/' url = service + 'Europe/Berlin' # Aufbau einer WiFi Verbindung def wifiConnect(): wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) print("Waiting to connect:") while not wlan.isconnected() and wlan.status() >= 0: print(".", end="") time.sleep(1) print("") print(wlan.ifconfig()) # Funktion zum absenden eines HTTP Request und # Rückgabe des HTTP Response # Quelle: https://docs.micropython.org/en/latest/esp8266/tutorial/network_tcp.html def http_get(url): result = '' _, _, host, path = url.split('/', 3) addr = socket.getaddrinfo(host, 80)[0][-1] s = socket.socket() s.connect(addr) s.send(bytes('GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (path, host), 'utf8')) while True: data = s.recv(100) if data: result = result + str(data, 'utf8') else: break s.close() return result # Ermitteln des JSONs aus dem HTTP Response def findJson(response): txt = 'abbreviation' return response[response.find(txt)-2:] # parsen des Zeitstempels # Parameter ist das JSON als Dictionary def parseDateTimeStr(responeDict): # umwandeln des UNIX Timestamp in eine Liste aus Datum & Zeit Werten dateTime = time.localtime(int(responeDict['unixtime'])) year = dateTime[0] month = dateTime[1] dayOfMonth = dateTime[2] # Wenn der Monat kleiner als 10 ist, # dann eine führende Null anhängen if month < 10: month = str('0' + str(month)) if dayOfMonth < 10: dayOfMonth = str('0' + str(dayOfMonth)) dateStr = str(dayOfMonth)+'.'+str(month)+'.'+str(year) hour = dateTime[3] minutes = dateTime[4] # offset für die Uhrzeit auslesen timeOffset = responeDict['utc_offset'] # Wenn der Offset mit einem Minus beginnt # dann soll der Wert abgezogen werden if timeOffset[0:1] == '-': hour = hour - int(timeOffset[1:3]) elif timeOffset[0:1] == '+': # beginnt der Offset mit einem "+" # dann soll die Zeit addiert werden hour = hour + int(timeOffset[1:3]) if hour < 10: hour = str('0' + str(hour)) if minutes < 10: minutes = str('0' + str(minutes)) timeStr = str(hour)+':'+str(minutes) # auslesen der Zeitzone timezone = responeDict['timezone'] # zurückgeben der Zeitzone, des Datums sowie der Uhrzeit return timezone, dateStr, timeStr # Anzeigen der Daten auf dem Display # Hinweis: Wenn die Zeichen der Zeitzone # länger als 16 Zeichen ist, dann wird # diese automatisch umgebrochen! def displayDateTime(timezone, dateStr, timeStr): timezone = "{:<16}".format(timezone) lcd.putstr(timezone +"\n" +dateStr + " " + timeStr) time.sleep(2) # Main Funktion def main(): # Aufbau der Wifi-Verbindung wifiConnect() # Auslesen des HTTP Response response = http_get(url) # ermitteln des JSONs jsonData = findJson(response) # umwandeln des JSONs in ein Dictionary aDict = json.loads(jsonData) # parsen des Zeitstempels aus dem Dictionary timezone, dateStr, timeStr = parseDateTimeStr(aDict) # ausgeben der Werte auf dem LCD-Display displayDateTime(timezone, dateStr, timeStr) # Aufrufen der Funktion main() while True: main() # eine Pause von 60 Sekunden einlegen # Der Server leht die Verbindung ab wenn der Intervall # des Zugriffs zu klein ist. time.sleep(60)