Abfrage der Laufzeit entfernen und weiteres

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

Wollte ich morgen machen, heute Abend werfe ich den Raspi nicht mehr an. Mir geht es erstmal darum, überhaupt den Code zu verstehen. Und das ist gar nicht so einfach, denn obwohl ich schon unzählige Anleitungen durchwühlt habe, fehlt irgendwie immer genau das, was ich suche. Klar, für Fortgeschrittene sieht das anders aus, aber als Anfänger kann man z.B. nicht immer sehen, ob ein Beispiel auch wirklich korrekt ist. So habe ich z.B. ein Codebeispiel abgetippt, und beim debuggen über Thonny wurde sofort eine Fehlermeldung angezeigt. Da war eine eine einzige Codezeile drin, welche das gesamte Beispiel funktionsunfähig gemacht hat. Genau deswegen mag ich es auch nicht sonderlich, wenn ich auf Tutorials verwiesen werde, die ggf. fehlerhaft sind oder die mich noch mehr verwirren.
Am sinnvollsten ist immer, wenn man kommentierten Code hat, bei dem ich nachvollziehen kann, was da eigentlich passiert. Dann wird´s mit dem Lernen auch einfacher.
__deets__
User
Beiträge: 14539
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das verwiesene Tutorial ist das offizielle von Python. Nicht das es perfekt wäre - aber fehlerhaft ist es jedenfalls nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Frank R.: Ich verweise dann noch mal auf das Tutorial in der Python-Dokumentation. Das ist von den Leuten die auch Python entwickeln, die kennen sich also wirklich gut mit dem Thema aus. Die Codebeispiele dort sind getestet, und falls die doch mal Fehler machen, dann werden diese in der Regel auch gemeldet und behoben. Wobei die Codebeispiele in der Python-Dokumentation, die Schnippsel aus einer Python-Shell, sind auch automatisiert getestet werden. Das heisst jedes mal wenn die Dokumentation gebaut wird, dann läuft da auch ein Programm drüber und führt diese Code-Schnippsel aus und vergleicht die Ausgabe mit dem was in der Dokumentation steht. Dazu gibt es in der Python-Standardbibliothek das `doctest`-Modul: https://docs.python.org/3.6/library/doctest.html

Programmieren zu lernen oder Code zu schreiben ohne den ausführen und testen zu können, ist so sinnvoll wie ohne Wasser schwimmen lernen zu wollen. Klar kann man bestimmte Bewegungsabläufe auf dem trockenen am Boden durchführen, aber ob das tatsächlich auch im Wasser klappt, merkt man erst wenn man im Wasser ist. Und wenn es nicht klappt, kann man auch nur dort Verbesserungen vornehmen und prüfen ob die das ganze tatsächlich besser oder schlechter machen.

Man programmiert nicht in dem man ganze Programme runterschreibt und die dann laufen lässt wenn man sie fertig geschrieben hat, sondern man entwickelt Programme Stück für Stück. In dem man immer ein bisschen Code schreibt der dem Ziel näher kommt und den dann testet. Erst wenn der Code das macht was er soll, schreibt man weiteren Code dazu. Und testet den dann auch wieder. Bis man am Ende ein komplettes Programm entwickelt hat.

Ich habe immer noch das Gefühl Du drückst Dich so ein bisschen darum selbst zu lernen und willst *alles* von anderen erklärt bekommen. Denn Du hast immer noch keinen einzigen *konkreten* Punkt genannt an dem Du etwas nicht verstehst. Andererseits aber Code gezeigt der im Grunde alle Zutaten enthält um Dein Ziel zu erreichen, und der sich auf Grundlagen beschränkt die in Tutorials und Bücher beschrieben werden. Warum sollte man Dir hier Schleifen erklären, wo das doch bereits in diversen öffentlich zugänglichen Büchern und Tutorials erklärt wird? Da steht nicht exakt der Code den Du haben willst, aber den musst *Du* ja auch entwickeln. Genau das ist ja programmieren.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

Habe den Raspi eben doch noch mal befeuert, und den folgenden Code anhand eurer Beispiele angepasst:

Code: Alles auswählen

import gpiozero
import time
import random


def main():
    leds = gpiozero.LEDBoard(17, 18, 27, 22, 23, 24, 25, 4, 12)
    button = gpiozero.Button(16)
    numled = len(LED)  # Anzahl aktiver LEDs
    repeat = 5  # Muster wird Repeat mal wiederholt
    ontime = 0.1  # Leuchtdauer der LED
    button.wait_for_press()

    for i in range(repeat * numled):
        j = random.randint(0, numled - 1)
        LED[j].on()
        time.sleep(ontime)
        LED[j].off()
    
    leds.off()


if __name__ == '__main__':
    main()

Beim debuggen in Thonny werden mir nun zwei Fehler angezeigt:
  • in Zeile 9 > Python does not know what LED stands for
  • in Zeile 14 > Unused variable 'i'
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

ah, habe schon was gefunden. Natürlich geht das so nicht, denn im alten Code für RPi.GPIO war ja noch die Zeile "for i in LED: GPIO.setup(i, GPIO.OUT, initial=0)" vorhanden, deswegen meckert der wegen fehlender variable 'i'
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Frank R.: Das sind kein zwei Fehler sondern ein Fehler und eine Warnung. Beides ist recht selbsterklärend und offensichtlich. Das erste solltest Du Dir selbst klar machen. Was soll denn in der Zeile passieren?

Die Warnung bezieht sich auf das `i` was nirgends verwendet wird, weil Variablen die definiert, aber für nichts verwendet werden, in der Regel auf ein Problem hinweisen. Hier nicht, weil es bei der Schleife ausschliesslich darum geht das der Schleifenkörper wiederholt ausgeführt wird. In solchen Fällen gibt es die Konvention das man Namen die man aus syntaktischen Gründen benötigt, aber deren Wert nicht verwendet wird, einfach mit einem einzelnen Unterstrich benannt werden. Also ``for _ in range(…):`` statt ``for i in range(…):``.

Das ``leds.off`` macht übrigens keinen Sinn, weil die LEDs an der Stelle ja sowieso alle aus sind.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

So, nochmal überarbeitet:

Code: Alles auswählen

import gpiozero
import time
import random


def main():
    leds = gpiozero.LEDBoard(17, 18, 27, 22, 23, 24, 25, 4, 12)
    button = gpiozero.Button(16)
    numled = len(leds)  # Anzahl aktiver LEDs
    repeat = 5  # Muster wird Repeat mal wiederholt
    ontime = 0.1  # Leuchtdauer der LED
    button.wait_for_press()

    for _ in range(repeat * numled):
        j = random.randint(0, numled - 1)
        leds[j].on()
        time.sleep(ontime)
        leds[j].off()
    

if __name__ == '__main__':
    main()
Wenn ich das Programm starte, und den Taster drücke, leuchten die LEDs alle zufällig auf und gehen danach alle aus. So weit, so gut. Allerdings passiert beim zweiten Druck auf den Taster dann nicht mehr. Wenn ich debugge, werden mir bis ans Ende keine Fehler angezeigt, aber auch keine Meldung vom Assistant. Steckt also doch noch ein Fehler drin?
Update: Es kam doch eine Meldung vom Assistant > "looks good".
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Frank R.: Natürlich passiert beim zweiten Druck nichts mehr. Warum sollte es auch? Das Programm ist dann ja zuende und läuft nicht mehr. Wenn Du etwas wiederholen möchtest, brauchst Du eine Schleife in der das steht was Du wiederholen möchtest. Das hat Sirius3 bereits zu Deinem Punkteplan geschrieben, das man das für Punkt 6 braucht.

Ich würde ja mindestens `random.randrange()` statt `random.randint()` verwenden — dann kann man sich das abziehen der 1 sparen und auch die explizite 0. Aber besser ist wirklich das mit dem Index sein zu lassen und `random.choice()` zu verwenden.

Bei den Namen sollte man auch nichts abkürzen und zwischen Worte auch einen Unterstrich setzen. Also `led_count` statt `numled` und `on_time` statt `ontime`. Oder noch besser `led_on_duration`, dann kann man sich den Kommentar sparen. `repeat` klingt auch eher nach einer Tätigkeit, ist also ein Name für aktive Werte wie Funktionen oder Methoden. `repetitions` oder `repetition_count` wäre da passender.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

@__blackjack__

Guten Morgen,
danke für Deine Hinweise :-) Werde ich im Laufe des Tages mal austesten und dann Rückmeldung geben.
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

Hallo, leider etwas verspätet, aber hier ist der überarbeitete Code:

Code: Alles auswählen

import gpiozero
import time
import random

def main ():
    while True:
        prompt_user()

def prompt_user():
    leds = gpiozero.LEDBoard(17, 18, 27, 22, 23, 24, 25, 4, 12)
    button = gpiozero.Button(16)
    led_count = len(leds)  # Anzahl aktiver LEDs
    repetition_count = 5  # Muster wird repetition_count mal wiederholt
    on_time = 0.1  # Leuchtdauer der LED
    button.wait_for_press()

    for _ in range(repetition_count * led_count):
        j = random.randrange(led_count)
        leds[j].on()
        time.sleep(on_time)
        leds[j].off()


if __name__ == '__main__': main()
Ich habe den Code getestet, und so wie es aussieht funktioniert die Schleife auch so wie gedacht. Bitte trotzdem mal drüber gucken :-)
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Jetzt ist nur noch die Aufteilung, was alles in der Schleife passiert, etwas ungünstig.

Code: Alles auswählen

import gpiozero
import time
import random

def play_leds(leds):
    repetition_count = 5  # Muster wird repetition_count mal wiederholt
    on_time = 0.1  # Leuchtdauer der LED
    for _ in range(repetition_count * led(leds)):
        led = random.choice(leds)
        led.on()
        time.sleep(on_time)
        led.off()

def main ():
    leds = gpiozero.LEDBoard(17, 18, 27, 22, 23, 24, 25, 4, 12)
    button = gpiozero.Button(16)
    while True:
        button.wait_for_press()
        play_leds(leds)

if __name__ == '__main__':
    main()
Zuletzt geändert von Sirius3 am Samstag 28. September 2019, 15:06, insgesamt 1-mal geändert.
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

Hallo Sirius3,

danke für Deine Antwort, aber ist da nicht ein Fehler drin? Vor "if __name...." steht "prompt_user(leds)". Sollte das nicht eher "play_leds(leds)" heißen? Das Zeile bezieht sich ja auf "def play_leds(leds) :"
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

ja, hab ich geändert.
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

Habe den Code eben mal gestartet, da kommt folgende Fehlermeldung in Zeile 8:

UnboundLocalError: local variable 'led' referenced before assignment
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

muß ja auch ›len‹ heißen.
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

Sieht gut aus, habe "led" in "len" geändert. Besten Dank Sirius3.

Jetzt bleibt nur noch der Teil, dass die zuletzt aktivierte LED für einige Sekunden eingeschaltet bleibt. Ich schaue mal, ob ich dazu was finde.
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

Hallo,

nach dem ich jetzt tagelang nach Antworten gesucht habe, bin ich leider immer noch nicht schlauer geworden. Ist es überhaupt möglich, die Zufallswiedergabe so zu ändern, dass die zuletzt aktivierte LED noch für einige Sekunden eingeschaltet bleibt? Mir ist zwar bekannt, dass es neben der sleep noch eine delay Funktion gibt, aber wie ich den Code schreiben muss... Also, geht das überhaupt, oder kann ich das Suchen auch aufgeben?
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Wo willst Du denn nach Antworten suchen? Als Programmierer mußt Du die Antwort selbst entwickeln, und zwar aus Bausteinen, die Du schon kennst, also Zufalls-LED auswählen, für ein paar Sekunden warten, LED ausschalten.
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

Ich habe nach Beispielen gesucht, bei https://py-tutorial-de.readthedocs.io/de/python-3.3/# z.B. Habe aber nichts gefunden, bzw. weiß nicht wie ich das realisieren soll. Ich habe zwar eine vage Vermutung, wie das funktionieren könnte, aber das war's dann auch.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Dann mußt Du nur noch die vage Idee, die ich meiner Meinung nach recht konkret beschrieben habe, umsetzen.
Antworten