Delay durch Laufzeit

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
MaWiSy
User
Beiträge: 3
Registriert: Freitag 11. September 2020, 11:53

Hallo zusammen,

ich bin sowohl neu in diesem Forum, als auch noch relativ unerfahren was Python und Progammierung angeht.

Ich habe mir einen Hobbybaukasten (FNK0025-Freenove RFID Starter Kit for Raspberry Pi) und einfach mal losgelegt.

Nun habe ich mir einen Schaltkreis aufgebaut, der an sich super funktioniert, allerdings mit der Dauer immer langsamer wird. Ich nutze einen Raspberry Zero mit aktuellster Firmware. Das gleiche Problem tritt aber auch bei einem Raspberry Pi 4 mit 4GB auf.
Zum generellen Aufbau:
ich habe eine RGB LED Lampe verbaut, jeder der Dioden-Pin ist mit einem 220 Ohm Widerstand verknüpft. Ein LCD Display ist angeschlossen, der durchgehend "Hello World" ausgibt, bis eine Aktion eintritt. Dieser liegt an 5V und GND, sowie SDA und SCL. Ein Ultraschallsensor (HC SR04) ist verbaut, der permanent eine Distanz von bis zu 220 cm misst, dieser soll Alarm geben, wenn sich etwas in der Reichtweite von 1 cm bis 10 cm vor dem Sensor befindet. Dabei soll die RGB von grün auf rot wechselt, der Display wechselt den Schriftzug und ein Buzzer wird auf High gesetzt, gibt also einen Warnton aus. Vor dem Echo-Pin ist ein 1k Ohm Widerstand gelegt, von diesem Potential ist ebenfalls ein Kabel mit 2k Ohm an GND. Zusätzlich habe ich einen Push Button eingebaut, bei einmaligem Drücken blinkt die LED blau und es wird mit der Picamera ein Foto gemacht und gespeichert. Sollte der Button mehrfach gedrückt werden, so wird ein die LED auf blau gesetzt und ein Video aufgenommen. Auch bei diesen beiden Aktion verändert sich der Schriftzug der LCD Anzeige.
Der Aufbau und die Parametrierung der Widerstand ist alles aus der Anleitung übernommen, weshalb ich da wenig Fehlerpotential sehe.

Nun zu meinem Problem:
Die erste Zeit läuft dieses Skript einwandfrei, weshalb ich eine fehlerhafte Verkabelung ausschließe. Der eingebaute Ultraschallsensor besitzt beim Starten des Skriptes nahezu gar keine Verzögerung, später ist jedoch ein leichter Delay sichtbar. Schlimmer ist jedoch die Funktion des eingebauten Push-Buttons. Durch die Funktionen ist gegeben, dass dieser beim Einmaligen drücken 1s abwartet und die Knopfdrücke zählt, danach geht es zu einer Fallunterscheidung, ob nur 1 mal gedrückt wurde -> Foto oder ob mehrfach gedrückt wurde -> Video. Lasse ich dieses Skript einige Zeit laufen, dann verzögert sich die Reaktionszeit zwischen Knopfdruck und Foto (bzw. Aufleuchten der dazugehörigen LED Farbe). Ab einer Laufzeit von über 1,5 h ist die Verzögerung dermaßen groß, dass kein Foto oder Video mehr aufgenommen werden kann.

Gibt es eine Art temporären Buffer, den das Programm anlegt, welcher mit der Zeit anwächst und somit das Programm verlangsamt? Wie könnte ich den wieder leeren? Bis auf die gespeicherten Fotos/ Videos muss nichts dauerhaft gespeichert werden. Oder habe ich einen Fehler im Skript, der meinen Loop nach und nach zum erliegen bringt. Ich würde für Hilfe und Anregungen sehr dankbar.


Mein Code:

Code: Alles auswählen

from PCF8574 import PCF8574_GPIO
from Adafruit_LCD1602 import Adafruit_CharLCD
import RPi.GPIO as GPIO
import time
from picamera import PiCamera
import os
import smbus
import datetime



pins = [11, 12, 13]         # define the pins for R:11,G:12,B:13 of a RGB LED
trigPin = 16                #Trigger pin of the ultrasonic sensor
echoPin = 18                #Echo Pin of the ultrasonic sensor
buzzerPin = 15              # define buzzerPin
MAX_DISTANCE = 220          # define the maximum measuring distance, unit: cm
timeOut = MAX_DISTANCE*60   # calculate timeout according to the maximum measuring distance
sw_in = 22                  #GPIO 25 Data pin of the push Button
camera = PiCamera()



def setup():
    global pwmRed,pwmGreen,pwmBlue, maxdistance
    maxdistance=10              #max distance for the ultrasonic sensor until the alert comes
    GPIO.setmode(GPIO.BOARD)       # use PHYSICAL GPIO Numbering
    GPIO.setup(pins, GPIO.OUT)     # set RGBLED pins to OUTPUT mode
    GPIO.output(pins, GPIO.HIGH)   # make RGBLED pins output HIGH level
    GPIO.setup(trigPin, GPIO.OUT)   # set trigPin to OUTPUT mode
    GPIO.setup(echoPin, GPIO.IN)    # set echoPin to INPUT mode
    GPIO.setup(buzzerPin, GPIO.OUT)   # set buzzerPin to OUTPUT mode
    GPIO.setup(sw_in,GPIO.IN,pull_up_down=GPIO.PUD_UP) #set Push Button as Input
    GPIO.add_event_detect(sw_in,GPIO.FALLING)
    pwmRed = GPIO.PWM(pins[0], 2000)      # set PWM Frequence to 2kHz
    pwmGreen = GPIO.PWM(pins[1], 2000)  # set PWM Frequence to 2kHz
    pwmBlue = GPIO.PWM(pins[2], 2000)    # set PWM Frequence to 2kHz
    pwmRed.start(0)      # set initial Duty Cycle to 0
    pwmGreen.start(0)
    pwmBlue.start(0)

    
def setColor(r_val,g_val,b_val):      
    pwmRed.ChangeDutyCycle(r_val)     
    pwmGreen.ChangeDutyCycle(g_val)   
    pwmBlue.ChangeDutyCycle(b_val)
    
def blink():
    r=0  
    g=0
    b=0
    setColor(r,g,b)
    time.sleep(.05)
    r=0  
    g=0
    b=100
    setColor(r,g,b)
    time.sleep(.05)
    return
    

def pulseIn(pin,level,timeOut): # obtain pulse time of a pin under timeOut
    t0 = time.time()
    while(GPIO.input(pin) != level):
        if((time.time() - t0) > timeOut*0.000001):
            return 0;
    t0 = time.time()
    while(GPIO.input(pin) == level):
        if((time.time() - t0) > timeOut*0.000001):
            return 0;
    pulseTime = (time.time() - t0)*1000000
    return pulseTime
    
def getSonar():     # get the measurement results of ultrasonic module,with unit: cm
    GPIO.output(trigPin,GPIO.HIGH)      # make trigPin output 10us HIGH level 
    time.sleep(0.00001)     # 10us
    GPIO.output(trigPin,GPIO.LOW) # make trigPin output LOW level 
    pingTime = pulseIn(echoPin,GPIO.HIGH,timeOut)   # read plus time of echoPin
    distance = pingTime * 340.0 / 2.0 / 10000.0     # calculate distance with sound speed 340m/s 
    return distance


def loop():
    mcp.output(3,1)     # turn on LCD backlight
    lcd.begin(16,2)     # set number of LCD lines and columns
    while(True):
        lcd.setCursor(0,0)  # set cursor position
        lcd.message( 'Hello World' )
        distance = getSonar() # get distance
        #print ("The distance is : %.2f cm"%(distance))
        r=0  
        g=100
        b=0
        setColor(r,g,b)          #set random as a duty cycle value
        GPIO.output(buzzerPin,GPIO.LOW)
        if distance < maxdistance:
            time.sleep(0.1)
            distance = getSonar() 
            if distance < maxdistance:
                    if distance > 1.0:
                        r=100  
                        g=0
                        b=0
                        setColor(r,g,b)
                        #GPIO.output(buzzerPin,GPIO.HIGH)
                        lcd.clear()
                        lcd.setCursor(0,0)  # set cursor position
                        lcd.message( 'Attention! \n' )# display CPU temperature
                        lcd.message( 'Keep distance' )   # display the time
                        time.sleep(0.5)
                        lcd.clear()
        #print ('r=%d, g=%d, b=%d ' %(r ,g, b))
        if GPIO.event_detected(sw_in):   #geht rein bei falling edge, also beim drücken des knopfes
            r=100
            g=100
            b=100
            setColor(r,g,b)
            GPIO.remove_event_detect(sw_in)
            now = time.time()
            count = 1
            GPIO.add_event_detect(sw_in,GPIO.FALLING)
            while time.time() < now + 1.0: 
                if GPIO.event_detected(sw_in):
                    count +=1
            if count <= 1:
                blink()
                blink()
                blink()
                print("picture will now be captured..")
                lcd.clear()
                lcd.setCursor(0,0)  
                lcd.message( 'Picture will \n' )
                lcd.message( 'be captured..' )   
                camera.capture('/test/' + datetime.datetime.now().strftime('%Y-%m-%d%H:%M:%S') + '.png')
                blink()
                blink()
                blink()
                GPIO.remove_event_detect(sw_in)
                GPIO.add_event_detect(sw_in,GPIO.FALLING)
                lcd.clear()
            else:
                print("start recording...")
                r=0  
                g=0
                b=100
                setColor(r,g,b)
                lcd.clear()
                lcd.setCursor(0,0)  
                lcd.message( 'Video is \n' )
                lcd.message( 'recording..' )   
                camera.resolution = (640, 480)
                camera.start_recording('/test/' + datetime.datetime.now().strftime('%Y-%m-%d%H:%M:%S') + '.h264')
                camera.wait_recording(5)
                camera.stop_recording()
                lcd.clear()
        
def destroy():
    pwmRed.stop()
    pwmGreen.stop()
    pwmBlue.stop()
    GPIO.cleanup()
    lcd.clear()
    
    
PCF8574_address = 0x27  # I2C address of the PCF8574 chip.
PCF8574A_address = 0x3F  # I2C address of the PCF8574A chip.
# Create PCF8574 GPIO adapter.
try:
    mcp = PCF8574_GPIO(PCF8574_address)
except:
    try:
        mcp = PCF8574_GPIO(PCF8574A_address)
    except:
        print ('I2C Address Error !')
        exit(1)
# Create LCD, passing in MCP GPIO adapter.
lcd = Adafruit_CharLCD(pin_rs=0, pin_e=2, pins_db=[4,5,6,7], GPIO=mcp)


if __name__ == '__main__':     # Program entrance
    print ('Program is starting...')
    setup()
    try:
        loop()
    except KeyboardInterrupt:  # Press ctrl-c to end the program.
        destroy()         # release GPIO resource


    
The two imported scripts:

Code: Alles auswählen

import smbus
import time
class PCF8574_I2C(object):
    OUPUT = 0
    INPUT = 1
    
    def __init__(self,address):
        # Note you need to change the bus number to 0 if running on a revision 1 Raspberry Pi.
        self.bus = smbus.SMBus(1)
        self.address = address
        self.currentValue = 0
        self.writeByte(0)   #I2C test.
        
    def readByte(self):#Read PCF8574 all port of the data
        #value = self.bus.read_byte(self.address)
        return self.currentValue#value
        
    def writeByte(self,value):#Write data to PCF8574 port
        self.currentValue = value
        self.bus.write_byte(self.address,value)

    def digitalRead(self,pin):#Read PCF8574 one port of the data
        value = readByte()  
        return (value&(1<<pin)==(1<<pin)) and 1 or 0
        
    def digitalWrite(self,pin,newvalue):#Write data to PCF8574 one port
        value = self.currentValue #bus.read_byte(address)
        if(newvalue == 1):
            value |= (1<<pin)
        elif (newvalue == 0):
            value &= ~(1<<pin)
        self.writeByte(value)   

def loop():
    mcp = PCF8574_I2C(0x27)
    while True:
        #mcp.writeByte(0xff)
        mcp.digitalWrite(3,1)
        print ('Is 0xff? %x'%(mcp.readByte()))
        time.sleep(1)
        mcp.writeByte(0x00)
        #mcp.digitalWrite(7,1)
        print ('Is 0x00? %x'%(mcp.readByte()))
        time.sleep(1)
        
class PCF8574_GPIO(object):#Standardization function interface
    OUT = 0
    IN = 1
    BCM = 0
    BOARD = 0
    def __init__(self,address):
        self.chip = PCF8574_I2C(address)
        self.address = address
    def setmode(self,mode):#PCF8574 port belongs to two-way IO, do not need to set the input and output model
        pass
    def setup(self,pin,mode):
        pass
    def input(self,pin):#Read PCF8574 one port of the data
        return self.chip.digitalRead(pin)
    def output(self,pin,value):#Write data to PCF8574 one port
        self.chip.digitalWrite(pin,value)
        
def destroy():
    bus.close()
    
if __name__ == '__main__':
    print ('Program is starting ... ')
    try:
        loop()
    except KeyboardInterrupt:
        destroy()
        
    

Code: Alles auswählen

from time import sleep


class Adafruit_CharLCD(object):

    # commands
    LCD_CLEARDISPLAY        = 0x01
    LCD_RETURNHOME          = 0x02
    LCD_ENTRYMODESET        = 0x04
    LCD_DISPLAYCONTROL      = 0x08
    LCD_CURSORSHIFT         = 0x10
    LCD_FUNCTIONSET         = 0x20
    LCD_SETCGRAMADDR        = 0x40
    LCD_SETDDRAMADDR        = 0x80

    # flags for display entry mode
    LCD_ENTRYRIGHT          = 0x00
    LCD_ENTRYLEFT           = 0x02
    LCD_ENTRYSHIFTINCREMENT = 0x01
    LCD_ENTRYSHIFTDECREMENT = 0x00

    # flags for display on/off control
    LCD_DISPLAYON           = 0x04
    LCD_DISPLAYOFF          = 0x00
    LCD_CURSORON            = 0x02
    LCD_CURSOROFF           = 0x00
    LCD_BLINKON             = 0x01
    LCD_BLINKOFF            = 0x00

    # flags for display/cursor shift
    LCD_DISPLAYMOVE         = 0x08
    LCD_CURSORMOVE          = 0x00

    # flags for display/cursor shift
    LCD_DISPLAYMOVE         = 0x08
    LCD_CURSORMOVE          = 0x00
    LCD_MOVERIGHT           = 0x04
    LCD_MOVELEFT            = 0x00

    # flags for function set
    LCD_8BITMODE            = 0x10
    LCD_4BITMODE            = 0x00
    LCD_2LINE               = 0x08
    LCD_1LINE               = 0x00
    LCD_5x10DOTS            = 0x04
    LCD_5x8DOTS             = 0x00

    def __init__(self, pin_rs=25, pin_e=24, pins_db=[23, 17, 21, 22], GPIO=None):
        # Emulate the old behavior of using RPi.GPIO if we haven't been given
        # an explicit GPIO interface to use
        if not GPIO:
            import RPi.GPIO as GPIO
            GPIO.setwarnings(False)
        self.GPIO = GPIO
        self.pin_rs = pin_rs
        self.pin_e = pin_e
        self.pins_db = pins_db

        self.GPIO.setmode(GPIO.BCM) #GPIO=None use Raspi PIN in BCM mode
        self.GPIO.setup(self.pin_e, GPIO.OUT)
        self.GPIO.setup(self.pin_rs, GPIO.OUT)

        for pin in self.pins_db:
            self.GPIO.setup(pin, GPIO.OUT)

        self.write4bits(0x33)  # initialization
        self.write4bits(0x32)  # initialization
        self.write4bits(0x28)  # 2 line 5x7 matrix
        self.write4bits(0x0C)  # turn cursor off 0x0E to enable cursor
        self.write4bits(0x06)  # shift cursor right

        self.displaycontrol = self.LCD_DISPLAYON | self.LCD_CURSOROFF | self.LCD_BLINKOFF

        self.displayfunction = self.LCD_4BITMODE | self.LCD_1LINE | self.LCD_5x8DOTS
        self.displayfunction |= self.LCD_2LINE

        # Initialize to default text direction (for romance languages)
        self.displaymode = self.LCD_ENTRYLEFT | self.LCD_ENTRYSHIFTDECREMENT
        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)  # set the entry mode

        self.clear()

    def begin(self, cols, lines):
        if (lines > 1):
            self.numlines = lines
            self.displayfunction |= self.LCD_2LINE

    def home(self):
        self.write4bits(self.LCD_RETURNHOME)  # set cursor position to zero
        self.delayMicroseconds(3000)  # this command takes a long time!

    def clear(self):
        self.write4bits(self.LCD_CLEARDISPLAY)  # command to clear display
        self.delayMicroseconds(3000)  # 3000 microsecond sleep, clearing the display takes a long time

    def setCursor(self, col, row):
        self.row_offsets = [0x00, 0x40, 0x14, 0x54]
        if row > self.numlines:
            row = self.numlines - 1  # we count rows starting w/0
        self.write4bits(self.LCD_SETDDRAMADDR | (col + self.row_offsets[row]))

    def noDisplay(self):
        """ Turn the display off (quickly) """
        self.displaycontrol &= ~self.LCD_DISPLAYON
        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)

    def display(self):
        """ Turn the display on (quickly) """
        self.displaycontrol |= self.LCD_DISPLAYON
        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)

    def noCursor(self):
        """ Turns the underline cursor off """
        self.displaycontrol &= ~self.LCD_CURSORON
        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)

    def cursor(self):
        """ Turns the underline cursor on """
        self.displaycontrol |= self.LCD_CURSORON
        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)

    def noBlink(self):
        """ Turn the blinking cursor off """
        self.displaycontrol &= ~self.LCD_BLINKON
        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)

    def blink(self):
        """ Turn the blinking cursor on """
        self.displaycontrol |= self.LCD_BLINKON
        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)

    def DisplayLeft(self):
        """ These commands scroll the display without changing the RAM """
        self.write4bits(self.LCD_CURSORSHIFT | self.LCD_DISPLAYMOVE | self.LCD_MOVELEFT)

    def scrollDisplayRight(self):
        """ These commands scroll the display without changing the RAM """
        self.write4bits(self.LCD_CURSORSHIFT | self.LCD_DISPLAYMOVE | self.LCD_MOVERIGHT)

    def leftToRight(self):
        """ This is for text that flows Left to Right """
        self.displaymode |= self.LCD_ENTRYLEFT
        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)

    def rightToLeft(self):
        """ This is for text that flows Right to Left """
        self.displaymode &= ~self.LCD_ENTRYLEFT
        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)

    def autoscroll(self):
        """ This will 'right justify' text from the cursor """
        self.displaymode |= self.LCD_ENTRYSHIFTINCREMENT
        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)

    def noAutoscroll(self):
        """ This will 'left justify' text from the cursor """
        self.displaymode &= ~self.LCD_ENTRYSHIFTINCREMENT
        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)

    def write4bits(self, bits, char_mode=False):
        """ Send command to LCD """
        self.delayMicroseconds(1000)  # 1000 microsecond sleep
        bits = bin(bits)[2:].zfill(8)
        self.GPIO.output(self.pin_rs, char_mode)
        for pin in self.pins_db:
            self.GPIO.output(pin, False)
        for i in range(4):
            if bits[i] == "1":
                self.GPIO.output(self.pins_db[::-1][i], True)
        self.pulseEnable()
        for pin in self.pins_db:
            self.GPIO.output(pin, False)
        for i in range(4, 8):
            if bits[i] == "1":
                self.GPIO.output(self.pins_db[::-1][i-4], True)
        self.pulseEnable()

    def delayMicroseconds(self, microseconds):
        seconds = microseconds / float(1000000)  # divide microseconds by 1 million for seconds
        sleep(seconds)

    def pulseEnable(self):
        self.GPIO.output(self.pin_e, False)
        self.delayMicroseconds(1)       # 1 microsecond pause - enable pulse must be > 450ns
        self.GPIO.output(self.pin_e, True)
        self.delayMicroseconds(1)       # 1 microsecond pause - enable pulse must be > 450ns
        self.GPIO.output(self.pin_e, False)
        self.delayMicroseconds(1)       # commands need > 37us to settle

    def message(self, text):
        """ Send string to LCD. Newline wraps to second line"""
        for char in text:
            if char == '\n':
                self.write4bits(0xC0)  # next line
            else:
                self.write4bits(ord(char), True)


if __name__ == '__main__':
    lcd = Adafruit_CharLCD()
    lcd.clear()
    lcd.message("  Adafruit 16x2\n  Standard LCD")
Vielen Dank :roll:
Benutzeravatar
__blackjack__
User
Beiträge: 13938
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@MaWiSy: Allgemeines zum Quelltext:

`os` und `smbus` werden importiert aber nicht verwendet. ``as`` ist beim Importieren dazu da das importiere umzubenennen, `GPIO` wird aber gar nicht umbenannt.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich sind in der Regel auch Sachen die in der Dokumentation von Python und den verwendeten Bibliotheken stehen.

Auch Werte die schon im Code stehen gehören nicht noch mal in den Kommentar. Das wird nämlich blöd für den Leser wenn im Code steht die Pinnummer ist 22, im Kommentar dann aber 25. Ist da jetzt der Code falsch oder der Kommentar?

Andere Beispiele für inhaltlich falsche Kommentare sind ``#set random as a duty cycle value``, ``# display CPU temperature``, und ``# display the time``.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Also keine Variablen auf Modulebene. Auch nicht mit ``global``. Alles was eine Funktion oder Methode ausser Konstanten benötigt wird als Argument(e) übergeben.

`PiCamera`-Objekte sind Kontextmanager, die kann man mit ``with`` verwenden. Momentan wird diese Ressource gar nicht aufgeräumt.

Man verwendet keine nackten ``except:``\s ohne konkrete Ausnahme(n). Das behandelt *alles*, inklusive Ausnahmen mit denen man gar nicht rechnet, und für die das was man da macht sicher keine sinnvolle Ausnahmebehandlung ist. Fehlersuche wird damit extrem erschwert.

`exit()` gibt es eigentlich gar nicht, das sollte man explizit aus dem `sys`-Modul importieren.

Die `destroy()`-Funktion wird nur aufgerufen wenn das Programm mit Strg+C beendet wird. Die sollte aber auch aufgerufen werden wenn das Programm aus anderen Gründen endet, beispielsweise weil in `loop()` eine Ausnahme ausgelöst wird.

Es würde mehr Sinn machen die PWM-Objekte für die RGB-Werte in einer Liste zusammen zu fassen und herum zu reichen, und die Farbwerte auch nicht als einzelne Argumente sondern als Tupel zu übergeben. Besser wäre die RGB-LED mit einer Klasse zu modellieren. Noch besser wäre es nicht das olle GPIO-Modul zu verwenden sondern `gpiozero`.

Um Bedingungen bei ``if`` und ``while`` gehören keine unnötigen Klammern.

Den ``%`` würde ich nicht mehr für Zeichenkettenformatierung verwenden. Es gibt die `format()`-Methode und f-Zeichenkettenliterale.

Anstelle von `time.time()` sollte man zum Zeitmessen `time.monotonic()` verwenden.

Die API von `pulseIn()` ist hässlich und nicht dokumentiert das 0 da ein besonderer Wert ist der Zeitüberschreitung bedeutet. Für so etwas gibt es Ausnahmen. Der Vergleich mit >1 ist ja auch falsch denn zwischen 0 (exlusive) und 1 sollte der ”Alarm” ja auch greifen nehme ich mal an.

``count <= 1`` macht keinen Sinn denn `count` wird mit 1 initialisiert, kann also niemals kleiner sein.

Die `blink()`-Funktion sollte man nicht mehrfach hintereinander aufrufen sondern ein Argument mitgeben das sagt wie oft geblinkt werden soll.

Das `lcd.clear()` wird sowohl am Ende im ``if``- als auch im ``else``-Zweig aufgerufen, kann also *einmal* hinter das Konstrukt geschrieben werden.

Pfade setzt man nicht als Zeichenkettenoperation zusammen sondern mit dem `pathlib`-Modul und da wiederholen sich auch Code und Daten bei Foto und Video.

Was das langsamer werden angeht würde ich das ständige entfernen und wieder hinzufügen von der „event detection“ mal sein lassen. Das GPIO-Modul könnte damit Probleme haben. Das ist wirklich nicht besonders gut.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import sys
import time
from datetime import datetime as DateTime
from pathlib import Path

from Adafruit_LCD1602 import Adafruit_CharLCD
from PCF8574 import PCF8574_GPIO
from picamera import PiCamera
from RPi import GPIO

RECORDING_PATH = Path("/test")

RGB_PINS = [11, 12, 13]
ULTRASONIC_SENSOR_TRIGGER_PIN = 16
ULTRASONIC_SENSOR_ECHO_PIN = 18
BUZZER_PIN = 15
MAX_DISTANCE = 220  # Maximum measuring distance in cm.
MAX_DISTANCE_TIMEOUT = MAX_DISTANCE * 60
BUTTON_PIN = 22

PCF8574_ADDRESS = 0x27  # I2C address of the PCF8574 chip.
PCF8574A_ADDRESS = 0x3F  # I2C address of the PCF8574A chip.
#
# max distance for the ultrasonic sensor until the alert comes.
#
MAXDISTANCE = 10

BLACK = (0, 0, 0)
BLUE = (0, 0, 100)
GREEN = (0, 100, 0)
RED = (100, 0, 0)
WHITE = (100, 100, 100)


def setup():
    GPIO.setmode(GPIO.BOARD)

    GPIO.setup(ULTRASONIC_SENSOR_TRIGGER_PIN, GPIO.OUT)
    GPIO.setup(ULTRASONIC_SENSOR_ECHO_PIN, GPIO.IN)

    GPIO.setup(BUZZER_PIN, GPIO.OUT)

    GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
    GPIO.add_event_detect(BUTTON_PIN, GPIO.FALLING)

    GPIO.setup(RGB_PINS, GPIO.OUT, initial=GPIO.HIGH)
    led_pwms = [GPIO.PWM(pin, 2000) for pin in RGB_PINS]
    for pwm in led_pwms:
        pwm.start(0)
    return led_pwms


def set_color(led_pwms, colour):
    for pwm, component in zip(led_pwms, colour):
        pwm.ChangeDutyCycle(component)


def blink(led_pwms, count):
    for _ in range(count):
        set_color(led_pwms, BLACK)
        time.sleep(0.05)
        set_color(led_pwms, BLUE)
        time.sleep(0.05)


def get_pulse_time(pin, level, timeout):
    for _ in range(2):
        level = not level
        start_time = time.monotonic()
        while GPIO.input(pin) == level:
            if (time.monotonic() - start_time) > timeout * 0.000001:
                #
                # TODO Replace this special value with raising `TimeoutError` or
                #   at least return `None`.
                #
                return 0

    return (time.monotonic() - start_time) * 1_000_000


def get_distance():  # in cm.
    GPIO.output(ULTRASONIC_SENSOR_TRIGGER_PIN, GPIO.HIGH)
    time.sleep(0.00001)  # 10µs
    GPIO.output(ULTRASONIC_SENSOR_TRIGGER_PIN, GPIO.LOW)
    ping_time = get_pulse_time(
        ULTRASONIC_SENSOR_ECHO_PIN, GPIO.HIGH, MAX_DISTANCE_TIMEOUT
    )
    #
    # Calculate distance with sound speed 340m/s.
    #
    return ping_time * 340 / 2 / 10_000


def create_file_path(suffix):
    return RECORDING_PATH / f"{DateTime.now():%Y-%m-%d%H:%M:%S}.{suffix}"


def loop(lcd, led_pwms, camera):
    while True:
        lcd.setCursor(0, 0)
        lcd.message("Hello World")
        distance = get_distance()
        # print(f"The distance is : {distance:.2f} cm")
        set_color(led_pwms, GREEN)
        GPIO.output(BUZZER_PIN, GPIO.LOW)
        if distance < MAXDISTANCE:
            time.sleep(0.1)
            distance = get_distance()
            #
            # Must be >0 because 0 is special value for timeout.  (Ugly!)
            #
            if 0 < distance < MAXDISTANCE:
                set_color(led_pwms, RED)
                # GPIO.output(BUZZER_PIN, GPIO.HIGH)
                lcd.clear()
                lcd.setCursor(0, 0)
                lcd.message("Attention! \n")
                lcd.message("Keep distance")
                time.sleep(0.5)
                lcd.clear()
        #
        # Geht rein bei falling edge, also beim drücken des Knopfes.
        #
        if GPIO.event_detected(BUTTON_PIN):
            set_color(led_pwms, WHITE)

            count = 1
            end_time = time.monotonic() + 1
            while time.monotonic() < end_time:
                if GPIO.event_detected(BUTTON_PIN):
                    count += 1
            
            if count == 1:
                blink(led_pwms, 3)
                print("picture will now be captured..")
                lcd.clear()
                lcd.setCursor(0, 0)
                lcd.message("Picture will \n")
                lcd.message("be captured..")
                camera.capture(str(create_file_path("png")))
                blink(led_pwms, 3)
            else:
                print("start recording...")
                set_color(led_pwms, BLUE)
                lcd.clear()
                lcd.setCursor(0, 0)
                lcd.message("Video is \n")
                lcd.message("recording..")
                camera.resolution = (640, 480)
                camera.start_recording(str(create_file_path("h264")))
                camera.wait_recording(5)
                camera.stop_recording()
            
            lcd.clear()


def cleanup(lcd, led_pwms):
    for pwm in led_pwms:
        pwm.stop()
    lcd.clear()
    GPIO.cleanup()


def main():
    with PiCamera() as camera:
        mcp = None
        for i2c_address in [PCF8574_ADDRESS, PCF8574A_ADDRESS]:
            try:
                mcp = PCF8574_GPIO(i2c_address)
                break
            except SomeConcreteExcpectedException:
                pass

        if mcp is None:
            print("I2C Address Error !")
            sys.exit(1)

        lcd = Adafruit_CharLCD(
            pin_rs=0, pin_e=2, pins_db=[4, 5, 6, 7], GPIO=mcp
        )
        lcd.begin(16, 2)
        mcp.output(3, 1)  # Turn on LCD backlight.

        print("Program is starting...")
        led_pwms = setup()
        try:
            loop(lcd, led_pwms, camera)
        except KeyboardInterrupt:
            pass
        finally:
            cleanup(lcd, led_pwms)


if __name__ == "__main__":
    main()
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
MaWiSy
User
Beiträge: 3
Registriert: Freitag 11. September 2020, 11:53

Vielen Dank für die schnelle Antwort!
Hätte niemals mit so einer ausführlichen Antwort gerechnet :shock:

Werde es am Montag durcharbeiten, testen und Rückmeldung geben.

Schönes Wochenende
MaWiSy
User
Beiträge: 3
Registriert: Freitag 11. September 2020, 11:53

Alles läuft perfekt! Das Skript läuft nun seit 7 Stunden und es ist kein Delay erkennbar.
Vielen, vielen Dank, da wäre ich alleine vermutlich nie drauf gekommen.

Beitrag kann bitte von einem Moderator geschlossen/ gelöscht werden :)
Antworten