Python mit Arduino

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Hast Du denn die Firmata-Firmware auf dem Arduino? Und eine LED-Matrix an dem Arduino angeschlossen? Und weisst Du wie die dann angesteuert werden muss über die Pins?

Falls die Frage zu den Teilen die mit dem RaspberryPi zu tun haben, sich auf den Quelltext im ersten Beitrag beziehen: Vergiss den am besten gleich wieder. Der ist an sich schon recht schlecht, nicht zuletzt wegen den vielen ``global``-Anweisungen. Wenn Du dann dann noch alles raus löschst was mit dem Raspi zu tun hat, bleibt nichts übrig was Du ohne es komplett umzuschreiben für zwei verschiedene Frontends wiederverwenden kannst. Unter anderem wegen der ganzen ``global``\s.

Wenn Du verzweifelt bist, könntest Du Dich an einen Tutor oder den Dozenten wenden, oder wer auch immer das Projekt betreut, und dem Deine Probleme schildern und um Rat fragen.

Wie gesagt noch mal mein Rat für die Strukturierung: Erst die Spiellogik ohne Benutzerinteraktion, dann die GUI, und dann erst den Teil mit dem Arduino. Auf die Spiellogik bauen sowohl GUI als auch Arduino auf. Die GUI ist einfacher umzusetzen als das mit dem Arduino. Gleichzeitig bekommt man bei der GUI auch recht schnell Probleme mit der sauberen Trennung zwischen Logik und Benutzerinteraktion mit und gelöst, die man für de Arduino dann ja auch braucht.

Beim Firmata-Protokoll würde ich als erstes evaluieren ob das für die verwendete Hardware geeignet ist, oder ob man da auf dem Arduino nicht am Ende doch selbst Code schreiben muss um die LED-Matrix anzusteuern, weil die vielleicht einen dauerhaft laufenden „refresh“ braucht, wenn das nicht in Hardware, also durch einen entsprechenden Steuerchip gelöst ist.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Hase
User
Beiträge: 100
Registriert: Donnerstag 1. Oktober 2009, 15:17
Wohnort: Bremer Speckgürtel

Die Programmieraufgabe hat, so wie du es geschildert hast, nichts mit dem RaspberryPi zu tun. Der Code sollte daher von allen RaspberryPi bezogenem Code befreit werden.
1) Letzlich bleibt nur sowas übrig wie: hoch, runter, links rechts auf Tastenddruck. Zum Test brauchst du auch keine GUI, es genügt die Ausgabe auf der Kommandozeile.
2) Such dir ein Beispiel, wie der Aurduino das LCD-Display ansteuert. Da gibt es ausreichend in Netz.
3) Jetzt musst du nur noch eine Schnittstelle zwischen Computer und Arduino schaffen, am besten eine Serielle. Python gibt die Kommandos "rechts links oben unten" auf die serielle Schnittstelle aus und der Arduino nimmt sie auf der seriellen Schnittstelle entgegen und leitet sie an das LCD weiter. Das sollte es sein.

So würde ich rangehen. (Wenn ich die Aufgabenstellung richtig verstanden habe. )
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hase: Es soll sowohl eine Tk-GUI als auch eine Darstellung über den Arduino umgesetzt werden. Also würde man die Richtungen eher nicht *zum* Arduino übertragen, denn das würde ja bedeuten, dass man die Spiellogik für die Tk-GUI in Python hat und dann noch mal in C für den Arduino programmieren müsste. Sinnvoller wäre es auch im Arduino-Fall die Spiellogik in Python zu schreiben, die man dann sowohl für die GUI als auch den Arduino verwenden kann. Die Richtungen könnten aber *vom* Arduino in Richtung PC übertragen werden, falls am Arduino Taster für die Richtungen existieren/angeschlossen sind.

Ausserdem ist am Arduino eine LED-Matrix angeschlossen und kein LCD-Display. Aber auch dafür wird es natürlich Dokumentation und sehr wahrscheinlich auch Beispiele geben.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
DeaD_EyE
User
Beiträge: 1012
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

EDIT: Meine Antwort hat sich hiermit erübrigt. Zu spät die anderen Beiträge gesehen..

Firmata ist ein Protokoll zur Steuerung von Mikrocontroller, dass so in ziemlich allen Sprachen implementiert ist.

Kurz und Knapp: Das Protokoll macht aus deinem Arduino einen Slave und der Rapsberry Pi ist sein master.
Zuerst überträgst du das Standard-Firmata-Projekt zum Arduino.

Dann verbindest du den RaspberryPi mit dem Arduino, kann auch irgendein anderer Computer sein.
Der USB-Port des Arduino stellt den virtuellen COM (seriellen Port) zur Verfügung. Unter dem Raspberry Pi müsste
der unter /dev/ttyUSB0 erscheinen. Die Namensgebung kann sich aber auch geändert haben.

Der master nutzt dann eins der Frameworks für Firmata. Da gibt es z.B. PyFirmata: https://github.com/tino/pyFirmata
Damit machst du dann nichts anderes, als Eingänge und Ausgänge einzulesen bzw. zu steuern.
Das erfordert dann aber eine derauerhafte Verbindung zwischen Arduino und Raspberry Pi.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Laura98
User
Beiträge: 17
Registriert: Sonntag 3. Juni 2018, 09:59

Danke für eure Antworten :)
ich hab jetzt diese code Komplett selber geschrieben.

Code: Alles auswählen

from random import *
from tkinter import *
from time import *

WELT_GRÖSSE = 200
ZELLEN = 8
SNAKETEMPO = 250

class Welt:
    height = 20
    width = 20
    apfelposition = (1,1)

    def __init__(self, snake):
        self.snake = snake
        self.width = ZELLEN
        self.height = ZELLEN
        self.neueApfelposition()
        

    def neueApfelposition(self):
        self.apfelposition = (randint(1, self.width - 1), randint(1, self.height - 1))

        x = self.apfelposition[0]
        y = self.apfelposition[1]

        for part in self.snake.körper:
            if(x == part.x and y == part.y):
                self.neueApfelposition()
    

class SnakeKörperStück:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class Snake:

    def __init__(self):
        startpunkt = (ZELLEN/2, ZELLEN/2)

        part1 = SnakeKörperStück(startpunkt[0], startpunkt[1])
        part2 = SnakeKörperStück(startpunkt[0], startpunkt[1]+1)
        part3 = SnakeKörperStück(startpunkt[0], startpunkt[1]+2)

        self.körper = [part1,part2,part3]

        self.richtung = "up"
        self.letzte_richtung = "up"

class SnakeSpiel(Frame):

    def __init__(self, welt, snake, master=None):
        Frame.__init__(self, master)
        self.pack()
        self.welt = welt
        self.snake = snake

    def myLoop(self):
        self.zeichneWelt()
        self.Spiellogik()
        self.after(SNAKETEMPO, self.myLoop)         

    def setCanvas(self, canvas):
        self.canv = canvas  

    def zeichneWelt(self):
        self.canv.delete("all")

        # Gitter zeichnen
        zellen_grösse = WELT_GRÖSSE / self.welt.width
        for x in range(self.welt.width):
            for y in range(self.welt.height):
                self.canv.create_rectangle(x * zellen_grösse, y * zellen_grösse, x * zellen_grösse + zellen_grösse,
                                           y * zellen_grösse + zellen_grösse)

        # Essen zeichen
        self.canv.create_oval(welt.apfelposition[0] * zellen_grösse + 3, welt.apfelposition[1] * zellen_grösse + 3,
                              welt.apfelposition[0] * zellen_grösse + zellen_grösse - 3,
                              welt.apfelposition[1] * zellen_grösse + zellen_grösse - 3, fill="darkred")

        # Snake zeichnen
        for part in self.snake.körper:
            self.canv.create_rectangle(part.x * zellen_grösse, part.y * zellen_grösse, part.x * zellen_grösse + zellen_grösse,
                                       part.y * zellen_grösse + zellen_grösse, fill="green")

    
    def Spiellogik(self):
        if(self.snake.richtung == "up"):
            self.snake.letzte_richtung = "up"
            newPart = SnakeKörperStück(self.snake.körper[0].x, self.snake.körper[0].y - 1)
            self.snake.körper.insert(0, newPart)
        elif (self.snake.richtung == "left"):
            self.snake.letzte_richtung = "left"
            newPart = SnakeKörperStück(self.snake.körper[0].x - 1, self.snake.körper[0].y)
            self.snake.körper.insert(0, newPart)
        elif (self.snake.richtung == "right"):
            self.snake.letzte_richtung = "right"
            newPart = SnakeKörperStück(self.snake.körper[0].x + 1, self.snake.körper[0].y)
            self.snake.körper.insert(0, newPart)
        elif (self.snake.richtung == "down"):
            self.snake.letzte_richtung = "down"
            newPart = SnakeKörperStück(self.snake.körper[0].x, self.snake.körper[0].y + 1)
            self.snake.körper.insert(0, newPart)


        xKopf = self.snake.körper[0].x
        yKopf = self.snake.körper[0].y

        xApfel = self.welt.apfelposition[0]
        yApfel = self.welt.apfelposition[1]

        if(xKopf == xApfel and yKopf == yApfel):
            self.welt.neueApfelposition()
        else:
            self.snake.körper.pop()    

        if(xKopf >= welt.width or xKopf < 0 or yKopf >= welt.height or yKopf < 0):
            self.snake = Snake()
            

        körperIter = iter(self.snake.körper)
        next(körperIter)
        for part in körperIter:
            if(xKopf == part.x and yKopf == part.y):
                self.snake = Snake()

    def hochTaste(self, event):
        if(self.snake.letzte_richtung != "down"):
            self.snake.richtung = "up"
    def runterTaste(self, event):
        if(self.snake.letzte_richtung != "up"):
            self.snake.richtung = "down"
    def rechtsTaste(self, event):
        if(self.snake.letzte_richtung != "left"):
            self.snake.richtung = "right"
    def linksTaste(self, event):
        if(self.snake.letzte_richtung != "right"):
            self.snake.richtung = "left"

            
snake = Snake()
welt = Welt(snake)

spiel = SnakeSpiel(welt, snake)
spiel.master.title("Snake-Projekt")
spiel.master.minsize(WELT_GRÖSSE,WELT_GRÖSSE)
spiel.master.maxsize(WELT_GRÖSSE,WELT_GRÖSSE)

spiel.master.bind("<Down>", spiel.runterTaste)
spiel.master.bind("<Up>", spiel.hochTaste)
spiel.master.bind("<Left>", spiel.linksTaste)
spiel.master.bind("<Right>", spiel.rechtsTaste)

canv = Canvas(spiel.master, width=WELT_GRÖSSE, height=WELT_GRÖSSE)
canv.pack()




spiel.setCanvas(canv)
spiel.zeichneWelt()
spiel.myLoop()
spiel.mainloop()
Mir fehlt jetzt leider der Ansatz wie ich es mit dem Arduino wie ESP8266 Wifi shield mit den Arduino verbinde. Es soll ja auch durch das Drücken eines Knopfes nicht nur auf der Matrix gesteuert werden sonder auch auf dem GUI
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Zuerst das Grundsätzliche. Benutze keine *-Importe, sondern gib die Namen explizit an, die Du importieren willst. Aus random benutzt Du nur randint, aus time gar nichts. Methoden und Funktionen werden klein_mit_unterstrich geschrieben und sollten Tätigkeiten beschreiben.
if und elif sind keine Funktionen, deshalb ist es unsinnig, die Bedingung noch zu klammern.
Die Klassenattribute von Welt werden gar nicht benutzt, sondern in __init__ gleich von Instanzattributen versteckt, die können also weg. SnakeKörperStück kann eigentlich komplett durch ein 2-Tuple ersetzt werden, wie Du es ja auch schon für Apfelposition benutzt. In neueApfelposition benutzt Du Rekursion, wo man besser eine einfache while-Schleife einsetzen würde. Wären die Schlangenteile eine Liste von Tupeln, könnte das so aussehen:

Code: Alles auswählen

    def neueApfelposition(self):
        while True:
            position = (randint(1, self.width - 1), randint(1, self.height - 1))
            if position not in self.snake.körper:
                break
        self.apfelposition = position
 
In SnakeSpiel ist die Spielelogik immer noch nicht sauber von der Tk-Anzeige getrennt. In der Methode Spiellogik sind jeweils zwei Zeilen der ersten if-elif-Kaskade in jedem Block identisch, die können also herausgezogen werden. Außerdem erzeugst Du neue Snake-Objekte, obwohl das ürsprüngliche Snake-Objekt von außerhalb gekommen ist, das ist verwirrend, wenn man von außerhalb noch etwas mit snake machen möchte.
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Hallo Laura,
bevor hier einige Profis deinen Code zerlegen und ihn dir um die Ohren hauen, (Edit: zu spät :cry: )
möchte Ich dir sagen: Toll gemacht! Ich bin stolz auf dich.
Er ist nicht perfekt aber er läuft.
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Laura98: Okay, vorsicht, viel Kritik, bitte nicht davon erschlagen lassen. :-)

Die Sternchenimporte sollten nicht sein. Dadurch weiss man nicht mehr so einfach wo welcher Name her kommt, und speziell bei `tkinter` holst Du Dir ca. 190 Namen ins Modul von denen dann nur ein ganz kleiner Bruchteil tatsächlich verwendet wird. Zudem besteht bei Sternchenimporten die Gefahr von Namenskollisionen.

Das sich das Programm auf die implizite Erstellung eines `Tk`-Exemplars verlässt ist ungewöhnlich. Das würde ich explizit erstellen. So wie es jetzt ist, kann man `SnakeSpiel` auch nur mit einem `Tk`-Exemplar verwenden und nicht in einen anderen Rahmen einbetten. Das ist ein bisschen unflexibel. Da wäre es besser gleich von `Tk` zu erben, statt von `Frame`.

Auf Modulebene sollte nur Code stehen, der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Wenn man das macht, dann fallen ein paar Fehler im Programm auf, denn an einigen Stellen greifen Methoden in `SnakeSpiel` auf `welt` ausserhalb der Klasse auf Modulebene zu, statt auf das `welt`-Attribut des eigenen Objekts.

Es gibt keinen vernünftigen Grund `canvas` nicht auszuschreiben und stattdessen `canv` zu schreiben. Abkürzungen erschweren das flüssige lesen des Quelltextes, ausser es sich allgemein bekannte Abkürzungen.

Insgesamt stimmen an einigen Stellen die Verantwortlichkeiten nicht, das heisst es wird da von aussen zu tief in Objekte hinein- und durchgegriffen. Das zum Beispiel sie `Snake`-Klasse überhaupt gar keine Methoden hat ist ein Warnzeichen dafür. Und was im Hauptprogramm alles mit `spiel.master` gemacht wird, gehört dort auch nicht hin.

Die `Snake`-Klasse sollte als einzige wissen müssen wie sie intern aufgebaut ist. Wenn man zum Beispiel die Darstellung von einer Python-Liste mit `SnakeKoerperTeil`-Elementen auf eine verkettete Liste mit entsprechend geänderten `SnakeKoerperTeil`-Objekten ändern würde, dann sollten Codeänderungen dafür *nur* in der `Snake`-Klasse nötig sein. Du hast den ganzen Code dafür aber in der `SnakeSpiel`-Klasse stehen.

Was auch etwas mit dem Problem zu tun hat da jetzt noch den Arduino mit zu integrieren. `SnakeSpiel` macht zu viel. Das ist die Spiellogik *und* die GUI in einer Klasse. Das sollte getrennt sein. Dann kann man nämlich die Spiellogik so in einer Klasse kapseln, dass das selbe Objekt als Grundlage für Anzeige und Steuerung für GUI und Arduino-Schnittstelle dienen kann, und auch GUI- und Arduino-Code unabhängig voneinander sind.

Warum wird der `Canvas` ausserhalb der `SnakeSpiel`-Klasse erzeugt? Und wenn man das so macht, waraum wird das `Canvas`-Objekt dann nicht beim erzeugen der `SnakeSpiel`-Klasse übergeben, sondern erst später mit einem trivialen Setter von aussen gesetzt? Nach dem die `__init__()` durchgelaufen ist, sollte eine Klasse alle Attribute besitzen und verwendbar sein. Zudem sind trivialle Getter- und Setter-Methoden ”unpythonisch”. Man greift in solchen Fällen in Python direkt auf die Attribute zu.

Die Namensschreibweisen halten sich nicht an den Style Guide for Python Code. `Spiellogik` hat neben der Schreibweise als Klasse, noch das Problem das es keine Tätigkeit beschreibt. Denn üblicherweise sind Funktions- und Methodennamen die Tätigkeit die von der Funktion oder Methode durchgeführt wird.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Laura98
User
Beiträge: 17
Registriert: Sonntag 3. Juni 2018, 09:59

Es soll ja auch nicht perfekt sein bin ja noch Anfängerin..
Hab mir mehr Hilfe für die Verbindung mit dem Arduino gewünscht, da die Hardware auch schon steht
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dafür fehlen uns die Informationen. Irgendwas mit Wifi, arduino oder doch esp8266 und so weiter. Das ist nichts, womit man was anfangen kann :K
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Laura98: Die Strukturierung und Aufteilung des Codes hat ja durchaus etwas mit der Anbindung des Arduino zu tun. Das ist einfacher wenn man alles sauber trennt und nicht Spiellogik und GUI vermengt, denn dann muss man am Ende ja auch noch den Arduino-Code da *auch* noch mit rein schreiben und es wird noch unübersichtlicher.

„Ich bin ja noch Anfängerin“ ist kein Argument das nicht besser zu machen, bzw. für uns doch kein Grund zu sagen was daran noch getan werden muss. Gerade Anfängern muss man das doch sagen. Leute die keine Anfänger mehr sind, machen diese Fehler ja nicht. Und vom Anfänger-Status kommt man nicht runter wenn man keine Verbesserungen vorgeschlagen bekommt und umsetzt.

Konkretere Hilfe zur Einbindung des Arduino kann man nur geben, wenn Du konkretere Probleme damit beschreiben würdest. So allgemein kann man halt nur sagen Programmlogik und GUI trennen, denn der Arduino ist im Grunde ja noch eine weitere GUI die mit der selben Programmlogik interagieren muss.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Laura98
User
Beiträge: 17
Registriert: Sonntag 3. Juni 2018, 09:59

ich bin natürlich dankbar für die Tipps:-) aber bekomm gerade zeitdruck. Ich weiß zum Beispiel gar nicht mit welchen befehlen und in wie fern ich den Arduino in den Python Code einbinden soll, es soll ja mit einem ESP8266 verbunden werden
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Also, Dein Arduino ist mit einem ESP8266 verbunden, am Arduino hängen Taster und ein LED-Display. Dann mußt Du also Arduino-Code schreiben, der über ein wie auch immer geartetes TCP-Protokoll Befehle entgegen nimmt und das LED ansteuert, bzw. über TCP Tastendrücke zurücksendet. Auf Python-Seite mußt Du also einen TCP-Client schreiben, der mit dem ESP8266 als Server kommuniziert.

Dieses Setup hat den Vorteil, dass Du einfach einen Arduino-Emulator schreiben kannst, weil der ja einfach ein TCP-Server ist (z.B. wiederum in Python geschrieben).

In wie weit weißt Du schon, wie man einen ESP8266 mit einem Arduino anspricht?
Laura98
User
Beiträge: 17
Registriert: Sonntag 3. Juni 2018, 09:59

Danke @Sirius3 für die Antwort, damit kenn mich leider noch kaum aus, hätteste du ein paar Tipps wie ich es schnell verstehe?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Warum Arduino *und* ESP8266? Was bringt der ESP8266 was der Arduino nicht bringt — oder umgekehrt‽
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Laura98
User
Beiträge: 17
Registriert: Sonntag 3. Juni 2018, 09:59

Esp8266 ist ja auf den Arduino gesteckt, es soll ja eine Wifi Verbindung bestehen
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich wundere mich halt bloss weil der ESP8266 ja selbst so etwas wie ein Arduino ist (Microcontroller, GPIOs, …), wozu man dann noch den Arduino braucht. :-)
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst schon ein bisschen weiter ausholen. Du denkst mit solchen zwei Schlagworten wuesste hier jeder bescheid - dem ist nicht so. Der ESP8266 ist sein eigener MicroController (deutlich dem Arudino ueberlegen was Rechenleistung angeht) und ich betreibe den alleine, ohne Arduino. Das du ein darauf basierendes WIFI-Shield hast, kann keiner erraten. Dazu kommt dann die Frage, wie die beiden verbunden sind, ob du schon eine WIFI-Verbindung aufgebaut hast, ob du schon mit dem Arduino kommunizieren kannst, wie die LED-Matrix aussieht (nicht optisch. Technisch). Und und und. Bis hin zu Fotos von deinem Aufbau, wenns geht. Du wirst schon ein bisschen liefern muessen, denn das ist ein weites Feld, und fuer die ca 100000 verschiedenen Moeglichkeiten dieses Setup aufzubauen Code abzuliefern kann hier wohl kaum jemand leisten. Nicht zuletzt, weil den Code auszprobieren mangels eigener Hardware nicht geht.
Laura98
User
Beiträge: 17
Registriert: Sonntag 3. Juni 2018, 09:59

Das WIFI shield funktioniert bei uns nicht einzeln dass muss auf den Arduino gesteckt werden
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Bleiben die schon mehrfach gestellten Fragen, was Du schon auf Arduino-Seite programmiert hast. Wie sieht die Ansteuerung der LCD-Matrix aus, wie sprichst Du das WIFI-Modul an? Welches Protokoll verwendest Du oder gedenkst Du zu verwenden, um mit Python zu kommunizieren?
Antworten