Feedback Motor Controller in Tkinter

Fragen zu Tkinter.
Antworten
seeka
User
Beiträge: 1
Registriert: Donnerstag 14. Mai 2015, 16:06

Hallo,

ich habe mit Tkinter ein Programm aus Einzelteilen zusammengestellt, dass bereits 2 Linearaktuatoren anspricht. Das funktioniert problemlos.
Jetzt zur eigentlichen Frage: Der Motor Treiber gibt mir ein Feedback ob die Motoren Strom verbrauchen oder nicht, diese Information habe ich auf 2 Pins gelegt und als Eingang definiert. In dem Programm ist ein Label angelegt dass diesen Status mit dem Text "Motoren laufen" oder "Fehler" updaten soll, je nach Eingang auf den Pins. --> beide Pins HIGH --> "Motoren laufen" ; Nur einer oder kein PIN HIGH --> "Fehler".
Ich habe versucht die textvariable des Labels mit einer Funktion zu belegen, jedoch bin ich mir als Anfänger nicht sicher ob das geht, des weiteren müsste die Funktion eine IF-Schleife abfragen, geht das?
Ich habe einige sinnvolle Themen ähnlicher Natur im Internet gefunden, aber da wird meistens die Funktion durch einen Button aufgerufen, was durch ein Label ja nicht funktioniert?

Ab Zeile 62 geht es mit der Funktion los, die aber in welcher Ausführung auch immer nichts lieferte:(.

Auch würde ich gerne eine Stop-Funktion der Motoren durchführen lassen, wenn der Motor Treiber kein Feedback mehr liefert,…

Ich wäre wirklich über jede Hilfe froh,...


Code: Alles auswählen

# Tkinter and GPIO import

import Tkinter
from Tkinter import *
import RPi.GPIO as GPIO
import time, termios, sys, tty

GPIO.setmode(GPIO.BCM)

#Motor Treiber Aktivierung
Enable  = 25
GPIO.setup(Enable,GPIO.OUT)

#Motor1 Pins als Aus- und Eingang setzen
Motor1_D = 19
Motor1_On = 20
Motor1_F = 21
GPIO.setup(Motor1_D,GPIO.OUT)
GPIO.setup(Motor1_On,GPIO.OUT)
GPIO.setup(Motor1_F,GPIO.IN)

#Motor2 Pins als Aus- und Eingang setzen
Motor2_D = 22
Motor2_On = 23
Motor2_F = 24
GPIO.setup(Motor2_D,GPIO.OUT)
GPIO.setup(Motor2_On,GPIO.OUT)
GPIO.setup(Motor2_F,GPIO.IN)

#Treiber und Motoren initialisieren: AUS
GPIO.output(Enable,GPIO.LOW)
GPIO.output(Motor1_On,GPIO.LOW)
GPIO.output(Motor2_On,GPIO.LOW)

#Definition der Auf Methode
def Tore_auf():
	GPIO.output(Enable,GPIO.HIGH)
	GPIO.output(Motor1_On,GPIO.HIGH)
	GPIO.output(Motor1_D,GPIO.LOW)
	GPIO.output(Motor2_On,GPIO.HIGH)
	GPIO.output(Motor2_D,GPIO.LOW)

#Definition der Zu Methode
def Tore_zu():
	GPIO.output(Enable,GPIO.HIGH)
	GPIO.output(Motor1_On,GPIO.HIGH)
	GPIO.output(Motor1_D,GPIO.HIGH)
	GPIO.output(Motor2_On,GPIO.HIGH)
	GPIO.output(Motor2_D,GPIO.HIGH)

#Definition der Stop Methode
def Tore_stop():
	GPIO.output(Enable,GPIO.LOW)
	GPIO.output(Motor1_On,GPIO.LOW)
	GPIO.output(Motor2_On,GPIO.LOW)

# Definition Quit
def quit():
	root.destroy()


# Definition Feedback
def feedback():
	var.set ("Status Linearaktuatoren")
#	if GPIO.input(Motor2_F):
#		var.set ("Motoren laufen")
#	else:
#		var.set ("Fehler")
#	root.update_idletasks()


root = Tk()
root.title("Steuerung Motoren")


var = StringVar()
var.set (feedback())


Fenster = Frame(root)
Fenster.grid_rowconfigure(0, weight=1)
Fenster.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
Fenster.grid(row=0, column=0, sticky=N+S+E+W)
AUFButton = Button(Fenster, text="Tore Auf!", command=Tore_auf)
AUFButton.grid(row=3, column=0, padx=5, pady=5, sticky=N+S+E+W)
ZUButton = Button(Fenster, text="Tore Zu!", command=Tore_zu)
ZUButton.grid(row=3, column=1, padx=5, pady=5, sticky=N+S+E+W)
STOPButton = Button(Fenster, text="Tore Stop!", command=Tore_stop)
STOPButton.grid(row=3, column=2, padx=5, pady=5, sticky=N+S+E+W)
BEENDENButton = Button(Fenster, text="Beenden", fg="red", command=quit)
BEENDENButton.grid(row=3, column=3, padx=5, pady=5, sticky=N+S+E+W)
x = Label(Fenster, bg="grey", textvariable=var)
x.grid(row=2, columnspan=4, padx=10, pady=10, sticky=N+S+E+W)
w = Label(Fenster, bg="grey", text="Status Linearaktuatoren")
w.grid(row=0, rowspan=2, columnspan=4,  padx=10, pady=10, sticky=N+S+E+W)


root.mainloop()

GPIO.cleanup()
BlackJack

@seeka: Etwas spät eine Antwort, aber vielleicht besser als nie. :-)

Erst einmal Lesestoff unter anderem zu Namensschreibweisen und der Formatierung von Quelltext: Style Guide for Python Code.

Kommentare sollten dem Leser einen Mehrwert über den Code liefern. Das tut im Grunde keiner der Kommentare in dem Programm wirklich. Faustregel: Kommentare beschreiben nicht *was* gemacht wird, denn das sieht man ja schon am Code, sondern *warum* etwas so gemacht wird wie es im Code steht. Das aber auch nur wenn das nicht offensichtlich ist, denn dann hätte ein Kommentar wieder keinen Mehrwert.

Es werden ganze fünf Module importiert die dann überhaupt nicht benutzt werden. Das `Tkinter`-Modul sollte aber benutzt werden statt alle Namen aus dem Modul noch einmal mit einem Sternchenimport in den aktuellen Namensraum zu holen. Oft wird das Modul unter dem Namen `tk` importiert damit der Code etwas übersichtlicher ist.

Abkürzungen die nicht allgemein bekannt sind führen dazu das man unnötig raten muss was sie bedeuten mögen. Namen sollten aber ohne rätselraten aussagen was der Wert dahinter bedeutet. Also statt `Motor2_D` besser `direction` ausschreiben und nicht nur ein `D`. Ich hoffe ich habe das jetzt richtig geraten. :-)

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm gehört in eine Funktion, üblicherweise `main()` genannt. Damit existieren dann keine Daten (ausser Konstanten) mehr auf Modulebene und man wird gezwungen sauberere Funktionen zu schreiben, die alle Wert auf denen sie operieren als Argumente übergeben bekommen und nicht auf magische Weise Objekte benutzen die irgendwie einfach da sind.

`Tore_auf()` und `Tore_zu()` enthalten fast identischen Code der sich nur durch *einen* Wert beim Aufruf von zwei `GPIO.output()`-Aufrufen unterscheidet. Damit kann man die beiden Funktionen zu einer zusammenfassen, die diesen Wert als Argument bekommt. Codewiederholungen zu vermeiden ist ein wichtiges Prinzip bei der Programmierung.

Die `quit()`-Funktion ist eine unnötige Indirektion, denn es wird ja nur die `destroy()`-Methode aufgerufen. Man könnte also an der Stelle wo man `quit()` übergibt, auch gleich die Methode übergeben.

Da man bei GUIs sowieso objektorientierte Programmierung anwenden muss, sofern sie nicht *sehr* trivial sind, kann man das auch gleich auf die Geschäftslogik des Programms anwenden und die Hardware-Steuerung nicht als ”Einzelanweisungen” über das gesamte Programm verteilen, sondern Daten und Operation darauf sinnvoll als Objekte zusammenfassen. Hier bietet sich zum Beispiel ein Motor als Objekt an und vielleicht eines das beide Motoren zu einem Objekt zusammenfasst, welches für die Steuerung der Türen verantwortlich ist.

Zum eigentlichen Problem: `Label`-Widgets kann man nicht mit einer Funktion belegen weil im Gegensatz zu Schaltflächen ja gar nicht klar ist wann die Funktion ausgeführt werden soll. So ein `Label` ist ein passives GUI-Element. Was man machen kann ist eine Funktion regelmässig vom GUI-Rahmenwerk ausführen lassen, zum Beispiel alle 0,5 Sekunden. Dafür gibt es die `after()`-Methode auf Widgets.

Das könnte dann so aussehen (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
# http://www.python-forum.de/viewtopic.php?f=18&t=36289
from __future__ import absolute_import, division, print_function
import Tkinter as tk
from functools import partial

import RPi.GPIO as GPIO


ENABLE_PIN = 25
MOTOR_A_DIRECTION_PIN = 19
MOTOR_A_ON_PIN = 20
MOTOR_A_FEEDBACK_PIN = 21
MOTOR_B_DIRECTION_PIN = 22
MOTOR_B_ON_PIN = 23
MOTOR_B_FEEDBACK_PIN = 24


class Motor(object):
    
    def __init__(self, direction_pin, on_pin, feedback_pin):
        self.direction_pin = direction_pin
        self.on_pin = on_pin
        self.feedback_pin = feedback_pin
        GPIO.setup(self.direction_pin, GPIO.OUT)
        GPIO.setup(self.on_pin, GPIO.OUT)
        GPIO.setup(self.feedback_pin, GPIO.IN)
        GPIO.output(self.on_pin, GPIO.LOW)

    @property
    def feedback(self):
        return GPIO.input(self.feedback_pin)

    def start(self, direction):
        GPIO.output(self.on_pin, GPIO.HIGH)
        GPIO.output(self.direction_pin, direction)

    def stop(self):
        GPIO.output(self.on_pin, GPIO.LOW)

    def check_and_get_feedback(self):
        result = self.feedback
        if not result:
            self.stop()
        return result


class Doors(object):
    
    OPENING, CLOSING = 'opening', 'closing'

    def __init__(self, enable_pin, motors):
        self.enable_pin = enable_pin
        self.motors = motors
        GPIO.setup(self.enable_pin, GPIO.OUT)
        GPIO.output(self.enable_pin, GPIO.LOW)

    def start(self, direction):
        if direction == self.OPENING:
            io_value = GPIO.LOW
        elif direction == self.CLOSING:
            io_value = GPIO.HIGH
        else:
            raise ValueError('unknown direction {0!r}'.format(direction))
        GPIO.output(self.enable_pin, GPIO.HIGH)
        for motor in self.motors:
            motor.start(io_value)

    def stop(self):
        GPIO.output(self.enable_pin, GPIO.LOW)
        for motor in self.motors:
            motor.stop()

    def check_and_get_feedback(self):
        return [motor.check_and_get_feedback() for motor in self.motors]


class DoorsUI(tk.Frame):

    def __init__(self, master, doors):
        tk.Frame.__init__(self, master)
        self.doors = doors

        options = {
            'column': 0,
            'columnspan': 4,
            'padx': 10,
            'pady': 10,
            'sticky': tk.NSEW,
        }
        tk.Label(self, bg='grey', text='Status Linearaktuatoren').grid(
            row=0, **options
        )
        self.status_label = tk.Label(self, bg='grey')
        self.status_label.grid(row=1, **options)

        options = {'row': 2, 'padx': 5, 'pady': 5, 'sticky': tk.NSEW}
        tk.Button(
            self,
            text='Tore Auf!',
            command=partial(self.doors.start, Doors.OPENING),
        ).grid(column=0, **options)

        tk.Button(
            self,
            text='Tore Zu!',
            command=partial(self.doors.start, Doors.CLOSING),
        ).grid(column=1, **options)

        tk.Button(
            self, text='Tore Stop!', command=self.doors.stop
        ).grid(column=2, **options)

        tk.Button(
            self, text='Beenden', fg='red', command=self.quit
        ).grid(column=3, **options)

        self.update_status()

    def update_status(self):
        running_motor_count = self.doors.check_and_get_feedback()
        self.status_label['text'] = '{0} Motoren laufen gerade.'.format(
            running_motor_count
        )
        self.after(500, self.update_status)


def main():
    GPIO.setmode(GPIO.BCM)
    try:
        root = tk.Tk()
        root.title('Steuerung Motoren')
        door_ui = DoorsUI(
            root,
            Doors(
                ENABLE_PIN,
                [
                    Motor(
                        MOTOR_A_DIRECTION_PIN,
                        MOTOR_A_ON_PIN,
                        MOTOR_A_FEEDBACK_PIN,
                    ),
                    Motor(
                        MOTOR_B_DIRECTION_PIN,
                        MOTOR_B_ON_PIN,
                        MOTOR_B_FEEDBACK_PIN,
                    ),
                ]
            ),
        )
        door_ui.pack()
        root.mainloop()
    finally:
        GPIO.cleanup()


if __name__ == '__main__':
    main()
Antworten