Webseite daten an Python-script senden

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
wiifi
User
Beiträge: 2
Registriert: Sonntag 5. November 2017, 11:56

Hallo zusammen,

Ich habe mir mit einem Raspberry Pi und einem Arduino eine Lampen steuerung gebaut.
Nun habe ich auf dem Raspberry Pi ein python Script laufen um an einem GPIO pin spannung anzulegen oder sie weg zu nehmen.
Server Script:

Code: Alles auswählen

import threading
import socket
import sys
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)
GPIO.setup(21, GPIO.OUT)
PORT = 9898

class Server(threading.Thread):

    def __init__(self, port, host = ""):
        threading.Thread.__init__(self)
        self.host = host
        self.port = port
        try:
            self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        except socket.error as err_msg:
            print("Unable to instntiate socket. Error code: " + str(err_msg[0]) + "Error message: " + err_msg[1])
            sys.exit();
        print("[+] Server socket Initialized")

        try:
            self.server.bind((self.host, self.port))
        except socket.error as msg:
            print("Unable to bind socket. Error code: " + str(err_msg[0]) + "Error message: " + err_msg[1])
            sys.exit()

        self.server.listen(10)
        print("[+] Server started")

    def run_thread(self, conn, addr):
        print("Client connect with the ip:", addr[0] + " :" + str(addr[1]))
        while True:
            light = conn.recv(1024).decode()
            if not light:
                print("Client with the ip:", addr[0] + ":" + str(addr[1]), "is disconnected")
                break
            if light == 1:
                GPIO.output(21, GPIO.HIGH)
            if light == 2:
                GPIO.output(21, GPIO.LOW)
        conn.close()

    def run(self):
        print("Waiting for connections on port:" + str(self.port))
        while True:
            conn, addr = self.server.accept()
            threading.Thread(target=self.run_thread, args=(conn, addr)).start()

def main():
    server = Server(PORT)
    server.run()

if __name__ == "__main__":
    main()
Dieses Script steuere ich mit einem Client script das auf meinem Computer läuft an.

Client Script:

Code: Alles auswählen

import socket
import sys

class Client():
    def __init__(self):
        self.ip = "192.168.178.122"
        self.port = "9898"

        try:
            self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        except socket.error as err_msg:
            print("Unable to instntiate socket. Error code: " + str(err_msg[0]) + "Error message: " + err_msg[1])
            sys.exit();
        print("[+] Client socket Initialized")

        try:
            self.client_socket.connect((self.ip, int(self.port)))
        except:
            print("connection failed %s" % (socket.error))
            sys.exit()
        print("[+] Connected to server :", self.ip + ":" + self.port)

    def send_light(self):
        print("[1] Light On [2] Light off [q] Exit")
        light = input("->")
        while light != "q":
            self.client_socket.send(light.encode())
            print("[1] Light On [2] Light off [q] Exit")
            light = input("->")
        self.client_socket.close


def main():
    client = Client()
    client.send_light()

if __name__ == "__main__":
    main()
Nun möchte ich aber nicht immer über das Client Script denn Raspberry Pi ansteuern sondern das ganze über eine Webseite machen.
Auf der Webseite soll es anfangs nur zwei Buttons geben zum Licht an und aus machen.
Nun habe ich das Problem das ich nicht genau weiss wie ich das umsetzte da her meine frage wie ich das ganze am besten mache.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@wiifi: zuerst mal zu Deinem Client-Code: eine Klasse mit nur einer Methode rechtfertigt nicht wirklich eine Klasse. Konstanten sollten auch als Konstanten geschrieben werden. Wenn Port eine Zahl ist, warum definierst Du sie dann als String? »sys.exit« sollte niemals außerhalb von »main« vorkommen, weil solche Funktionen untestbar sind; denn um Fehler weiterzuverarbeiten sind Exceptions ja da. Nackte except nicht benutzen, da sie auch Programmierfehler verdecken können. Statt Strings mit + zusammenzustückeln benutze .format. Versuche Code-Duplikate zu vermeiden, indem Du die while-Schleife in eine while-True-Schleife verwandelst. »close« sollte man zum Schluß auch aufrufen.

Code: Alles auswählen

import socket

IP = "192.168.178.122"
PORT = 9898

def main():
    try:
        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client_socket.connect((IP, PORT))
    except socket.error as err:
        print("connection failed {}".format(error))
        return
    print("[+] Connected to server: {}:{}".format(IP, PORT))
    
    while True:
        print("[1] Light On [2] Light off [q] Exit")
        light = input("->")
        if light == "q":
            break
        client_socket.send(light.encode())
    client_socket.close()

if __name__ == "__main__":
    main()
Beim Server leitest Du die Klasse von Thread ab, obwohl Du gar keinen Thread startest. Auch hier macht die Klasse eigentlich keinen Sinn. Beim Abarbeiten der Client-Anfrage ist noch einiges falsch. Du hast ja zum Glück ein 1-Byte Protokoll, das heißt, jede Nachricht besteht nur aus einem Byte, so dass das Finden von Nachrichtengrenzen trivial ist. Beim Lesen liest Du aber bis zu 1024 Bytes, so dass mehrere Nachrichten in einem Leseaufruf enthalten sein könnten. Das berücksichtigst Du aber nicht. Auch vergleichst Du Strings mit Zahlen, so dass sich nie ein Licht schalten dürfte.

Code: Alles auswählen

import threading
import socket
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)
GPIO.setup(21, GPIO.OUT)
PORT = 9898

def handle_client(conn, addr):
    print("Client connect with the ip: {}:{}".format(*addr))
    while True:
        light = conn.recv(1).decode() # one-byte protocoll
        if not light:
            break
        GPIO.output(21, GPIO.HIGH if light=="1" else GPIO.LOW)
    print("Client with the ip: {}:{} is disconnected".format(*addr))
    conn.close()

def main():
    try:
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.bind(("", PORT))
    except socket.error as err_msg:
        print("Error: {}".format(err_msg))
        return
    print("[+] Server socket Initialized")
    server.listen(10)
    print("[+] Server started")

    print("Waiting for connections on port:" + str(self.port))
    while True:
        conn, addr = self.server.accept()
        threading.Thread(target=handle_client, args=(conn, addr)).start()

if __name__ == "__main__":
    main()
Wenn Du jetzt über einen Browser Deine Lichter steuern willst, nimmst Du am besten ein HTTP-Framework wie Bottle oder Flask.
wiifi
User
Beiträge: 2
Registriert: Sonntag 5. November 2017, 11:56

@Sirius3 Danke für die schnelle Antwort.
Da ich erst seite einer woche mich mit python beschäftige ist der überarbeitete Code sehr hilfreich.
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

wenn du dein Skript eh' überarbeitest könntest du direkt auf das neuere (und empfohlene) Modul gpiozero wechseln statt das ältere RPi.GPIO zu verwenden.

Gruß, noisefloor
Chasey
User
Beiträge: 2
Registriert: Samstag 25. November 2017, 10:09

Hallo wiifi,

ich weiß nicht wie aktuell dein Problem noch ist und wie gut du die mit PHP auskennst.

Als ich letztens etwas ähnliches realisieren wollte hab ich das ganze Programm in PHP geschrieben, also als Webseite.
Dann gibts in PHP die Möglichkeit ein Python Script aufzurufen. In dem hab ich dann einfach die GPIOs angesprochen.

In etwa so:

PHP:

Code: Alles auswählen

//Du musst natürlich zuerst die Buttons mit mit vermutlich $_POST abfragen... falls du Hilfe brauchst melde dich ;)
if $button_1 {
	exec("python3 /pfad zum Pythonscript/pin1_write.py $parameter", $result); // Siehe Erklärung unten 
}
if $button_2 {
	exec("python3 /pfad zum Pythonscript/pin2_write.py $parameter", $result);
}
Parameter und Result sind optional.
Was du als Parameter angibst kannst du im Pythonscript wieder abfragen. Wenn du im Pythonscript print verwendest wird das an Result übergeben.


Python: pin1_write.py

Code: Alles auswählen

import RPi.GPIO as GPIO
licht1 = 21
GPIO.setup(licht1, GPIO.OUT)
GPIO.output(licht1, HIGH)
Python: pin2_write.py

Code: Alles auswählen

import RPi.GPIO as GPIO
licht1 = 22
GPIO.setup(licht2, GPIO.OUT)
GPIO.output(licht2, HIGH)
Das gleiche müsstest du natürlich auch für den Ausschaltvorgang machen, also mit if $button == 0 --> GPIO = LOW ...
Mit dem oben erklärten Parameter könntest du es auch noch ein bisschen eleganter lösen.

Das ganze sind Auszüge aus meinem Programm, es kann gut sein, dass sie nicht 100% funktionieren, aber als Schema müsste es mal reichen.
Wenn du näheres wissen möchtest einfach nochmal Fragen ;)

Liebe Grüße Chasey
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

das kann man machen, wenn man es umständlich mag. Wenn man den serverseitigen Teil direkt auch in Python schreibt - was IMHO so wie so nur Vorteile gegenüber PHP hat - dann man auch direkt die alle ins Hauptskript importieren und alles in Python machen.

Gruß, noisefloor
Antworten