TigerJython | xx für Gymnasien |
Zwei Pi2Go-Roboter können miteinander über das WLAN kommunizieren, indem sie einander Strings zusenden. Dabei läuft auf einem Raspibrick ein TCP-Server- und auf dem anderen ein TCP-Client-Programm. Unter der Verwendung des Moduls tcpcon aus der TigerJython-Distribution, können mit wenig Aufwand interessante Anwendungen programmiert werden. |
In der Regel spielt es keine Rolle, welcher der beiden Robotern Server und welcher Client ist. Das Server-Programm muss aber immer zuerst gestartet werden. Der Client erstellt danach unter der Verwendung der IP-Adresse des Servers und der Portnummer, welche im Serverprogramm festgelegt ist, eine Verbindung zum Server. Die Verbindung kann über das öffentliche Internet, einen lokalen WLAN-Router oder einen mobiler WLAN-Hotspot, der auf einem Smartphone aktiviert ist, erfolgen. Mehr zur Einrichtung der Kommunikation findet man hier.
Das Modul tcpcom ist eventgesteuert aufgebaut. Der Status der Verbindung wird mit der Callbackfunktion onStateChanged(state, msg) überwacht. Diese wird bei der Erzeugung von TCPServers bzw. TCPClients registriert.
Der Parameter state des Servers kennt folgende Zustände:
TCP.Server.LISTENING TCP.Server.CONNECTED TCP.Server.MESSAGE TCP.Server.TERMINATE |
Server wartet auf eine neue Verbindung Die Verbindung zum Client wurde aufgebaut Der Server hat eine Message msg empfangen Der Server ist beendet |
Der Client kennt folgende Zustände:
TCPClient.CONNECTED TCPClient.MESSAGE TCPClient.DISSCONNECTED |
Verbindung erfolgreich erstellt Der Client hat eine Message msg empfangen Verbindung unterbrochen |
Mit der Funktion sendMessage() können Messages in Form eines Strings vom Server zum Client und umgekehrt gesendet werden. Mehr über die TCP/IP-Programmierung findet man unter www.tcpcom.ch.
Beispiel 1: Die Roboter stellen Züge dar, die sich auf einer runden Bahn bewegen. Ein Teil der Strecke ist nur einspurig befahrbar. Die Roboter teilen einander via TCP mit, wenn sie am Bahnhof angekommen sind und die Bahn frei ist. Das Serverprogramm wird zuerst gestartet. Nach der Aufbau der Verbindung fährt der Client zuerst seine Runde und hält beim beweglichen weissen Streifen an, den er mit seinem vorderen Infrarotsensor erfasst. Dabei sendet er die Message "go" an den Server. Dann fährt der Server seine Runde und sendet eine Message an Client, sobald er angehalten hat und die Bahn frei ist.
# TrainServer.py from raspibrick import * from tcpcom import TCPServer def onStateChanged(state, msg): global isWaiting, start if state == TCPServer.LISTENING: ledLeft.setColor("red") display.showText("stop") gear.stop() isWaiting = True if state == TCPServer.CONNECTED: ledLeft.setColor("green") if state == TCPServer.MESSAGE: isWaiting = False start = time.time() display.showText("run") robot = Robot() display = Display() ledLeft = Led(LED_LEFT) ledLeft.setColor("red") gear = Gear() gear.setSpeed(25) irLeft = InfraredSensor(IR_LINE_LEFT) irRight = InfraredSensor(IR_LINE_RIGHT) irCenter = InfraredSensor(IR_CENTER) r = 0.1 port = 5000 server = TCPServer(port, stateChanged = onStateChanged) start = time.time() isWaiting = True while not isEscapeHit(): if isWaiting: continue vL = irLeft.getValue() vR = irRight.getValue() vC = irCenter.getValue() if vL == 1 and vR == 0: gear.forward() elif vL == 0 and vR == 0: gear.leftArc(r) elif vL == 1 and vR == 1: gear.rightArc(r) if vC == 1 and time.time() - start > 5: server.sendMessage("go") display.showText("stop") gear.stop() isWaiting = True server.terminate() robot.exit() |
Programmcode markieren
|
# TrainClient.py from raspibrick import * from tcpcom import TCPClient import time def onStateChanged(state, msg): global isWaiting, start if state == TCPClient.CONNECTED: ledLeft.setColor("green") if state == TCPClient.DISCONNECTED: ledLeft.setColor("red") gear.stop() if state == TCPClient.MESSAGE: isWaiting = False start = time.time() display.showText("run") robot = Robot() display = Display() ledLeft = Led(LED_LEFT) ledLeft.setColor("red") gear = Gear() gear.setSpeed(25) irLeft = InfraredSensor(IR_LINE_LEFT) irRight = InfraredSensor(IR_LINE_RIGHT) irCenter = InfraredSensor(IR_CENTER) r = 0.1 host = "192.168.0.4" port = 5000 client = TCPClient(host, port, stateChanged = onStateChanged) isWaiting = False display.showText("try") if client.connect(): display.showText("run") start = time.time() while not isEscapeHit(): if isWaiting: continue vL = irLeft.getValue() vR = irRight.getValue() vC = irCenter.getValue() if vL == 1 and vR == 0: gear.forward() elif vL == 0 and vR == 0: gear.leftArc(r) elif vL == 1 and vR == 1: gear.rightArc(r) if vC == 1 and time.time() - start > 5: client.sendMessage("go") display.showText("stop") gear.stop() isWaiting = True client.disconnect() robot.exit() |
Programmcode markieren
|
Erklärungen zum Programmcode:
host = "192.168.0.4": IP-Adresse des Roboters, auf welchen das Serverprogramm läuft | |
if vC == 1 and time.time() - start > 5: Der vordere Infrarotsensor hat den weissen Streifen erfasst und die Zeit seit dem letzten Start grösser als 5 Sekunden. Damit wird verhinder, dass der Roboter nach dem Anfahren sofort wieder stopt | |
client.sendMessage("go"): Mit dieser Message wird die Bahn für den zweiten Roboter frei gegeben |
Beispiel 2: Die TCP-Kommunikation funktioniert auch im Simulationsmodus. Zum Testen kann man das Server- und das Client-Programm auf dem gleichen Computer ausführen, indem man die TigerJython-IDE zweimal startet und die IP-Adresse localhost verwendet. In diesem Beispiel werden die Züge jeweils mit einer Lichtquelle angehalten. Diese wird oberhalb der Geleise positioniert. Die Roboter erfassen die Lichtquelle mit den vorderen Lichtsensoren. Im realen Modus kann man als Lichtquelle eine Taschenlampe verwendet. Das Hintergrundbild für die Simulation (train.zip) muss in das Unterverzeichnis sprites des Programmverzeichnisses kopiert werden.
Server: |
Client |
# SimTrainServer.py from raspisim import * from tcpcom import TCPServer import time def onStateChanged(state, msg): global isWaiting, start if state == TCPServer.LISTENING: gear.stop() isWaiting = True if state == TCPServer.CONNECTED: robot.playTone(264, 100) if state == TCPServer.MESSAGE: if msg == "go": isWaiting = False start = time.time() print "run" RobotContext.useBackground("sprites/train.gif") RobotContext.setStartPosition(225, 400) RobotContext.setStartDirection(190) RobotContext.useTorch(1, 250, 400, 100) robot = Robot() gear = Gear() irL = InfraredSensor(IR_LINE_LEFT) irR = InfraredSensor(IR_LINE_RIGHT) ls1 = LightSensor(LS_FRONT_LEFT) ls2 = LightSensor(LS_FRONT_RIGHT) r = 0.1 port = 5000 server = TCPServer(port, stateChanged = onStateChanged) start = time.time() isWaiting = True while not isEscapeHit(): if isWaiting: continue vL = irL.getValue() vR = irR.getValue() v1 = ls1.getValue() v2 = ls2.getValue() if vL == 1 and vR == 1: gear.rightArc(r) if vL == 1 and vR == 0: gear.forward() elif vL == 0 and vR == 0: gear.leftArc(r) elif vL == 1 and vR == 0: gear.forward() if (v1 > 900 or v2 > 900) and time.time() - start > 5: server.sendMessage("go") gear.stop() Tools.delay(2000) isWaiting = True server.terminate() robot.exit() |
Programmcode markieren
|
# SimTrainClient.py from raspisim import * from tcpcom import TCPClient import time def onStateChanged(state, msg): global isWaiting, start if state == TCPClient.CONNECTED: robot.playTone(440, 100) if state == TCPClient.DISCONNECTED: gear.stop() if state == TCPClient.MESSAGE: if msg == "go": isWaiting = False start = time.time() print "run" RobotContext.useBackground("sprites/train.gif") RobotContext.useTorch(1, 250, 350, 100) RobotContext.setStartPosition(285, 350) RobotContext.setStartDirection(-20) robot = Robot() gear = Gear() irL = InfraredSensor(IR_LINE_LEFT) irR = InfraredSensor(IR_LINE_RIGHT) ls1 = LightSensor(LS_FRONT_LEFT) ls2 = LightSensor(LS_FRONT_RIGHT) gear.forward() r = 0.1 host = "localhost" port = 5000 client = TCPClient(host, port, stateChanged = onStateChanged) isWaiting = False if client.connect(): start = time.time() while not isEscapeHit(): if isWaiting: continue vL = irL.getValue() vR = irR.getValue() v1 = ls1.getValue() v2 = ls2.getValue() if vL == 0 and vR == 0: gear.leftArc(r) elif vL == 1 and vR == 1: gear.rightArc(r) elif vL == 1 and vR == 0: gear.forward() if (v1 > 900 or v2 > 900) and time.time() - start > 5: client.sendMessage("go") gear.stop() Tools.delay(2000) isWaiting = True client.disconnect() robot.exit() |
Programmcode markieren
|
Erklärungen zum Programmcode:
host = "localhost": Anstelle der IP-Adresse des Servers kann localhost verwendet werden | |
if (v1 > 900 or v2 > 900) and time.time() - start > 5: Falls der Roboter unter der Lichquelle ist und die Zeit seit dem letzten Start grösser als 5 Sekunden ist, hält er an | |
client.sendMessage("go"): Mit dieser Message wird die Bahn für den zweiten Roboter frei gegeben |
Aufgaben Serie10: |
1) |
Synchron fahren und stoppen.
|
|
|