38. Lektion: Webserver: Unterschied zwischen den Versionen

Aus Attraktor Wiki

Wechseln zu: Navigation, Suche
(Phew)
(Aus einer Datei mit Einfügen)
Zeile 217: Zeile 217:
 
Um in eine HTML-Seite zusätzliche Informationen einzufügen kann man die üblichen Stringformatierungsmethoden von Micropython verwenden.
 
Um in eine HTML-Seite zusätzliche Informationen einzufügen kann man die üblichen Stringformatierungsmethoden von Micropython verwenden.
 
<br>
 
<br>
==== Mit .format():====
+
'''Phew!''' bietet aber noch eine eigene Methode ('''Template Expression''') um Inhalte in Dateien einzufügen. Dazu werden in den HTML-Text doppelte geschweifte Klammern eingefügt, die den Namen der Variablen enthalten, deren Wert hier eingefügt werden soll:
<pre>
+
 
+
</pre>
+
 
+
==== Mit f-string:====
+
<pre>
+
 
+
</pre>
+
 
+
==== Phew====
+
phew! bietet aber noch eine eigene Methode (Template Expression) um Inhalte in Dateien einzufügen. Dazu werden in den HTML-Text doppelte geschweifte Klammern eingefügt, die den Namen der Variablen enthalten, deren Wert hier eingefügt werden soll:
+
 
<pre>
 
<pre>
 
<h1>Micropython Demoboard Temperatur</h1>
 
<h1>Micropython Demoboard Temperatur</h1>

Version vom 12. Februar 2024, 14:57 Uhr

Webserver

https://github.com/pimoroni/phew


Das Webserver Packet phew!

Für den Raspberry Pi Pico W gibt es ein Packet, das alles enthält, was zur Einrichtung eines Webservers auf dem Pico erforderlich ist. Es heißt "phew" und ist von Github unter https://github.com/pimoroni/phew zu beziehen. Das Archiv muss entpackt und dann das Unterverzeichnis "phew" ins Filesystem des Pico kopiert werden.
Alternativ kann phew auch mit Thonny installiert werden phew bei PyPi.

Was phew! leistet:

  • ein einfacher Webserver
  • auf Geschwindigkeit optimiert (beim Import und während der Ausführung)
  • minimale Nutzung des Speichers
  • parametrisierte Weiterleitungsregeln /greet/<name>
  • Template-Engine, die Inline-Python-Ausdrücke {{name.lower()}} erlaubt
  • GET- und POST-Anforderungsmethoden
  • Dekodierung und Parsing von Abfragezeichenfolgen
  • Catchall-Handler für nicht weitergeleitete Anfragen
  • multipart/form-data, x-www-form-urlencoded, und JSON POST Körper
  • String-, Byte- oder Generator-basierte Antworten
  • connect_to_wifi und access_point Komfortmethoden

Wo es möglich ist, versucht phew! die Menge an Code und Einstellungen, die Sie als Entwickler vornehmen müssen, zu minimieren, indem es vernünftige Standardeinstellungen wählt und einige Kleinigkeiten versteckt, die nur selten angepasst werden müssen.

phew! installieren

phew! kann mit pip von der Kommandozeile oder von Ihrer bevorzugten IDE aus installiert werden.
In Thonny können Sie dies erreichen, indem Sie auf Werkzeuge -> Pakete verwalten klicken und nach micropython-phew suchen.

Wlan einrichten

Um einen Webserver betreiben zu können, müssen wir zuerst eine Verbindung zu einem Netzwerk herstellen.
Hier noch einmal die klassische Einrichtung einer Wlan-Verbindung:

import network

# Wlan Verbindung herstellen

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
    print('connecting to network...')
    wlan.connect('ssid', 'key')
    while not wlan.isconnected():
        pass
print('network config:', wlan.ifconfig())

Einfaches Wlan verbinden

Phew bietet eine einfachere Möglichkeit die Verbindung zum Wlan herzustellen:

>>> from phew import server, connect_to_wifi
>>> connect_to_wifi("Attraktor", "blafablafa")
'192.168.5.120'

Es eine Funktion get_ip_address(). Sie muss aber erst importiert werden:

>>> from phew import get_ip_address
>>> get_ip_address()
'192.168.5.120'

Alternatv kann man die IP-Adresse auch gleich beim Connecten speichern:

>>> from phew import server, connect_to_wifi
>>> ip_adresse = connect_to_wifi("Attraktor", "blafablafa")
>>> ip_adresse
'192.168.5.120'

Den Webserver benutzen

Starten

Der Webserver wird einfach gestartet. Es ist keine großartige Konfiguration erforderlich. Allerdings müssen vorher noch die Webseiten und ihre Routen definiert werden.

from phew import server

# hier werden die auszuliefernden Webseiten definiert.   Siehe nächste Codebox.

server.run()

Einfaches Beispiel

Ein Beispiel-Webserver, der auf Anforderung eine Zufallszahl zwischen 1 und 100 (oder optional den vom Aufrufer angegebenen Bereich) zurückgibt:

from phew import server, connect_to_wifi

connect_to_wifi("<ssid>", "<password>")

@server.route("/random", methods=["GET"])
def random_number(request):
  import random
  min = int(request.query.get("min", 0))
  max = int(request.query.get("max", 100))
  return str(random.randint(min, max))

@server.catchall()
def catchall(request):
  return "Not found", 404

server.run()

Nach dem Start des Servers ist der Pico blockiert. Code der nach server.run() steht wird nicht mehr ausgeführt!
Wenn die Aufgabe des Pico nicht mit dem Webserver abgedeckt werden kann, dann kann mittels Timerinterrupt eine Funktion aufgerufen werden, die diese Dinge erledigt. Eine Möglichkeit den Server zu stoppen gibt es nicht.
Ggf. kann man auch Threads einsetzen. Beim MicroWebSrv für den ESP32 kann man das beim Starten angeben. Es wird nicht empfohlen, hat bei mir aber problemlos funktioniert.
Den phew! in einen Thread zu starten habe ich noch nicht probiert.

Webseiten routen

Um Webseiten zu erstellen wird zum Einen eine Funktion benötigt, die die Webseite als Rückgabewert zurück gibt und zum Zweiten muss diese Funktion dem Webserver als Route bekannt gemacht werden.
Die Route ist das, was in der URL hinter dem ersten / steht.

Mit Decorator:

Ein Decorator ist ein Konzept, dass eine Funktion dekoriert. D.h. Er ergänzt die Funktion, fügt also eine zusätzliche Funktionalität hinzu.
Es muss dem Decorator die Route - die Zeichen, die in der URL hinter der IP-Adresse stehen - und die HTTP-Methode (GET oder POST) ünergeben werden. Die Definition folgt in der nächsten Zeile.

@server.route("/random", methods=["GET"])
def random_number(request):
    import random
    min = int(request.query.get("min", 0))
    max = int(request.query.get("max", 100))
    return str(random.randint(min, max))

Mit Route-Tabelle:

# Webseiten definieren

def random_number(request):
    import random
    min = int(request.query.get("min", 0))
    max = int(request.query.get("max", 100))
    return str(random.randint(min, max))

def info_site(request):
      return "<h1>Infoseite vom Raspberry Pi Pico W</h1>"

# Routing Tabelle

server.add_route("/random", random_number, methods=["GET"])
server.add_route("/info", info_site, methods=["GET"])

Webseiten erstellen

Eine Webseite wird erstellt indem eine Funktion definiert wird, die den Inhalt des Response zurück gibt. Der Funktion muss request übergeben werden.

@server.route("/test", methods=["GET"])
def test_seite(request):
    version = '1.0.0'
    return f"Aktuelle Version {version}", 200

Response erzeugen

Der Response ist ein String der alle Informationen enthält die an den Client zurückgegeben werden sollen.
Innerhalb der Definition kann alles das gemacht werden, was auch in einer Funktion möglich ist. Der Return-Wert ist der Response. Dieser kann auf unterschiedliche Weise erzeugt werden.

Einfacher Text

Hier steht hinter return ein String, der den Response enthält. Dieser kann, aber muss nicht, HTML-Auszeichnungen enthalten. Es kann ein kurzer Text sein, oder auch eine umfangreiche Webseite mit HTML-Header und -body. Als zweites Argument kann der HTTP-Code angegeben werden.

@server.catchall()
def catch_all(request):
    return "<h1>Hallo, diese Webseite gibt es nicht!</h1>", 404

# oder mit f-string:

@server.route("/test", methods=["GET"])
def test_seite(request):
    version = '1.0.0'
    return f"Aktuelle Version {version}", 200

# oder mit einer Funktion:

@server.route("/random", methods=["GET"])
def random_number(request):
    import random
    min = int(request.query.get("min", 0))
    max = int(request.query.get("max", 100))
    return str(random.randint(min, max))

In einer Variablen

@server.route("/info", methods=["GET"])
def info_seite(request):
    content = '''
                 <html>
                     <header>
                          ....
                     </header>
                     <body>
                          ....
                     </body>
                 </html>
              '''
    return content, 200

Aus einer Datei

Um den Response aus einer Datei zurück zu geben gibt es die Funktion render_template().
So kann eine komplette Webseite aus einer Datei zurückgegeben werden, es können aber auch Werte in das Webseiten-Template eingesetzt werden.

from phew.template import render_template
HTDOCS = 'htdocs/'

@server.route("/test", methods=["GET"])
def test_site(request):
    return render_template(HTDOCS + "html-test_001.html")

Aus einer Datei mit Einfügen

Um in eine HTML-Seite zusätzliche Informationen einzufügen kann man die üblichen Stringformatierungsmethoden von Micropython verwenden.
Phew! bietet aber noch eine eigene Methode (Template Expression) um Inhalte in Dateien einzufügen. Dazu werden in den HTML-Text doppelte geschweifte Klammern eingefügt, die den Namen der Variablen enthalten, deren Wert hier eingefügt werden soll:

<h1>Micropython Demoboard Temperatur</h1>
Dieses ist eine kleine HTML-Testseite.
<br>
Temperatur vom BME280: {{temp}} °C
<br>
Noch ist es nur ein einfacher Test!

Beim schreiben der Template-Seite muss darauf geachtet werden, dass für Sonderzeichen und Umlaute HTML-Code geschrieben wird. UTF-8 führt hier zu ungewünschten Ausgaben.
Für das °-Zeichen muss im HTML-Code &deg; stehen! Das Wiki macht daraus automatisch °! Deshalb konnte ich es im Codeblock nicht richtig darstellen.
In der Serverfunktion sieht die Einsetzung dann so aus:

return render_template(HTDOCS + "temp2.html", temp=str(temperatur))

Wenn die Seite nicht existiert

Der Server gibt üblicherweise die der Route zugeordnete Webseite zurück. Wenn eine Webseite angefordert wird, die nicht existiert, wird die Servermethode catchall() aufgerufen.

@server.catchall()
def catch_all(request):
      return "<h1>Hallo, diese Webseite gibt es nicht!</h1>", 404

# oder aus einer Datei in /htdocs:

HTDOCS = 'htdocs/'

@server.catchall()
def catch_all(request):
    return render_template(HTDOCS + 'error_404.html')

Daten an den Server übergeben

Wenn Daten an den Server übergeben werden sollen geht aus das:

@server.route("/hallo/<name>", methods=["GET"])
def hallo(request, name):
    return f"Hallo {name}!"

Sie können dann in die URL eingegeben werden. Für dieses Beispiel:

Server-IP/hallo/peter

Weiter Möglichkeiten mit HTML-Formularen werden in der nächsten Lektion besprochen.

Lager

Beispiel

Die verschiedenen Return-Methoden ausprobieren

Hier gibt es ein Programm, das den phew-Server einrichtet und einige Seiten mit verschiedenen Return-Methoden bereitstellt. Welche Seiten abgerufen werden können ist unter IP/info abrufbar.

# webserver_002.py
#
# V.0.0.1
# 07.12.2023
# Peter Stöck
#
# Hier werden die verschiedenen Returnmethoden getestet.



from phew import server, connect_to_wifi
from phew.template import render_template


# Konstanten und Variablen
HTDOCS = 'htdocs/'
ip_adresse = None

SSID = 'Attraktor'
PW = 'blafablafa'

# Wlan Verbindung herstellen

ip_adresse = connect_to_wifi(SSID, PW)
print(ip_adresse)

# Websiten erstellen

@server.route("/string1", methods=["GET"])
def string1(request):
    return str(42) + ' ist string1'

@server.route("/string2", methods=["GET"])
def string2(request):
    return '''<h1>Infoseite vom Raspberry Pi Pico W</h1><br>
            Dieses ist string2'''

@server.route("/string3", methods=["GET"])
def string3(request):
    return render_template(HTDOCS + "html-test_002.html")

@server.route("/var", methods=["GET"])
def var(request):
    content = 'Hallo, ich komme aus einer Variablen'
    return content

@server.route("/hallo/<name>", methods=["GET"])
def hallo(request, name):
    return f"Hallo {name}!"

@server.route("/info", methods=["GET"])
def string3(request):
    content = '''
                 <h1>Infoseite vom phew-Server</h1>
                 Folgende Seiten sind abrufbar:<br>
                 /string1<br>
                 /string2<br>
                 /string3<br>
                 /var<br>
                 /hallo/Hier Name eingeben<br>
                 /info - diese Seite<br>
              '''
    return content

@server.catchall()
def catch_all(request):
    return "<h1>Hallo, diese Webseite gibt es nicht!</h1><br>Sie steht für catch_all.", 404

# Server starten - nun geht nichts anderes mehr:(

server.run()

Hier die Datei html-test_002.html. Sie muss in das Verzeichnis htdocs kopiert werden.

<h1>&Uuml;berschrift</h1>
Dieses ist eine kleine HTML-Testseite.
<br>
Sie steht f&uuml;r string3
<br>
Noch ist es nur ein einfacher Test!

Ein praktisches Beispiel

Dieses Programm ermöglicht es die LED ein-/auszuschalten und die Sensoren abzufragen.

# webserver_003.py
#
# Version 0.0.1
# 07.12.2023
# Peter Stöck
#
# Dieses Programm hat folgende Funktionen:
# LED ein- und ausschalten
#

from phew import server, connect_to_wifi
from phew.template import render_template
from machine import Pin


# Konstanten und Variablen
HTDOCS = 'htdocs/'
ip_adresse = None
pin_von_led = 22

SSID = 'Attraktor'
PW = 'blafablafa'

# Led aktivieren

led_pin = Pin(pin_von_led, Pin.OUT)
led_pin.value(0)

# Wlan Verbindung herstellen

ip_adresse = connect_to_wifi(SSID, PW)
print(ip_adresse)


# Webseiten erstellen

@server.route("/led/<led_status>", methods=["GET"])
def led_ctrl(request, led_status):
    if led_status == 'ein':
        led_pin.value(1)
    elif led_status == 'aus':
        led_pin.value(0)
    else:
        return "<h1>Hallo, diese Funktion gibt es nicht!</h1>", 404
    return f"LED wurde {led_status}geschaltet."

@server.catchall()
def catch_all(request):
    return "<h1>Hallo, diese Webseite gibt es nicht!</h1>", 404

# Server starten - nun geht nichts anderes mehr:(

server.run()

HTML-Seiten mit Daten ergänzen

Meistens möchte man nicht nur eine statische HTML-Seite ausgeben, sondern z.B. Sensordaten übertragen. Beim Micropython Demoboard könnte es die Temperatur vom BME280 Sensor sein.
Es wäre dann praktisch, wenn der HTML-Code aus einer Datei käme und der Temperaturwert eingefügt wird. Phew bietet dafür Template Expressions:

from phew.template import render_template
HTDOCS = 'htdocs/'

temperatur = 23

text = render_template(HTDOCS + "temp2.html", temp=str(temperatur))

for i in text:
    print(i)

Das Ergebnis:

b'<h1>Micropython Demoboard Temperatur</h1>\nDieses ist eine kleine HTML-Testseite.\n<br>\nTemperatur vom BME280: '
23
b' \xc2\xb0C\n<br>\nNoch ist es nur ein einfacher Test!'

Phew erzeugt den HTML-Code nicht für die ganze Seite im Stück, sondern erzeugt kleine Blöcke, so dass der Speicherbedarf reduziert wird.

Beim schreiben der Template-Seite muss darauf geachtet werden, dass für Sonderzeichen und Umlaute HTML-Code geschrieben wird. UTF-8 führt heir zu ungewünschten Ausgaben.

<h1>Micropython Demoboard Temperatur</h1>
Dieses ist eine kleine HTML-Testseite.
<br>
Temperatur vom BME280: {{temp}} °C
<br>
Noch ist es nur ein einfacher Test!

Für das °-Zeichen muss im HTML-Code &deg; stehen! Das Wiki macht daraus automatisch °! Deshalb konnte ich es im Codeblock nicht richtig darstellen.

Lager

Navigation

Zurück zur "Micropython Kurs 2023 Teil 2" Startseite
Zurück zur "Micropython Kurs 2023" Startseite
Zurück zur Programmieren Startseite
Zurück zur Wiki Startseite