Willkommen zu meinem heutigen Beitrag auf meinem Technikblog! Vor kurzem habe ich einen interessanten Kommentar von einem aufmerksamen Leser erhalten. Er stellte die Frage, wie man mehrere Sensordaten, insbesondere Temperaturwerte, auf einer Webseite darstellen kann. Eine faszinierende Idee, die mich dazu inspiriert hat, diesen Beitrag zu verfassen. In diesem Artikel werde ich dir zeigen, wie du genau das mit dem DS18B20-Sensor und dem Raspberry Pi Pico W erreichen kannst. Zusammen werden wir lernen, wie man die Sensoren anschließt, die Daten ausliest und sie auf einer Webseite in ansprechender Weise präsentiert.
Im Beitrag Raspberry Pi Pico W – anzeigen von Sensordaten auf einer Webseite habe ich dir bereits erläutert, wie man die Sensordaten eines BME280 Sensors auf einer Webseite visualisiert. Hier möchte ich teile des Quellcodes wiederverwenden und dir aufzeigen, wie man die Sensordaten von mehreren DS18B20 Sensoren am Pi Pico auf einer Webseite anzeigen kannst.
Inhaltsverzeichnis
- Aufbau der Schaltung am Raspberry Pi Pico W
- Auslesen der Sensoren in Micropython
- Anzeigen der Sensorwerte von mehreren DS18B20 am Pi Pico auf einer Webseite
- Mögliche Erweiterung – minimale, maximale, sowie die Durchschnittstemperatur anzeigen
- Download des Quellcodes für das Anzeigen der Sensorwerte von mehreren DS18B20 am Pi Pico auf einer Webseite
Aufbau der Schaltung am Raspberry Pi Pico W
Der Raspberry Pi Pico / Pico W hat 26 GPIOs (davon 6 analoge Pins) somit können wir eine Menge an Sensoren anschließen und auf der Seite anzeigen. In meinem Fall möchte ich 6 Temperatursensoren anschließen und benötige für die Schaltung:
- einen Raspberry Pi Pico W*,
- ein Micro-USB Datenkabel*,
- sechs Temperatursensoren DS18B20*,
- einen 4,7 kOhm Widerstand*,
- ein paar Breadboardkabel*, männlich-männlich, 10 cm,
- ein 830 Pin Breadboard*
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!
Die Abbildung verdeutlicht, dass die digitalen Pins der Temperatursensoren in Serie geschaltet sind und nur einen GPIO-Anschluss des Pi Pico verwenden (während die Spannungsversorgung parallel erfolgt).
Jeder Temperatursensor vom Typ DS18B20 hat eine eigene einzigartige ID über welche dieser referenziert werden kann und somit können wir quasi fast beliebig viele Sensoren hintereinander schalten. Wir müssen lediglich darauf achten, die maximale Stromaufnahme pro GPIO (16 mA) nicht zu überschreiten.
Auslesen der Sensoren in Micropython
Zunächst schreiben wir ein kleines Programm in Micropython um die Sensoren auszulesen.
#Import der benötigten Module
#zum Zugriff auf den Sensor DS18B20
#und der Pins des Pi Pico
import machine, onewire, ds18x20, time
#der Sensor ist am GPIO16 angeschlossen
sensorPin = machine.Pin(16)
#initialisieren eines Objektes vom Typ DS18X20
ds18b20Sensor = ds18x20.DS18X20(onewire.OneWire(sensorPin))
#anlegen eines Feldes für die IDs der Sensoren
roms = None
def setup():
#Zugriff auf die Globale Variable roms
global roms
#auslesen der IDs der Sensoren
roms = ds18b20Sensor.scan()
#ausgeben der Anzahl der Sensoren auf
#der seriellen Schnittstelle
print('Anzahl gefundener Sensoren: ', len(roms))
def main():
#Zugriff auf die Globale Variable roms
global roms
#starten einer Endlosschleife, ...
while True:
#auslesen der Sensorwerte
ds18b20Sensor.convert_temp()
#nach dem aufruf der Funktion "convert_temp()"
#soll man gem. Dokumentation 750ms warten
time.sleep_ms(750)
#erzeugen eines Indexes
idx = 0
#Schleife über die gefundenen IDs / Sensoren
for rom in roms:
#Index um eins erhöhen
idx = idx + 1
#Ausgeben der Temperatur, zusätzlich wird noch der Temperaturwert
#auf 2 Stellen nach dem Komma gekürzt
print("#", str(idx), " Temperatur:", round(ds18b20Sensor.read_temp(rom),2), "°C")
#Pause von 5 Sekunden
time.sleep(5)
setup()
main()
Wenn wir diesen Code auf dem Pi Pico / Pico W ausführen, dann sollten wir in der Konsole nun die Werte der Sensoren ablesen können.
Anzeigen der Sensorwerte von mehreren DS18B20 am Pi Pico auf einer Webseite
Im nächsten Schritt wollen wir diese Sensorwerte jetzt auf einer kleinen Webseite anzeigen lassen. Zusätzlich möchte ich einen Schritt weitergehen und ein Diagramm anzeigen lassen, in welchem die Sensordaten visualisiert werden.
Schritt 1 – Aufbau des Codes
Zunächst erzeugen wir uns zwei Funktionen
setup: Diese Funktion wird einmalig aufgerufen.
main: Diese Funktion beinhaltet den Ablauf des Programmes.
Wenn du bereits Erfahrung mit dem Arduino gesammelt hast, dann wird dir dieses nicht fremd sein.
Der Vorteil ist, dass man hier besser nachvollziehen kann, wann was gestartet wird.
def setup():
pass
def main():
pass
setup()
main()
Schritt 2 – Aufbau der WiFi-Verbindung
Im ersten Schritt bauen wir die WiFi-Verbindung auf. Dazu benötigen wir die SSID sowie das Passwort für das lokale WiFi-Netzwerk. Diese beiden Werte legen wir uns in Felder im Code ab.
#Import der benötigten Module
#für den Aufbau der WiFi-Verbindung
import network
import socket
ssid = 'FRITZBox7590GI24'
password = '22894580214767401850'
#anlegen eines Feldes für die WiFi-Verbindung
wlan = None
#anlegen eines Feldes für die Socketverbindung
s = None
def doConnect():
global wlan, s
wlan=network.WLAN(network.STA_IF)
wlan.active(True)
#Wenn die Verbindung nicht erstellt wurde, dann...
if not wlan.isconnected():
#Aufbau der WiFi-Verbindung mit den Daten
wlan.connect(ssid, password)
#solange die Verbindung noch nicht hergestellt wurde
#dann soll diese Schleife laufen und einen Punkt ausgeben
while not wlan.isconnected():
#einen Punkt ausgeben ohne Zeilenumbruch!
print(".", end="")
#kleine Pause von 250 Millisekunden
time.sleep(0.250)
print("")
#Wenn die WiFi-Verbindung erfolgreich aufgebaut wurde,
#dann soll auf der seriellen Schnittstelle der Text ausgegeben werden
print('Verbindung erfolgreich zu', ssid, 'aufgebaut!')
status = wlan.ifconfig()
print('IP-Adresse: ' + status[0])
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
def setup():
doConnect()
def main():
pass
setup()
main()
Wenn wir den Code ausführen, dann wird beim Aufbau einer WiFi-Verbindung die IP-Adresse ausgegeben, welche wir im späteren Verlauf im Browser eingeben können, um die Webseite aufzurufen.
Schritt 3 – Auslesen der Sensordaten in einem Intervall
Wir müssen eine Endlosschleife starten, in welcher wir „lauschen“, ob sich Clients mit dem Pi Pico verbinden wollen. Zeitgleich müssen wir aber auch die Sensordaten in einem Intervall auslesen.
Wir können hier aber nicht die Funktion sleep aus dem Modul time verwenden, denn dann legt der Mikrocontroller eine Pause ein und in dieser Zeit kann sich kein neuer Client verbinden.
Meine Lösung ist, die Sekunden seit dem 01.0.1.1970 auszulesen und zu prüfen ob seit dem letzen Lesevorgang eine Zeit X zbsp. 30 Sekunden vergangen ist.
#Import der benötigten Module
#zum Zugriff auf den Sensor DS18B20
#und der Pins des Pi Pico
import machine, onewire, ds18x20, time
#Import der benötigten Module
#für den Aufbau der WiFi-Verbindung
import network
import socket
ssid = 'xxx'
password = 'yyy'
#der Sensor ist am GPIO16 angeschlossen
sensorPin = machine.Pin(16)
#initialisieren eines Objektes vom Typ DS18X20
ds18b20Sensor = ds18x20.DS18X20(onewire.OneWire(sensorPin))
#anlegen eines Feldes für die IDs der Sensoren
roms = None
#anlegen eines Feldes für die WiFi-Verbindung
wlan = None
s = None
data = []
actualTime = 0
lastReadSensordata = 0
readSensordataIntervall = 30
def doConnect():
global wlan, s
wlan=network.WLAN(network.STA_IF)
wlan.active(True)
#Wenn die Verbindung nicht erstellt wurde, dann...
if not wlan.isconnected():
#Aufbau der WiFi-Verbindung mit den Daten
wlan.connect(ssid, password)
#solange die Verbindung noch nicht hergestellt wurde
#dann soll diese Schleife laufen und einen Punkt ausgeben
while not wlan.isconnected():
#einen Punkt ausgeben ohne Zeilenumbruch!
print(".", end="")
#kleine Pause von 250 Millisekunden
time.sleep(0.250)
print("")
#Wenn die WiFi-Verbindung erfolgreich aufgebaut wurde,
#dann soll auf der seriellen Schnittstelle der Text ausgegeben werden
print('Verbindung erfolgreich aufgebaut!')
status = wlan.ifconfig()
print('IP-Adresse: ' + status[0])
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
def readSensordata():
result = []
#auslesen der Sensorwerte
ds18b20Sensor.convert_temp()
#nach dem aufruf der Funktion "convert_temp()"
#soll man gem. Dokumentation 750ms warten
time.sleep_ms(750)
#erzeugen eines Indexes
#Schleife über die gefundenen IDs / Sensoren
for rom in roms:
result.append(round(ds18b20Sensor.read_temp(rom),2))
return result
def setup():
#Zugriff auf die globalen Variablen roms, wifi
global roms
#auslesen der IDs der Sensoren
roms = ds18b20Sensor.scan()
#ausgeben der Anzahl der Sensoren auf
#der seriellen Schnittstelle
print('Anzahl gefundener Sensoren: ', len(roms))
doConnect()
def main():
#Zugriff auf die Globale Variable roms
global roms, data, lastReadSensordata
#starten einer Endlosschleife, ...
while True:
#nur alle 30 Sekunden neue Sensordaten lesen
actualTime = time.time()
if (lastReadSensordata+readSensordataIntervall)< actualTime:
lastReadSensordata = actualTime
data = readSensordata()
#ausgeben der Daten auf der seriellen Schnittstelle
print(data)
setup()
main()
Wenn wir nun auf die Konsole schauen, dann sehen wir, dass im angegebenen Intervall die Sensordaten gelesen und ausgegeben werden.
Schritt 4 – Anzeigen der Webseite mit den Sensordaten
Nachdem wir nun die Sensordaten in eine Liste ausgelesen haben wollen wir diese auf eine Webseite anzeigen. Dazu schreiben wir eine kleine Webseite mit ebenso ein wenig CSS für das Styling.
<!DOCTYPE html>
<html lang="de">
<head>
<meta http-equiv="refresh" content="35">
</head>
<body>
<div style="margin:0px auto; border: 1px solid gray; box-shadow:5px 5px 5px gray;width:60%;padding:25px;text-align:center;">
<h1>Sensordaten</h1>
<center>
{dataTable}
</center>
</div>
</body>
</html>
In dem HTML Code setzen wir einen Platzhalter {dataTable} an der Stelle ein, wo wir die Tabelle einfügen wollen. Diese Datei speichern wir nun auf dem Pi Pico als Datei „website.html“.
Diese Datei lesen wir nun in eine Variable ein und ersetzen mit der Funktion format den Platzhalter mit den Daten unserer Tabelle, welche wir zuvor in eine HTML-Tabelle umgewandelt haben.
#Import der benötigten Module
#zum Zugriff auf den Sensor DS18B20
#und der Pins des Pi Pico
import machine, onewire, ds18x20, time
#Import der benötigten Module
#für den Aufbau der WiFi-Verbindung
import network
import socket
ssid = 'xxx'
password = 'yyyy'
#der Sensor ist am GPIO16 angeschlossen
sensorPin = machine.Pin(16)
#initialisieren eines Objektes vom Typ DS18X20
ds18b20Sensor = ds18x20.DS18X20(onewire.OneWire(sensorPin))
#anlegen eines Feldes für die IDs der Sensoren
roms = None
#anlegen eines Feldes für die WiFi-Verbindung
wlan = None
s = None
data = []
actualTime = 0
lastReadSensordata = 0
readSensordataIntervall = 30
def doConnect():
global wlan, s
wlan=network.WLAN(network.STA_IF)
wlan.active(True)
#Wenn die Verbindung nicht erstellt wurde, dann...
if not wlan.isconnected():
#Aufbau der WiFi-Verbindung mit den Daten
wlan.connect(ssid, password)
#solange die Verbindung noch nicht hergestellt wurde
#dann soll diese Schleife laufen und einen Punkt ausgeben
while not wlan.isconnected():
#einen Punkt ausgeben ohne Zeilenumbruch!
print(".", end="")
#kleine Pause von 250 Millisekunden
time.sleep(0.250)
print("")
#Wenn die WiFi-Verbindung erfolgreich aufgebaut wurde,
#dann soll auf der seriellen Schnittstelle der Text ausgegeben werden
print('Verbindung erfolgreich aufgebaut!')
status = wlan.ifconfig()
print('IP-Adresse: ' + status[0])
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
def readSensordata():
result = []
#auslesen der Sensorwerte
ds18b20Sensor.convert_temp()
#nach dem aufruf der Funktion "convert_temp()"
#soll man gem. Dokumentation 750ms warten
time.sleep_ms(750)
#erzeugen eines Indexes
#Schleife über die gefundenen IDs / Sensoren
for rom in roms:
result.append(round(ds18b20Sensor.read_temp(rom),2))
return result
def createHtmlTable(data):
htmlTable = """
<table border='1' style='border-collapse: collapse;'>
<thead>
<tr>
<th>Sensor</th>
<th>Wert</th>
</tr>
</thead>
<tbody>
{bodyRows}
</tbody>
</table>
"""
idx = 0
rows = ""
for value in data:
idx = idx + 1
rows += "<tr><td>{sensor}</td><td>{wert}</td></tr>".format(sensor=str(idx), wert=str(value)+"°C")
return htmlTable.format(bodyRows=rows)
def getWebsite(dataTbl):
with open('website.html', "r") as file:
website = file.read()
file.close()
return website.format(dataTable=dataTbl)
def deliverWebsite(website):
global s
cl, addr = s.accept()
cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
cl.send(website)
cl.close()
def setup():
#Zugriff auf die globalen Variablen roms, wifi
global roms
#auslesen der IDs der Sensoren
roms = ds18b20Sensor.scan()
#ausgeben der Anzahl der Sensoren auf
#der seriellen Schnittstelle
print('Anzahl gefundener Sensoren: ', len(roms))
doConnect()
def main():
#Zugriff auf die Globale Variable roms
global roms, data, lastReadSensordata
#starten einer Endlosschleife, ...
while True:
#nur alle 30 Sekunden neue Sensordaten lesen
actualTime = time.time()
if (lastReadSensordata+readSensordataIntervall)< actualTime:
lastReadSensordata = actualTime
data = readSensordata()
htmlTable = createHtmlTable(data)
website = getWebsite(htmlTable)
deliverWebsite(website)
setup()
main()
Damit haben wir nun die Hauptaufgabe erledigt, es werden die Sensordaten der angeschlossenen DS18B20 auf der Webseite in tabellarischer Form angezeigt.
Mögliche Erweiterung – minimale, maximale, sowie die Durchschnittstemperatur anzeigen
Die eigentliche Aufgabe war es, die aktuellen Sensordaten auf einer Webseite anzuzeigen. Wir können aber noch ein Feature dazu programmieren. Wenn wir schon die Temperatur messen, können wir zusätzlich noch die drei Werte berechnen und in der Tabelle speichern.
Jedoch müssen wir dazu die Sensordaten speichern. Ich nutze hier das Datenformat JSON, zum einen, weil dieses sehr einfach zum Lesen und die Implementierung in Python sehr gut gelungen ist.
{
"sensors" :[
{"idx":1, "values":[], "max":0, "min":0,"average":0},
{"idx":2, "values":[], "max":0, "min":0,"average":0},
{"idx":3, "values":[], "max":0, "min":0,"average":0},
{"idx":4, "values":[], "max":0, "min":0,"average":0},
{"idx":5, "values":[], "max":0, "min":0,"average":0},
{"idx":6, "values":[], "max":0, "min":0,"average":0}
]
}
Diese Datenstruktur legen wir in der Datei data.json auf dem Pi Pico ab, welche wir später im Code lesen und beschreiben werden.
Zusätzlich habe ich noch etwas CSS implementiert und in der Datei styles.css abgelegt. Dieses dient lediglich für das Styling der Tabelle.
Die JSON-Datei müssen wir zunächst in unser Dictionary einlesen, um die Werte daraus zu lesen und zu ändern.
#Feld für die gespeicherte Daten vom Pi Pico
storedData = {}
def readJsonToData():
global storedData
file = open('data.json')
storedData = json.load(file)
print(storedData)
file.close()
Natürlich müssen wir auch die Daten wieder in die JSON-Datei schreiben.
def storeDataToJson():
global storedData
with open("data.json", "w") as outfile:
json.dump(storedData, outfile)
Das lesen der Daten erfolgt initial in der Funktion setup und das speichern jeweils nach einem erfolgreichen laden und verarbeiten der Daten.
Den neuen Temperaturwert fügen wir der Liste hinzu, jedoch speichern wir nur die letzten 10 Werte in der Liste. (Damit wird vorgebeugt, dass der Speicher des Pi Pico irgendwann voll läuft.)
def appendDataToJson(tempValue, idx):
tempList = storedData["sensors"][idx]["values"]
if len(tempList)>=10:
tempList.insert(0, tempValue)
else:
tempList.append(tempValue)
maxTemp = storedData["sensors"][idx]["max"]
maxTemp = max(maxTemp, tempValue)
storedData["sensors"][idx]["max"] = maxTemp
minTemp = storedData["sensors"][idx]["min"]
minTemp = min(minTemp, tempValue)
storedData["sensors"][idx]["min"] = minTemp
averageTemp = storedData["sensors"][idx]["average"]
averageTemp = round(sum(tempList) / len(tempList),2)
storedData["sensors"][idx]["average"] = averageTemp
Zusätzlich müssen wir noch die Funktion createHtmlTable erweitern, damit diese noch zusätzlich die drei neuen Werte aufnimmt.
def createHtmlTable(data):
htmlTable = """
<table border='1' style='border-collapse: collapse;'>
<thead>
<tr>
<th>Sensor</th>
<th>Wert</th>
<th>min</th>
<th>max</th>
<th>durchschnitt</th>
</tr>
</thead>
<tbody>
{bodyRows}
</tbody>
</table>
"""
idx = 0
rows = ""
for value in data:
idx = idx + 1
minValue = storedData["sensors"][idx-1]["min"]
maxValue = storedData["sensors"][idx-1]["max"]
average = storedData["sensors"][idx-1]["average"]
rows += "<tr><td>{sensor}</td><td>{wert}</td><td>{minValue}</td><td>{maxValue}</td><td>{average}</td></tr>".format(
sensor=str(idx),
wert=str(value)+" °C",
minValue=str(minValue)+" °C",
maxValue=str(maxValue)+" °C",
average=str(average)+" °C")
return htmlTable.format(bodyRows=rows)
Download des Quellcodes für das Anzeigen der Sensorwerte von mehreren DS18B20 am Pi Pico auf einer Webseite
Hier nun der Download des gesamten Quellcodes inkl. der Erweiterung um die Min, Max und durchschnittstemperatur.
Letzte Aktualisierung am: 10. März 2024










Hallo Stefan
Die „Hauptaufgabe“ klappt auch hier bei mir. Dank an dich.
Allerdings musste ich mich an das richtige „Pico-Handling“ erinnern, dass die „website.html“ nicht einfach per „drag and drop“ auf den Pico kopiert werden kann, sondern sie muss aus der IDE (hier „Thonny“) „ordentlich“ auf den Pico geschrieben werden.
Probleme habe ich noch mit der json-erweiterung. Wie und wo müssen diese Module im Hauptprogramm platziert werden , einfach hineinkopieren?
Der Download des Gesmtprogramms klappt bei mir nicht, es kommt nur „RaspberryPiPicoW_multiple_DS18B20.zip“ mit einem leeren Verzeichnis an. Was mache ich falsch?
Hallo,
der Beitrag ist ja super aufgebaut, gut dokumentiert und es klappt alles wie beschrieben.
Einfach top!
Vielen Dank für diese tolle Anleitung.
Mit bestem Gruß aus der Lüneburger-Heide
Robert Waldow