webserver

14. WEBSERVER, IOT

 

 

DU LERNST HIER...

 

wie du einen Webserver und sogar einen Accesspoint auf der Oxocard einrichten kannst, um über das WLAN mit einem Smartphone oder PC mit der Oxocard zu kommunizieren. In den Musterbeispielen lernst du auch, wie man ferngesteuerte Geräte programmiert.

Du erhältst damit Einblick in die modernen  Gebiete der Rechnerkommunikation und  des "Internet der Dinge" (IOT, Internet of Things).

Um die Einzelheiten zu verstehen,  benötigst du einige Kenntnisse von HTTP, HTML (und wenig JavaScript), die du dir am besten mit einem Web-Tutorial aneignest.

 

 

ZWEI VERBINDUNGSMÖGLICHKEITEN

 

Damit zwei Rechner miteinander Informationen austauschen können, müssen sie über einen Datenkanal miteinander verbunden sein. Bei der Oxocard wird dabei das WLAN verwendet und als Verbindungsprotokoll TCP/IP eingesetzt. Die Verbindung erfolgt über ein spezielles Gerät, das man Accesspoint, Hotspot oder WLAN-Router nennt. (Kennst du diese Begriffe nicht, so orientiere dich im Internet. Du verwendest dieselben Verfahren wie mit deinem Smartphone.)

Für die TCP/IP-Kommunikation mit der Oxocard gibt es zwei Szenarien:

Verwendung eines vorhandenen Accesspoints
Die Oxocard loggt sich auf einem bestehenden Accesspoint ein. Ein anderes Gerät, beispielsweise dein PC oder Smartphone, das mit ihr kommunizieren will, loggt sich ebenfalls auf demselben Accesspoint ein (oder ist über das  Internet mit diesem Accesspoint verbunden).


Die Oxocard als Accesspoint
Die Oxocard ist selbst ein Accesspoint mit einer eigenen SSID und die anderen Geräte loggen sich über WLAN auf diesem Accesspoint ein.

 

 

MUSTERBEISPIELE

 

Das Modul tcpcom einhält mehrere Klassen, um die Programmierung von Webapplikationen stark zu vereinfachen. Mit der Klasse Wlan kann man sich auf einem bestehenden Accesspoint einloggen oder einen eigenen Accesspoint starten. Die Klasse HTTPServer enthält einen einfachen Webserver. Erzeugt man mit HTTPServer(requestHandler = onRequest) ein Serverobjekt, so wird der Server auch gleich auf Port 80 gestartet.

Dieser wartet auf auf diesem Port  auf einen Client. Trifft ein HTTP-Request eines Clients ein, so wird die Callbackfunktion onRequest() aufgerufen, wo du festlegst, wie der Server reagieren soll und welchen HTTP-Response er dem Client zurück sendet. Als Client kannst du den Browser eines Smartphones oder eines beliebigen Computers verwenden.


A. Vorhandenen Accesspoint verwenden

Im ersten Beispiel realisierst du eine Fernsteuerung (Remote control). Dabei kannst du mit einem Webbrowser einen grünen Farbkreis ein- oder ausgeschalten. Stattdessen könnte es sich aber auch um irgend ein anderes Gerät handeln, dass du ein- oder ausschalten willst (Motor, Lampe, Raumheizung, usw.).

Zuerst loggt sich die Oxocard mit Wlan.connect() auf einem Accesspoint mit einer SSID und einem (leeren) Passwort ein. (Diese Zeile musst du entsprechend den Angaben deines Accesspoints anpassen).




Nachfolgend wird der Webserver automatisch beim Erzeugen des HTTPservers gestartet. Er funktioniert eventgesteuert  mit der Callbackfunktion onRequest(), die bei einem eingehenden GET-Request aufgerufen wird. Sie hat drei Parameter clientIP, filename, params. clientIP liefert die IP-Adresse des Clients, filename ist der Name der im GET-Request angeforderten Datei. params liefert die Parameter des GET-Requests als Tupel in der Form (key, value).

In der Callbackfunktion wird hier lediglich eine Variable state auf den Wert "ON" oder "OFF" gesetzt, auf die in der Endlosschleife des Hauptprogramm getestet wird. Verändert sich ihr Wert, so wird der Farbkreis ein- oder ausgeschaltet  (state muss daher als global bezeichnet werden).

Für den HTTP-Response verwendest du den vordefinierten String html, wobei der aktuelle Wert des Zustands mit der Formatzeile

Current light state: %s<br>

eingebaut wird. Der return-Wert von onRequest() wird automatisch als HTTP-Response an den Client übertragen.

Programm:

from tcpcom import * # import before oxocard
from oxocardext import *

html = """<!DOCTYPE html>
<html>
  <head> <title>Oxocard Light</title> 
  <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body> 
     <h2>Welcome to the Oxocard</h2>
     <b>Press to change light state:</b>
     <form method="get">
       <input type="submit" style="font-size: 50px; 
           height: 90px; width: 150px" name="light" value="ON" />
       <input type="submit" style="font-size: 50px; 
           height: 90px; width: 150px" name="light" value="OFF" />
     </form>
     <br>
    Current light state: %s<br>
  </body>
</html>
"""

def onRequest(clientIP, filename, params):
    global state
    if len(params) > 0:
        state = params[0][1]
    return html%(state)

state = "OFF"
oldState = ""
dot(0, 0, BLUE)
sleep(0.2)
clear()
Wlan.connect("ar", "aabbaabbaabb")
print("Connected. IP:", Wlan.getMyIPAddress())
bigTextScroll(Wlan.getMyIPAddress())
HTTPServer(requestHandler = onRequest)
clear()
while True:
    if oldState != state:
        if state == "ON":
            fillCircle(3, 3, 3, GREEN)
        if state == "OFF":
            clear()
        oldState = state
    sleep(0.1)          
► In Zwischenablage kopieren

Nach dem Programmstart wird im Console-Fenster die IP-Adresse des Webservers angezeigt, die du im Browser auf dem Smartphone oder auf PC eingeben musst. (Etwas schöner wäre es, diese Adresse auf dem LED-Display der Oxocard als Scrolltext auszuschreiben).

Das Tag <meta name="viewport"> bewirkt, dass die Textgrösse der Fenstergrösse des Webbrowsers angepasst wird. Damit wird die Webseite auf Smartphones vergrössert dargestellt.

 

B. Oxocard als Accesspoint

Auf der Oxocard kann auch ein Accesspoint aktiviert werden. Du kannst dann mit einem PC oder Smartphone im selben Raum direkt auf die Oxocard zugreifen. (Allerdings beeinflussen sich mehrere Accesspoints im gleichen Raum gegenseitig, was andere WLAN-Nutzungen stören kann)

Die Funktion Wlan.activateAP(ssid = "oxocard", password = "") aktiviert den Accesspoint. Du kannst selbstverständlich die SSID und das Passwort ändern.

Für Clients, die sich im Zugriffsbereich des Accesspoints befinden, ist die SSID oxocard in den WLAN-Einstellungen sichtbar und die Clients können sich (mit leerem Passwort) einloggen.

 


Um auf den Webserver der Oxocard zuzugreifen, musst du einen Browser starten und als URL die fixe IP-Adresse 192.168.4.1 eingeben.

Beobachte in der Statuszeile des Browsers, wie die Werte ON/OFF als Request-Para

meter gesendet werden.




 

Programm:

from tcpcom import *  
from oxocardext import *

html = """<!DOCTYPE html>
<html>
  <head> <title>Oxocard Light</title> 
  <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body> 
     <h2>Welcome to the Oxocard</h2>
     <b>Press to change light state:</b>
     <form method="get">
       <input type="submit" style="font-size: 50px; 
           height: 100px; width: 150px" name="light" value="ON" />
       <input type="submit" style="font-size: 50px; 
           height: 100px; width: 150px" name="light" value="OFF" />
     </form>
     <br>
    Current light state: %s<br>
  </body>
</html>
"""

def onRequest(clientIP, filename, params):
    global state
    if len(params) > 0:
        state = params[0][1]
    return html%(state)

state = "OFF"
oldState = ""
Wlan.activateAP(ssid = "oxocard", password = "")
HTTPServer(requestHandler = onRequest)
while True:
    if oldState != state:
        if state == "ON":
            fillCircle(3, 3, 3, GREEN)
        if state == "OFF":
            clear()
        oldState = state
    sleep(0.1)           
► In Zwischenablage kopieren

 


 

WEITERE ANWENDUNGEN

 

Bei den folgenden Programmen kannst du selbst entscheiden, ob du einen vorhandenen Accesspoint oder den Accesspoint der Oxocard verwenden möchtest, musst aber den Programmcode entsprechend anpassen.

Im folgenden Beispiel willst du ferngesteuert die Farbe der Schlange ändern. Im Hauptprogramm bewegst du die Schlange endlos auf einem Quadrat. Beim Klick auf die Schaltflächen "BLUE" bzw. "GREEN" sendet der Client einen GET-Request, wodurch die Callbackfunktion onRequest() aufgerufen wird. Hier legst du  mit setTailColor() die Farbe des Schwanzes fest, sendest aber auch noch die so gesetzte Farbe im HTTP-Response an den Client zurück, der sie anzeigt.





Programm:

from tcpcom import * 
from oxosnake import *

html = """<!DOCTYPE html>
<html>
  <head> <title>Oxocard Snake</title> 
  <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body> 
     <h1>Welcome to the Snake</h1>
     <b>Press to change the tail color</b>
     <form method="get">
       <input type="submit" style="font-size: 35px; 
           height: 90px; width: 150px" name="color" value="BLUE" />
       <input type="submit" style="font-size: 35px; 
           height: 90px; width: 150px" name="color" value="GREEN" />
     </form>
     <br>
    Current color: %s<br>
  </body>
</html>
"""

def onRequest(clientIP, filename, params):
    state = "GREEN"
    if len(params) > 0:
        state = params[0][1]
        if state == "BLUE":
            setTailColor(CYAN)
        if state == "GREEN":
            setTailColor(GREEN)
    return html%(state)

makeSnake()
if not Wlan.connect("ssid", "pasword"):
    print("Connection failed")
else:
    print("Successfully connected to AP. IP:", Wlan.getMyIPAddress())
    HTTPServer(requestHandler = onRequest)
    while True:
        forward(4)
        right(90)
► In Zwischenablage kopieren


Im nächsten Beispiel wird die Snake mit 5 Schaltflächen von einem Smartphone oder PC aus ferngesteuert.






Der HTML-Code ist etwas umfangreicher, da für die Anordnung der Schaltflächen eine Tabelle verwendet wird. Beim Klick auf einen Button bewirkt der an die Oxocard gesendete HTTP-Request wiederum den Aufruf der Callbackfunktion onRequest(). Der Wert von params[0][1] liefert den Namen des gedrückten Buttons (d.h. "north", "west", "south". "east" oder "stop") und dieser wird in der Variablen state gespeichert. Im Hautprogramm wird in einer endlosen while-Schleife (ausser im Fall von state = 'stop') forward() aufgerufen und und die Bewegungsrichtung entsprechend geändert.  (Das sleep(0.1) verhindert, dass im Fall von state = 'stop' in der Schleife unnötig viel Rechenzeit vergeudet wird.)

Programm:

from tcpcom import * 
from oxosnake import *

html = """<!DOCTYPE html>
<html>
  <head> <title>Oxocard Snake</title> 
  <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body> 
    <h2>Welcome to the Snake</h2>
    <b>Press to change the tail color</b>
    <form method="get">
    <table>
    <tr>
      <td>&nbsp;</td>
      <td><input type="submit" style="font-size:24px; height:50px; width:75px" 
          name="btn" value="north"/></td>
      <td>&nbsp;</td>
    </tr>
    <tr>
      <td><input type="submit" style="font-size:24px; height:50px; width:75px" 
          name="btn" value="west"/></td>
      <td><input type="submit" style="font-size:24px; height:50px; width:75px" 
          name="btn" value="stop"/></td>
      <td><input type="submit" style="font-size:24px; height:50px; width:75px" 
          name="btn" value="east"/></td>
    </tr>
    <tr>
      <td>&nbsp;</td>
      <td><input type="submit" style="font-size:24px; height:50px; width:75px" 
          name="btn" value="south"/></td>
      <td>&nbsp;</td>
    </tr>
    </table>
    </form><br>
    Current state: %s<br>
  </body>
</html>
"""

def onRequest(clientIP, filename, params):
    global state 
    if len(params) > 0:
        state = params[0][1]       
    return html%(state)

makeSnake()
state = "stop"
Wlan.activateAP(ssid = "oxocard", password = "")
HTTPServer(requestHandler = onRequest)
while True:
    if state == 'north':
        setHeading(0)     
    elif state == 'west':
        setHeading(-90) 
    elif state == 'south':
        setHeading(180)
    elif state == 'east':
        setHeading(90)          
    if state != 'stop':
        forward()                
    sleep(0.1)	
► In Zwischenablage kopieren

 

Temperatur messen und auf Smartphone oder PC anzeigen

Oft müssen Messungen an weit entfernten Orten durchgeführt und die Werte in Echtzeit an eine Auswertestelle übertragen werden. Zur Demonstration dieses Szenarios wird mit dem Temperatursensor, der im Beschleunigungssensor der Oxocard eingebaut ist, die aktuelle Temperatur gemessen und über WLAN an ein Notebook oder Smartphone übertragen und dort im Browser angezeigt. Die Temperatur erhältst du durch Aufruf der Methode getTemperature() des Accel-Objekts. Sie entspricht aber der Temperatur im Innern des Accelerometer-Chips und steigt an, je länger die Oxocard in Betrieb ist. Um realistischere Werte für die Umgebungstemperatur zu erhalten, musst du die Oxocard aus dem Gehäuse nehmen.





Programm:

from tcpcom import *
from oxocard import *
from oxoaccelerometer import *

html = """<!DOCTYPE html>
<html>
  <head> <title>Oxocard Temperature </title> 
  <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body> 
     <h2>Welcome to the Oxocard</h2>
      <b>Press to refresh:</b>
     <form method="get">
       <input type="submit" style="font-size: 25px; 
       height: 50px; width:250px" name="temp" value="getTemperature"/>      
     </form>
     <br>
     <h3>Current temperature: %d degC</h3>
  </body>
</html>
"""

def onRequest(clientIP, filename, params):
    return html%(temp)

Wlan.activateAP(ssid = "oxocard", password = "")
HTTPServer(requestHandler = onRequest)
acc = Accelerometer.create()

while True:
    temp = acc.getTemperature()
    display(temp)
    sleep(1)   
► In Zwischenablage kopieren


Oxocardfarben mit Schieberegler einstellen

Der HTML-Code lässt sich beliebig erweitern und du kannst natürlich auch JavaScript und CSS verwenden. In diesem Beispiel willst du mit 3 Schiebereglern die  Farbe eines Quadrats einstellen. Der JavaScript-Code sorgt für ein interaktives Verhalten der Schieberegler, so wie du es von einer PC-Applikation gewohnt bist: Während des "Ziehens" wird der aktuelle Wert ständig angezeigt und beim "Loslassen" wird der HTTP-Request (als Submit) automatisch an den Webserver auf der Oxocard gesendet.



Programm:

from tcpcom import *
from oxocardext import *

html = """<!DOCTYPE html>
<html>
  <head>
    <title>Oxocard Color Controller</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script>
      function showText(color, value) 
      {
        switch(color)
        {
          case 0:
            document.forms[0].redtext.value = value; break;
          case 1:
            document.forms[0].greentext.value = value; break;
          case 2:
            document.forms[0].bluetext.value = value; break;
        }        
      }

      function submitValues(color, value)
      {
        document.forms[0].submit();
      }
    </script>
  </head>
  <body>
     <h3>Welcome To The Color Controller</h3>
     <b>Drag to change colors </b>
     <p>
     <form method="get">
        Red:<br>
        <input type="range" min="0" max="100" value="%s"
        oninput="showText(0, value)" onchange="submitValues(0, value)">
        <input size="5" name="redtext" value="%s"><br>              
         
        Green:<br>
        <input type="range" min="0" max="100" value="%s"
        oninput="showText(1, value)" onchange="submitValues(1, value)">
        <input size="5" name="greentext" value="%s"><br>              
         
        Blue:<br> 
        <input type="range" min="0" max="100" value="%s"
        oninput="showText(2, value)" onchange="submitValues(2, value)">
        <input size="5" name="bluetext" value="%s">              
     </form></p>
  </body>
</html>
"""

def onRequest(clientIP, filename, params):
    global r, g, b
    if len(params) > 0:
        r = int(params[0][1])
        g = int(params[1][1])
        b = int(params[2][1])
        print(r, g, b)
    return html%(r, r, g, g, b, b)

r = g = b = 100
oldR = oldG = oldB = 0

if not Wlan.connect("SSID", pw):
    print("Connection to AP failed")
else:
    print("Successfully connected to AP. IP:", Wlan.getMyIPAddress())
    HTTPServer(requestHandler = onRequest)
    while True:
        if r != oldR or g != oldG or b != oldB:
            fillRectangle(2, 2, 4, 4, (r, g, b))
            oldR = r
            oldG = g
            oldB = b
        sleep(1)
► In Zwischenablage kopieren

 

MERKE DIR...

 

Die Oxocard kann sowohl als Accesspoint und als Webserver funktionieren. Der Webserver wartet auf einen HTTP-Request, der von einem Webbrowser eines Clients (PC, Smartphone, usw.) gesendet wird.  Der Server verarbeitet den Request und und sendet einen HTTP-Response an den Client zurück.

Mit dem Modul tcpcom wird das Eintreffen eines HTTP-Requests beim Server als Ereignis (Event) aufgefasst und automatisch eine Callbackfunktion aufgerufen.

 

 

ZUM SELBST LÖSEN

 

 

1.
Schreibe ein Programm, mit welchem du ferngesteuert mit einem Smartphone alle LEDs auf der Oxocard miteinander auf Gelb oder Grün stellen kannst.


2.

Die Schlange bewegt sich auf einem Quadrat. Mit Click auf den Button "grow" wird der Schwanz um 1 Teil grösser, mit Klick auf den Button" shorten" wird sie um 1 Teil kleiner. Verwende die Befehle growTail() und shortenTail().

 

 

3.

Mit einem Schieberegler willst du die Helligkeit eines gefüllten Kreises fernsteuern.  Verwende den unten stehenden Vorschlag für den HTML-Code, der einen Submit-Button verwendet. Der aktuelle Helligkeitswert wird im HTTP-Response an den Client zurück übertragen (durch zweimalige Verwendung des Formatparameters %d). Im Callback holst du den Helligkeitswert als Integer mit
intensity = int(params[0][1])

 
html = """<!DOCTYPE html>
<html>
  <head> <title>Oxocard Light Controller</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
     <h2>Welcome To The Light Controller</h2>
     <b>Drag to change intensity and press SUBMIT</b>
     <p>
     <form method="get">
       <input type="range" min="0" max="100" name="bar" value="%d">
       <input type="submit" name="submit" value="SUBMIT">
     </form></p>
     <br>
     Current intensity: %d
  </body>
</html>
"""
 

 

 

 

ZUSATZSTOFF: WEBCLIENT AUF DER OXOCARD

 

Statt mit einem Handy auf einen Webserver zuzugreifen, der auf der Oxocard A läuft, kannst du auch eine zweite Oxocard B zur Steuerung von A verwenden. Dazu startest du auf B einen Webclient und sendest dem Server auf A die nötigen HTTP-GET-Requests. Im folgenden Beispiel läuft auf A das unveränderte Ein-/Ausschaltprogramm für den grünen Kreis und zwar in der Version, wo A ein Accesspoint ist. Das Programm auf B loggt sich zuerst auf diesem Accesspoint ein und sendet dann als HTTP-Client einen GET-Request mit dem Parameter light=OFF oder light=ON, je nachdem ob der Button L3 oder R3 geklickt wurde.



Oxocard A
(Webserver
)


Oxocard B
(HTTP-Client)

Programm (Oxocard A):

from tcpcom import *  
from oxocardext import *

def onRequest(clientIP, filename, params):
    global state
    if len(params) > 0:
        state = params[0][1]
    return state

state = "ON"
oldState = ""
Wlan.activateAP(ssid = "oxocard", password = "")
HTTPServer(requestHandler = onRequest)
while True:
    if oldState != state:
        if state == "ON":
            fillCircle(3, 3, 3, GREEN)
        if state == "OFF":
            clear()
        oldState = state
    sleep(0.1)                
► In Zwischenablage kopieren

 

Programm (Oxocard B):

from tcpcom import *
from oxobutton import *

btnOff = Button(BUTTON_L3) 
btnOn = Button(BUTTON_R3) 

host = "192.168.4.1"
port = 80
Wlan.connect("oxocard", "")
client = HTTPClient()
while True:
    if btnOff.wasPressed():
        if (client.connect(host, port)):
            client.sendGetRequest("?light=OFF")
            client.closeConnection()
    if btnOn.wasPressed():
        if (client.connect(host, port)):
            client.sendGetRequest("?light=ON")
            client.closeConnection()
► In Zwischenablage kopieren


Eine Information kann auch so an den Webserver gesendet werden, dass man den Parameter filename im Callback onRequest() verwendet. Beispielweise könnten mit den folgenden Requests zwei Relais ein-/ausgeschaltet werden:

/relay/1/on
/relay/1/off
/relay/2/off
/relay/2/off

Da die Oxocard als Accesspoint die Webadresse 192.168.4.1 hat, sind dann in einem Webbrowser die URLs zum Schalten der Relais:

192.168.4.1/relay/1/on
192.168.4.1/relay/1/off
192.168.4.1/relay/2/on
192.168.4.1/relay/2/off

 

14-1
Fachliche Hinweise:

Du findest ein Tutorial über die Formatierung unter

https://www.python-course.eu/python3_formatted_output.php