Inputfehler

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Zuroll
User
Beiträge: 4
Registriert: Donnerstag 12. Mai 2016, 18:55

Hallo,
ich versuch seit ner Weile mit einem Input vom GPIO einen Ton abzuspielen. Mit der Tastatur geht es, aber aus irgendwelchen Gründen bekomme ich keinen Input vom GPIO. Ich habe noch nicht viel Erfahrung mit Python deswegen könnte es sein, dass ich etw. offensichtliches übersehen habe :oops:
Den Input habe ich versucht zustande zu kriegen indem ich ein Kabel an ein 3,3V Pin und an Pin #5 gehalten habe.

Code: Alles auswählen

import math
import pyaudio
import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

GPIO.setup(5, GPIO.IN)

PyAudio = pyaudio.PyAudio

while 1:

    x=input();
    
    if GPIO.input(5)==True:
    
        BITRATE = 8000       
    
        FREQUENCY = 440
        LENGTH = 0.25 
    
        NUMBEROFFRAMES = int(BITRATE * LENGTH)
        RESTFRAMES = NUMBEROFFRAMES % BITRATE
        WAVEDATA = ''    
    
        for x in xrange(NUMBEROFFRAMES):
         WAVEDATA = WAVEDATA+chr(int(math.sin(x/((BITRATE/FREQUENCY)/math.pi))*127+128))    
    
        for x in xrange(RESTFRAMES): 
         WAVEDATA = WAVEDATA+chr(128)
              
        p = PyAudio()
        stream = p.open(format = p.get_format_from_width(1), 
                        channels = 1, 
                        rate = BITRATE, 
                        output = True)
        
        stream.write(WAVEDATA)

    if x == 2:
              
        BITRATE = 8000       

        FREQUENCY = 493.883
        LENGTH = 0.25 
    
        NUMBEROFFRAMES = int(BITRATE * LENGTH)
        RESTFRAMES = NUMBEROFFRAMES % BITRATE
        WAVEDATA = ''    
    
        for x in xrange(NUMBEROFFRAMES):
         WAVEDATA = WAVEDATA+chr(int(math.sin(x/((BITRATE/FREQUENCY)/math.pi))*127+128))    
    
        for x in xrange(RESTFRAMES): 
         WAVEDATA = WAVEDATA+chr(128)
              
        p = PyAudio()
        stream = p.open(format = p.get_format_from_width(1), 
                        channels = 1, 
                        rate = BITRATE, 
                        output = True)
        
        stream.write(WAVEDATA)
    
    
    stream.stop_stream()
    stream.close()  
    continue
Um das was in den If-Schleifen steht müsst ihr euch keine Gedanken machen.
Vielen Dank schonmal.
Zuletzt geändert von Anonymous am Samstag 4. Juni 2016, 13:35, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Ich habe es implementiert, durchfusioniert, querschraffiert und durch null geteilt.
Und verstand es (trotzdem nicht).
BlackJack

@Zuroll: `input()` blockiert solange bis Du da etwas eingegeben und mit der Eingabetaste abgeschlossen hast. Erst wenn Du *das* gemacht hast, geht der Programmfluss weiter und prüft ob auch an Pin 5 Strom anliegt.

`input()` solltest Du nicht verwenden. Da kann man beliebige Python-Ausdrücke eingeben, die dann ausgeführt werden. Das ist zum einen eine Sicherheitslücke, und zum anderen eine Fehlerquelle die schwer zu beherrschen ist, weil der Benutzer dort nicht irgendwas eingeben kann, sondern es muss syntaktisch korrektes Python sein. Und selbst wenn es das ist, kann es inhaltliche Fehler enthalten, so dass man dort mit so ziemlich jeder Ausnahme rechnen muss. Verwende `raw_input()` und wandle die eingegebene Zeichenkette kontrolliert und mit Fehlerbehandlung in einen Datentyp um mit dem Du dann weiterarbeiten möchtest.

Ach ja, und: http://if-schleife.de/
BlackJack

@Zuroll: Ein paar Anmerkungen zum Quelltext:

Eingerückt wird üblicherweise vier Leerzeichen pro Ebene.

`sleep()` wird importiert, aber nirgends verwendet.

Es ist weder üblich etwas mit ``as`` unter dem gleichen Namen zu importieren den es sowieso schon hätte, weil das irgendwie total sinnfrei ist. Noch das man im Quelltext einen Namen aus einem Modul lokal durch Zuweisung an den gleichen Namen bindet statt das gleich beim Importieren zu tun.

Man sollte dafür sorgen das die GPIO-Schnittstelle vor Ende des Programms mit `cleanup()` wieder in einen ”sauberen” Zustand versetzt wird.

Wenn man `True` oder `False` meint, sollte man nicht 1 oder 0 schreiben.

`x` wird in der gleichen Funktion für die Benutzereingabe und für Laufvariablen verwendet. Wenn man also `x` irgendwo in einem Ausdruck sieht, muss man erst einmal suchen was das `x` an der Stelle bedeutet. Zudem überschneiden sich die Verwendungen, falls nämlich nach der Eingabe der Pin aktiv ist, wird der erste ``if``-Block ausgeführt und der verändert das `x`, so dass die Bedingung beim zweiten ``if`` einen falschen Wert prüft. Es ist zudem auch kein wirklich guter Name.

Vergleiche mit literalen Wahrheitswerten sind überflüssig. Da kommt nur wieder ein Wahrheitswert heraus, so dass man gleich den Wert nehmen kann, mit dem man vergleicht, oder dessen Negierung mit ``not`` falls man auf „nicht wahr“ Testen möchte.

Namen komplett in Grossbuchstaben sind per Konvention Konstanten.

`BITRATE` ist der falsche Name, denn das bezieht sich nicht auf Bits sondern auf Samples. `RESTFRAMES` ist anscheinend eine Mischung aus Deutsch und Englisch in einem Namen. Das ist unschön.

Die Art wie Du den String mit den Sampledaten erstellst ist denkbar ungünstig. Zeichenketten sind nicht veränderbar, dass heisst wenn Du die Zeichenketten um ein Byte erweiterst, muss eine neue Zeichenkette erstellt werden in die die alte kopiert wird und dann am ende das eine neue Byte dazu kommt. Der Speicher von der alten Zeichenkette kann dann freigegeben werden. Wenn man auf diese Weise eine Zeichenkette mit 8000 Bytes zusammensetzt, werden ungefähr 32 Megabyte im Speicher herumkopiert. Hier würde sich der eingebaute Datentyp `bytearray` anbieten um das effizienter zu lösen.

`p` ist kein guter Name.

Die beiden ``if``-Blöcke enthalten fast identischen Code. Das gehört in eine Funktion ausgelagert. Zum Beispiel müsste man sonst die Änderungen bezüglich der Namen und den Wavedaten als `bytearray` zweimal durchführen, mit den Gefahren die das birgt dort Diskrepanzen oder gar Fehler einzubauen.

Die `stop_stream()`- und `close()`-Aufrufe stehen an der falschen Stelle. Es kann dort passieren, dass der Name `stream` noch gar nicht definiert wurde, was zu einem `NameError` führt, oder dass die Methoden auf einem bereits geschlossenen `Stream`-Exemplar aufgerufen werden.

``continue`` am Ende eines Schleifenkörpers macht keinen Sinn.

Ich komme dann ungefähr bei so etwas heraus:

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
import math
import time

from pyaudio import PyAudio
from RPi import GPIO


BUTTON_PIN = 5


def play_sine_wave(frequency, duration=0.25, sample_rate=8000):
    sample_count = int(sample_rate * duration)
    wave_data = bytearray(chr(128) * sample_count)
    factor = math.pi * frequency / sample_rate
    for sample_number in xrange(sample_count):
        wave_data[sample_number] = int(
            math.sin(sample_number * factor) * 127 + 128
        )

    audio = PyAudio()
    stream = audio.open(
        format=audio.get_format_from_width(1),
        channels=1,
        rate=sample_rate,
        output=True,
    )
    try:
        stream.write(bytes(wave_data))
        time.sleep(0.3)
    finally:
        stream.stop_stream()
        stream.close()


def main():
    GPIO.setmode(GPIO.BCM)
    try:
        GPIO.setup(BUTTON_PIN, GPIO.IN)

        while True:
            user_input = int(raw_input())  # TODO Error handling.

            if GPIO.input(BUTTON_PIN):
                play_sine_wave(440)

            if user_input == 2:
                play_sine_wave(493.883)

    finally:
        GPIO.cleanup()


if __name__ == '__main__':
    main()
Hat natürlich noch das ursprüngliche Problem. Und ich habe ein `time.sleep()` eingefügt, damit man den Ton auch tatsächlich zu hören bekommt.
Antworten