Programmierproblem Taster

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Benutzeravatar
juicyboose
User
Beiträge: 7
Registriert: Donnerstag 10. August 2017, 16:26

Guten Tag meine lieben Leute. Ich möchte euch mein Problem schildern. Ich bin neu in Sachen Python und Raspberry Pi.
Ich versuche gerade eine Möglichkeit, bzw. ein Programm zu erstellen, dass mir ermöglicht bestimmte Tasterabfolgen wiederzugeben.
D.h. Ich habe 4 Taster. Ich möchte, dass wenn ich die Taster 1 2 3 2 1 drücke eine 50 erscheint und wenn ich 1 2 1 drücke die 10 erscheint. Ich bekomme gerade bloß nicht heraus wie man Tastendrückabfolgen in Python hineinbekommt. Mein Code sieht bis jetzt so aus:

Code: Alles auswählen

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.Board)
GPIO.setup(37, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(35, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(33, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(31, GPIO.IN, pull_up_down=GPIO.PUD_UP)

while True:
 button_1 = GPIO.input(37)
 button_2 = GPIO.input(35)
 button_3 = GPIO.input(33)
 button_4 = GPIO.input(31)

  if button_1 == False:
    print ("random")
    time.sleep(0.5)
Ich weis nun leider nicht wie ich dieses nacheinander drücken der Taster in den Code bekomme... Sowas wie.
if button1 then button 2 then button 1
print 10
if button 1 then button 2 then button 3 then button 2 then button 1
print 50

Ich wäre sehr dankbar für Hilfe :)
Zuletzt geändert von Anonymous am Donnerstag 10. August 2017, 17:21, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@juicyboose: Du musst Dir die Tastendrücke merken, und jedes mal wenn einer dazu kommt, prüfen ob das eine Sequenz ergibt, zu der eine Ausgabe passieren soll.

Tastendrücke wirst Du wahrscheinlich am besten über die Flanken erkennen, und zwar nicht manuell, sondern über die entsprechende Funktionalität vom `GPIO`-Modul mit Rückruffunktionen. Das bedeutet dann allerdings nebenläufige Programmierung, mit all den Problemen die man sich damit einfängt. Du musst zum Beispiel den Programmteil der einen neuen Tastendruck verarbeitet und eventuell eine Ausgabe daraufhin auslöst durch eine Sperre absichern (`threading.Lock()`) damit keine Tastendrücke verloren gehen können, oder während des Prüfens auf eine Sequenz die Sequenz erweitert wird.

Durch die Nebenläufigeit wird es hier übrigens nicht mehr so einfach eine Lösung ohne objektorientierte Programmierung (OOP) zu schreiben.

`GPIO.setup()` kann man übrigens auch eine Sequenz von Pin-Nummern übergeben, da reicht dann *ein* Aufruf, wenn die anderen Argumente gleich bleiben.

Wenn man anfängt Namen zu nummerieren, möchte man meistens nicht einzelne Namen sondern einen Namen für eine Datenstruktur. Oft ist das eine Liste. Auch im Falle der `button_*` würde ich sagen. Die könnte man wenn man für das `setup()` sowieso schon eine Liste mit den Nummern hat, auch recht einfach mit einer „list comprehension“ erstellen.

Eingerückt wird per Konvention vier Leerzeichen pro Ebene.

Und am Programmende sollte man sicherstellen das `GPIO.cleanup()` aufgerufen wird. Dafür braucht man ``try``/``finally`` als Syntaxkonstrukt.
Benutzeravatar
noisefloor
User
Beiträge: 3829
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

wenn du die neuere gpiozero Bibliothek benutzt ist das a) so wie so einfacher und b) kannst ganz einfach ein Callback Funktion wie `when_pressed()` an jeden Taster binden, der den Tastendruck dann wie von BlackJack beschrieben protokolliert.

Gruß, noisefloor
Benutzeravatar
juicyboose
User
Beiträge: 7
Registriert: Donnerstag 10. August 2017, 16:26

Da ich wie gesagt totaler Anfänger bin bräuchte ich etwas direktes gesagt. Also mein Ziel ist es ein Programm zum Laufen zu kriegen, dass:
Bestimmt Werte nach einer Tasteneingabefolge addiert. D.h. Ich drücke Knopf 1 2 3 2 1 und es kommt 50 heraus. Diese Zahl soll gespeichert werden. Drücke ich nun 1 2 1 soll 10 entstehen und zu den 50 addiert werden und 60 anzeigen lassen. Gebe ich nun 1 2 3 4 3 2 1 ein habe ich 100 und es entsteht 160. Da ich mit den Codes nicht ganz vertraut bin würde ich auch über Hilfe in Skype sehr glücklich sein :) Wenn es jemanden möglich wäre und mir über Skype etwas nachhelfen könnte, würde ich auch etwas entgegenkommen :)
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dieses Forum hilft Leuten, die wirklich *anfangen* wollen zu programmieren. Wenn du jemanden suchst, der dir für Geld ein Programm schreibt, gibt es dafür spezielle Dienste. ZB Rent a Coder.
Benutzeravatar
juicyboose
User
Beiträge: 7
Registriert: Donnerstag 10. August 2017, 16:26

Ich möchte keinen Dienst kaufen. Ich schätze es wenn sich jemand Zeit für mich nimmt. In dieser Zeit könnte er natürlich auch wesentlich interessantere Sachen für sich selbst machen. Nur dass ich eben ganz am Anfang stehe. Falls hilfe möglich ist habe ich auch meinen Stream angeschalten.
www.twitch.tv/juicyboose
BlackJack

@juicyboose: Kannst Du verraten worum es geht? Also was die ganzen Zahlen und Zahlenfolgen bedeuten sollen? Dann ist es vielleicht einfacher die tatsächlichen Anforderungen besser zu bestimmen. Also beispielsweise was passieren soll wenn der Benutzer andere Folgen drückt. Oder auch ob es Zufall ist das alle Deine Beispiele von Zahlenfolgen einen präfixfreien Code darstellen, oder ob das garantiert ist. Falls nicht, was soll dann in Fällen passieren bei dem eine Zahlenfolge Präfix einer anderen ist? Oder auch schon bei einem präfixfreiem Code: was soll passieren wenn eine Zahlenfolge in einer anderen enthalten ist?

Bevor man etwas Lösen kann, muss man das Problem ja erst einmal ausreichend verstanden haben.
Benutzeravatar
juicyboose
User
Beiträge: 7
Registriert: Donnerstag 10. August 2017, 16:26

Guten Tag. Also es gibt nur die maximalen Zahlenfolgen 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1. D.h. es entsteht ein Gerät das Bauteile misst. Ist das Bauteil 10mm lang, setzt man es in eine Vorrichtung. Diese Vorrichtung besteht aus verschiedenen Kontaktleitbahnen. Ein Kontakt ist dauerhaft auf 1. D.h. wird das kleinste Bauteil eingesetzt, so bewegt sich die Abfrage auf Kontakt 2. Nimmt man das Bauteil nun raus, zieht eine Feder die Abfrage wieder auf Kontakt 1, damit die Rücksetzung entsteht. Nehme ich nun ein großes Bauteil, bewegt sich die Abfrage über 2 3 4 5. Nehme ich das Bauteil nun wieder raus, ist es eigentlich nur wichtig, den Kontakt 1 abzufragen, da er ja sowieso 4 3 2 "überspringt". Um es Prototypenhaft abzubilden habe ich es zuerst mit Tastern probiert, da es für mich vorerst zu teuer wäre eine eigene Platine herstellen zu lassen, da ich erst über die Logik des Programms Bescheid wissen muss, bevor ich solche Platinen fertigen lassen kann. Nun zur Frage, wenn die Zahlenfolgen anders sind. Es soll immer der höchste Wert abgefragt werden. Kommt nun jemand auf die Idee, ein kleines Bauteil einzusetzten und ohne Rücksetzung direkt das große Teil einzusetzen, soll nur das große Bauteil ausgegeben werden. Kommt jemand auf die Idee ein mittleres Bauteil einzusetzen. Sagen wir mal. Es geht von 1 - 5 dann geht er von 5 auf 2 und dann mit einem größeren Bauteil wieder auf 7 und dann auf 3 und wieder auf 5 und dann erst auf 1 zum Zurücksetzen, soll auch nur das größte Bauteil angenommen werden. D.h. die 7. Ich hoffe das macht die Erklärung einfacher. Falls weitere Fragen bestehen kann ich gerne antworten. Ich werde nun weiter nach einer Programmierungshilfe suchen, die eine Sequenz unterstützt :) Vielen dank im Voraus und ich würde mich auf neue Nachrichten freuen.
BlackJack

@juicyboose: Mist, das Muster in den Zahlenfolgen hätte einem ja eigentlich auffallen können/müssen. :-)

Also hat man sozusagen Rahmen von Werten die mit einer 0 anfangen und enden, und man sucht den Maximalwert innerhalb des Rahmens. Und den bildet man noch einmal auf eine Länge ab.

Ich habe jetzt 0 geschrieben, weil Programmierer üblicherweise mit 0 anfangen zu zählen.

Vollkommen ungetestet (bis auf die Beispiele in den DocStrings):

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
from __future__ import absolute_import, division, print_function
from contextlib import closing
from functools import partial
from itertools import imap, takewhile
from Queue import Queue
from RPi import GPIO

CONTACT_PINS = [37, 35, 33, 31]


def create_contact_stream(pins):
    """Create an iterable of values for rising edges on given pins.

    The values are the index values of the given pins.  So the first pin will
    yield 0, the second 1, the third 2, and so on.

    The returned object has as ``close()`` method which detaches the
    callbacks/listeners for the pins.  If you don't close the object and don't
    consume the iterable, the values for rising edges will be queued and fill
    the memory over time.
    """
    GPIO.setup(pins, GPIO.IN, GPIO.PUD_UP)

    queue = Queue()
    for i, pin in enumerate(pins):
        GPIO.add_event_detect(pin, GPIO.RISING, lambda _, i=i: queue.put(i))

    try:
        while True:
            yield queue.get()
    finally:
        for pin in pins:
            GPIO.remove_event_detect(pin)


def iter_peak_values(contact_values):
    """Return the peak values between two zero values.

    >>> list(iter_peak_values([0, 1, 2, 1, 0, 0, 1, 0, 0, 1, 2, 3, 2, 1, 0]))
    [2, 1, 3]

    Values between an ending zero and the starting zero of the next measurement
    are silently ignored.  Like the 42 in this example:

    >>> list(iter_peak_values([0, 1, 2, 1, 0, 42, 0, 1, 0]))
    [2, 1]
    """
    contact_values = iter(contact_values)
    takewhile_not_zero = partial(takewhile, bool)
    while True:
        # 
        # Skip to first zero value.
        # 
        for _ in takewhile_not_zero(contact_values):
            pass

        try:
            yield max(takewhile_not_zero(contact_values))
        except ValueError:
            # 
            # When `max()` gets an empty iterable we are finished.
            # 
            break


def iter_lengths(lengths, peak_values):
    """Map an iterable of peak values to the given lengths.

    >>> list(iter_lengths([10, 50, 100], [2, 1, 3]))
    [50, 10, 100]
    """
    return imap(lambda x: lengths[x - 1], peak_values)


def main():
    GPIO.setmode(GPIO.BOARD)
    try:
        lengths = [10, 50, 100]
        with closing(create_contact_stream(CONTACT_PINS)) as contact_values:
            total_length = 0
            for length in iter_lengths(
                lengths, iter_peak_values(contact_values)
            ):
                total_length += length
                print(total_length)
    finally:
        GPIO.cleanup()


if __name__ == '__main__':
    main()
Benutzeravatar
juicyboose
User
Beiträge: 7
Registriert: Donnerstag 10. August 2017, 16:26

Teste gerade
Benutzeravatar
juicyboose
User
Beiträge: 7
Registriert: Donnerstag 10. August 2017, 16:26

Also ich habe den Code jetzt so in Python 2 eingegeben und auf 8 Taster erhöht und es wird zwar kein Fehler dargestellt aber nachdem ich die Knöpfe in der Reihenfolge drücke passiert auch nichts. Kann es sein dass die Tasten bei dem Code garnicht abgefragt werden oder woran erkenne ich dies?

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
from __future__ import absolute_import, division, print_function
from contextlib import closing
from functools import partial
from itertools import imap, takewhile
from Queue import Queue
from RPi import GPIO

CONTACT_PINS = [40, 38, 37, 36, 35, 33, 32, 31]

def create_contact_stream(pins):
    
    GPIO.setup(pins, GPIO.IN, GPIO.PUD_UP)
    
    queue = Queue()
    for i, pin in enumerate(pins):
        GPIO.add_event_detect(pin, GPIO.RISING, lambda _, i=i: queue.put(i))

    try:
        while True:
            yield queue.get()
    finally:
        for pin in pins:
            GPIO.remove_event_detect(pin)

def iter_peak_values(contact_values):
    
    contact_values = iter(contact_values)
    takewhile_not_zero = partial(takewhile, bool)
    while True:
        for _ in takewhile_not_zero(contact_values):
            pass
        
        try:
            yield max(takewhile_not_zero(contact_values))         
        except ValueError:
            break
                      
def iter_lengths(lengths, peak_values):
    return imap(lambda x: lengths[x - 1], peak_values)

def main():
    GPIO.setmode(GPIO.BOARD)
    try:
        lengths = [1, 5, 10, 20, 50]
        with closing(create_contact_stream(CONTACT_PINS)) as contact_values:
            total_amounts = 0
            for length in iter_lengths(
                lengths, iter_peak_values(contact_values)):
               	total_length += length
                print(total_length)
    finally:
        GPIO.cleanup()

if __name__ == '__main__':
    main()
Zuletzt geändert von Anonymous am Montag 14. August 2017, 14:23, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@juicyboose: Du müsstest halt schauen an welcher Stelle keine Werte (mehr) generiert werden. Da die beiden Generatorfunktionen die nichts mit der Hardware zu tun haben, (oberflächlich) getestet sind, würde ich mit `create_contact_stream()` anfangen. Ob das bei steigender Flanke jeweils eine Zahl liefert.

Und schaust Du auch nach Ausnahmen die ausgelöst werden? Denn die `total_length`-Zeile wird beispielsweise eine Ausnahme auslösen weil Du das dort nicht umbenannt hast, aber bei der Initialisierung zu `total_amount`.

Und Du hast mehr Taster als Werte in `lengths`. Das wird auch zu einer Ausnahme (`IndexError`) führen.
Benutzeravatar
juicyboose
User
Beiträge: 7
Registriert: Donnerstag 10. August 2017, 16:26

Hallo noch einmal. Also ich habe jetzt alles durchgetestet und es kommt mir etwas komisch vor. Es entstehen immer 4 Fälle.
Bild
Der erste Fall gibt mir einfach eine 1 wieder wenn ich 0 1 0 sehr schnell drücke.
Der zweite Fall schließt den Prozess ab sobald ich 0 drücke.
Der dritte Fall ist weiter unten zu erkennen. ich habe es geschafft 2 mal die 010 combo zu machen.
Und der vierte Fall gibt mir die untere Fehlermeldung aus. Diese Meldung erhalte ich aber auch manchmal, wenn ich nach dem Restart nur die 0 drücke.
Falls Interesse an der ganzen Aufgabe besteht könnte ich ein Video von meinem Aufbau machen und einfach mal hochladen. Mein Youtubechannel ist folgender:
www.youtube.com/juicyboose
BlackJack

@juicyboose: `SystemError` ist gar nicht gut. Da ist irgendwas im Python-Interpreter oder einem in C geschriebenen Modul fehlerhaft. Ich tippe hier mal auf das GPIO-Modul. Eventuell kann man das up- oder downgraden. Oder Du schaust Dich nach einem anderen Modul um um die GPIOs anzusprechen. `pigpio` zum Beispiel.
Antworten