Python2 /Python 3; Adafruit 16-Kanal PCA9685 und rpi-rf

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
__deets__
User
Beiträge: 3296
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sonntag 11. März 2018, 21:15

Zu den Abständen: du wartest doch diverse mal zB 0.4 Sekunden. Das ist relativ kurz. Wenn das präzise muss, sollte es ggf anders gemacht werden. Das 0.01 im receive ist eher ein freundlicher Hinweis an den Kernel, mal kurz wen anderes dran zu lassen, aber bitte gleich wieder weiter zu machen. Das wird nie eingehalten, wenn wie gesagt der scheduler schon mit der Auflösung arbeitet - da kommt garantiert immer eine, wenn nicht mehrere zeitscheiben dazwischen, und du wartest implizit immer mehrere 100stel. Schlimm ist das eher nicht, außer du machst Zuviel im anderen Thread. Was wiederum mit dem busywait doof ist... hm.

Schau erstmal das alles prinzipiell klappt, timer-tuning ist Sternchenaufgabe :)
mollyman
User
Beiträge: 27
Registriert: Samstag 24. Februar 2018, 12:10

Montag 12. März 2018, 07:18

__deets__ hat geschrieben:Du benutzt halt eine veraltete Thread api und machst dir das Leben damit schwerer. Aber das hast du ja so gewollt. Die will halt IMMER zwei Argumente. Du gibst ihr nur eines. Es muss ein zweites dahinter, MIT KOMMA GETRENNT, ein leeres Tupel - () - oder nicht-leer wie bei Start, aber da ist das ja dann in Training selbst ignoriert. Also

start_new_thread(receive, ())

Und das def keine Funktionen wären habe ich nicht behauptet.ich rede davon, was DARIN passiert. Da weist du irgendwelchen Variablen werdet zu. Du GLAUBST das würde dauerhaft sein, isses aber nicht. Teste das mal mit einem simplen Skript.

Und dein Skript ist auch ein Modul. Es hat den speziellen Namen __main__, aber die Semantik ist gleich. Insofern gelten meine Ausführungen eben auch da. Global-Deklarationen auf Modulebene sind sinnlos, weil da alles (Modul-)global IST. Unterschieden wird das nur IN Funktionen, denn da gibt es lokale Variablen, und die will man von globalen unterscheiden.
Sie schreibweise ruft die Funktion nicht so auf, dass sie funktioniert

Code: Alles auswählen

def receive(a):
und 
start_new_thread(receive, (99,))
funktioniert

Code: Alles auswählen

def receive(a):
und 
start_new_thread(receive, ())
gibt den Fehler, dass ein Argument fehlt

Code: Alles auswählen

def receive():
und 
start_new_thread(receive, ())
receive wird nicht ausgeführt, es gibt aber auch keinen Fehler
Sirius3
User
Beiträge: 8269
Registriert: Sonntag 21. Oktober 2012, 17:20

Montag 12. März 2018, 07:50

Im letzten Fall machst Du irgendetwas anderes falsch, was Du aber nicht zeigst.
mollyman
User
Beiträge: 27
Registriert: Samstag 24. Februar 2018, 12:10

Montag 12. März 2018, 09:19

Sirius3 hat geschrieben:Im letzten Fall machst Du irgendetwas anderes falsch, was Du aber nicht zeigst.
der komplette Code

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
import Adafruit_PCA9685
import time
import RPi.GPIO as GPIO
import os
import pygame
import _thread
import alsaaudio
import argparse
import signal
import sys
import logging

from rpi_rf import RFDevice
from tkinter import *

#GPIO für LED festlegen
GPIO.setwarnings(False) # Warnungen ausschalten
GPIO.setmode(GPIO.BCM) # Pin Nummern verwenden
GPIO.setup(6, GPIO.OUT) # Pin 31 als Output gruen
GPIO.setup(16, GPIO.OUT) # Pin 36 als Output rot
GPIO.setup(26, GPIO.OUT) # Pin 37 als Output gelb

# Variabelen festlegen
global Kom50, Wiederholungen, SchussZeit, AbzugZeit, Modus, Schieber

# Variabelen Werte zuweisen
SchussZeit = 3 # Dauer der Sichtbarkeit der Scheibe
AbzugZeit = 0 # Zeit um die die Sichtbarkeit der Scheibe reduziert wird
Kom50= 10 # Kommando 50 Reduziert die Zeit für das Laden der Waffe auf 10 Sekunden
Modus = 2 # 1 = Lichtmodus, 2 = Klappenmodus
Wiederholungen = 5 # Duell 5 Wiederholungen ( 1 Schuß pro aufklappen) alle anderen 1

# I2C Board für Servo
pwm = Adafruit_PCA9685.PCA9685(address=0x40) # I2C Board-Adresse
pwm.set_pwm_freq(50) # Festlegung der Frequenz

# Gui Grundlagen definieren
root = Tk() # Fenster erstellen
root.wm_title("Pistol Shoot Trainer") # Fenster Titel
#root.wm_attributes ('-fullscreen', 'true') # Vollbildmodus
root.wm_geometry('480x320+0+0')
root.config(background = "#FFFFFF") # Hintergrundfarbe des Fensters

def exithandler(signal, frame): # Teil des 433 Mhz Prozess Skriptes
    #rfdevice.cleanup()
    sys.exit(0)

def receive():
    print ("receive start")
    logging.basicConfig(level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S',
                    format='%(asctime)-15s - [%(levelname)s] %(module)s: %(message)s', )
 
    parser = argparse.ArgumentParser(description='Receives a decimal code via a 433/315MHz GPIO device')
    parser.add_argument('-g', dest='gpio', type=int, default=27,
                    help="GPIO pin (Default: 27)")
    args = parser.parse_args()
 
    signal.signal(signal.SIGINT, exithandler)
    rfdevice = RFDevice(args.gpio)
    rfdevice.enable_rx()
    timestamp = None
    while True:
        if rfdevice.rx_code_timestamp != timestamp:
            timestamp = rfdevice.rx_code_timestamp
            rec_num = (str(rfdevice.rx_code))
            print (rec_num)
            if rec_num == "16404":
                start()
            return()
        else:
            print ("test")
        time.sleep(0.01)
    #rfdevice.cleanup()

#Variabelen setzen für die Schusszeit und die Anzahl der Wiederholungen pro Magazin
def AchtSekunden():
    SchussZeit = 8
    Wiederholungen = 1 
     
def SechsSekunden():
    SchussZeit = 6
    Wiederholungen = 1 

def VierSekunden():
    SchussZeit = 4
    Wiederholungen = 1 

def ZehnSekunden():
    SchussZeit = 10
    Wiederholungen = 1 
     
def Duell():
    SchussZeit = 3
    Wiederholungen = 5

#Variabelen setzen für die Reduzierung der Schusszeit 
def AbzugOhne():
    AbzugZeit = 0
     
def Abzug01():
    AbzugZeit = 0.1

def Abzug02():
    AbzugZeit = 0.2
    
def Abzug03():
    AbzugZeit = 0.3

#Variabelen setzen für Kommando 50. Reduzierung der Ladezeit auf 10 Sek.
def Kommando50Aus():
    global Kom50
    Kom50= 60
    return()

def Kommando50An():
    global Kom50
    Kom50 = 10
    return()

#Variabelen setzen für Modus ( Licht oder Klapp )
def Modus1():
    global Modus
    Modus= 1

def Modus2():
    global Modus
    Modus= 2

def start():
    print (" start training prozess")
    _thread.start_new_thread(Training,())

def rf_modul():
    print (" start rf modul")
    _thread.start_new_thread(receive, ())
    receive()

def change_vol(l):
    m = alsaaudio.Mixer('PCM')
    int_l = int(l)
    m.setvolume(int_l)
    current_volume = m.getvolume()

def Training(a):
    if Modus == 1:
        os.system("sudo xscreensaver-command -activate")
        GPIO.output(26, True)
        GPIO.output(6, False)
        GPIO.output(16, False)
        pygame.init()
        if Kom50 == 60:
            pygame.mixer.music.load("/home/pi/Documents/shoot/WaffeLaden.wav")#Ansage "Laden"
        elif Kom50 == 10:
            pygame.mixer.music.load("/home/pi/Documents/shoot/50.wav")#Ansage "Laden"
        pygame.mixer.music.play()
        time.sleep(Kom50)
        pygame.mixer.music.load("/home/pi/Documents/shoot/Achtung.wav")#Ansage "Achtung"
        pygame.mixer.music.play()
        time.sleep(1)
        for g in range(Wiederholungen):
            GPIO.output(26, False)
            GPIO.output(16, False)
            GPIO.output(6, True)
            time.sleep(7)
            GPIO.output(16, True)
            GPIO.output(6, False)
            time.sleep(SchussZeit)
        GPIO.output(16, False)
        GPIO.output(6, True)
        for g in range(4):
            GPIO.output(6, False)
            time.sleep(0.4)
            GPIO.output(6, True)
            time.sleep(0.4)
        GPIO.output(6, True)
        GPIO.output(26, True)
        GPIO.output(16, True)
        os.system("sudo xscreensaver-command -deactivate")
    elif Modus == 2:       
        os.system("sudo xscreensaver-command -activate")
        GPIO.output(16, False)
        GPIO.output(6, False)       
        GPIO.output(26, True)
        pygame.init()
        if Kom50 == 60:
            pygame.mixer.music.load("/home/pi/Documents/shoot/WaffeLaden.wav")#Ansage "Laden"
        elif Kom50 == 10:
            pygame.mixer.music.load("/home/pi/Documents/shoot/50.wav")#Ansage "Laden"
        pygame.mixer.music.play()
        pwm.set_pwm(0, 0, 130)
        pwm.set_pwm(1, 0, 130)
        pwm.set_pwm(2, 0, 130)
        pwm.set_pwm(3, 0, 130)
        pwm.set_pwm(4, 0, 130)            
        time.sleep(Kom50)
        GPIO.output(26, False)
        pygame.mixer.music.load("/home/pi/Documents/shoot/Achtung.wav")#Ansage "Achtung"
        pygame.mixer.music.play()
        time.sleep(1)
        for g in range(Wiederholungen):
            pwm.set_pwm(0, 0, 300)
            pwm.set_pwm(1, 0, 300)
            pwm.set_pwm(2, 0, 300)
            pwm.set_pwm(3, 0, 300)
            pwm.set_pwm(4, 0, 300)
            time.sleep(7)
            pwm.set_pwm(0, 0, 130)
            pwm.set_pwm(1, 0, 130)
            pwm.set_pwm(2, 0, 130)
            pwm.set_pwm(3, 0, 130)
            pwm.set_pwm(4, 0, 130)
            time.sleep(SchussZeit)
        pwm.set_pwm(0, 0, 300)
        time.sleep(1)
        pwm.set_pwm(0, 0, 130)
        GPIO.output(26, True)
        GPIO.output(16, True)
        GPIO.output(6, True)
        os.system("sudo xscreensaver-command -deactivate")
    return
   
def herunterfahren():
    GPIO.cleanup()
    os.system("sudo shutdown -h now")
     
def GUI():
    rightLabel1 = Label(buttonFrame, text="Pistol Shoot Trainer", font = "Verdana 27 bold", background="#58FA58")
    rightLabel1.grid(row=0, column=0, padx=1, pady=1, columnspan=6)
    
    rightLabel4 = Label(buttonFrame, text="Schießzeit", font = "Verdana 10 bold", background="#58FA58")
    rightLabel4.grid(row=1, column=0, padx=0, pady=3)
    
    RB11 = Radiobutton(buttonFrame, indicatoron=0, text="Duell", height=1, width=5, font = "Verdana 12 bold", variable=SchussZeit, value=3, command=Duell)
    RB11.grid(row=1, column=1, padx=5, pady=8)
    RB11.select()
 
    RB12 = Radiobutton(buttonFrame, indicatoron=0,  text="8 Sek", height=1, width=5, font = "Verdana 12 bold" , variable=SchussZeit, value=8, command=AchtSekunden)
    RB12.grid(row=1, column=3, padx=5, pady=8)

    RB13 = Radiobutton(buttonFrame, indicatoron=0,  text="6 Sek", height=1, width=5,font = "Verdana 12 bold", variable=SchussZeit, value=6, command=SechsSekunden)
    RB13.grid(row=1, column=4, padx=5, pady=8)

    RB14 = Radiobutton(buttonFrame, indicatoron=0,  text="4 Sek", height=1, width=5,font = "Verdana 12 bold" , variable=SchussZeit, value=4, command=VierSekunden)
    RB14.grid(row=1, column=5, padx=5, pady=8)

    RB15 = Radiobutton(buttonFrame, indicatoron=0,  text="10 Sek", height=1, width=6,font = "Verdana 12 bold" , variable=SchussZeit, value=10, command=ZehnSekunden)
    RB15.grid(row=1, column=2, padx=5, pady=8)

    rightLabel5 = Label(buttonFrame, text="Zeitabzug", font = "Verdana 10 bold", background="#58FA58")
    rightLabel5.grid(row=2, column=0, padx=0, pady=3)
 
    RB21 = Radiobutton(buttonFrame, indicatoron=0,  text="Ohne", height=1, width=5, font = "Verdana 12 bold" , variable=AbzugZeit, value=1, command=AbzugOhne)
    RB21.grid(row=2, column=1, pady=8, padx=5)
    RB21.select()
    
    RB22 = Radiobutton(buttonFrame, indicatoron=0,  text="0,1", height=1, width=5, font = "Verdana 12 bold" , variable=AbzugZeit, value=2, command=Abzug01)
    RB22.grid(row=2, column=2, pady=8, padx=5)

    RB23 = Radiobutton(buttonFrame, indicatoron=0,  text="0,2", height=1, width=5, font = "Verdana 12 bold", variable=AbzugZeit, value=3, command=Abzug02)
    RB23.grid(row=2, column=3, pady=8, padx=5) 

    RB24 = Radiobutton(buttonFrame, indicatoron=0,  text="0.3", height=1, width=5, font = "Verdana 12 bold" , variable=AbzugZeit, value=4, command=Abzug03)
    RB24.grid(row=2, column=4, pady=8, padx=5)
                  
    rightLabel6 = Label(buttonFrame, text="Kommando \n50", font = "Verdana 10 bold", background="#58FA58")
    rightLabel6.grid(row=3, column=0, padx=0, pady=3)

    RB31 = Radiobutton(buttonFrame, indicatoron=0,  text="Ohne", height=1, width=7, font = "Verdana 15 bold" , variable=Kom50, value=1, command=Kommando50Aus)
    RB31.grid(row=3, column=1, pady=8,columnspan=2)
    
    RB32 = Radiobutton(buttonFrame, indicatoron=0,  text="Mit", height=1, width=7, font = "Verdana 15 bold" , variable=Kom50, value=2, command=Kommando50An)
    RB32.grid(row=3, column=3, pady=8,columnspan=2)
    RB32.select()
    
    rightLabel6 = Label(buttonFrame, text="Modus", font = "Verdana 10 bold", background="#58FA58")
    rightLabel6.grid(row=4, column=0, padx=0, pady=3)
    
    RB41 = Radiobutton(buttonFrame, indicatoron=0,  text="Licht", height=1, width=7, font = "Verdana 15 bold" , variable=Modus, value=1, command=Modus1)
    RB41.grid(row=4, column=1, pady=8,columnspan=2)
       
    RB42 = Radiobutton(buttonFrame, indicatoron=0,  text="Dreh", height=1, width=7, font = "Verdana 15 bold" , variable=Modus, value=2, command=Modus2)
    RB42.grid(row=4, column=3, pady=8,columnspan=2)
    RB42.select()

    BT42 = Button(buttonFrame, text="Beenden",  height=1, width=10, font = "Verdana 10 bold", command=herunterfahren)
    BT42.grid(row=5, column=4, padx=5, pady=5,columnspan=2)
    
    rightLabel7 = Label(buttonFrame, text="Lautstärke", font = "Verdana 10 bold", background="#58FA58")
    rightLabel7.grid(row=5, column=0, padx=0, pady=3)
    
    Schieber = Scale(buttonFrame, from_ = 0, to = 100, length = 200, orient =HORIZONTAL,tickinterval = 20,resolution =  5, command=change_vol) # tickinterval = 1,
    Schieber.grid(row=5, column=1, padx=5, pady=5,columnspan=3)
    Schieber.set(10)

    #root.after (0, receive)
    root.mainloop() # GUI wird upgedated. Danach keine Elemente setzen

buttonFrame = Frame(root, background="#58FA58")
buttonFrame.grid(row=0, column=0, padx=5, pady=5)        
GPIO.output(26, True)
GPIO.output(6, True)
GPIO.output(16, True)
GUI()
rf_modul()
    
mollyman
User
Beiträge: 27
Registriert: Samstag 24. Februar 2018, 12:10

Montag 12. März 2018, 09:27

__deets__ hat geschrieben:Zu den Abständen: du wartest doch diverse mal zB 0.4 Sekunden. Das ist relativ kurz. Wenn das präzise muss, sollte es ggf anders gemacht werden. Das 0.01 im receive ist eher ein freundlicher Hinweis an den Kernel, mal kurz wen anderes dran zu lassen, aber bitte gleich wieder weiter zu machen. Das wird nie eingehalten, wenn wie gesagt der scheduler schon mit der Auflösung arbeitet - da kommt garantiert immer eine, wenn nicht mehrere zeitscheiben dazwischen, und du wartest implizit immer mehrere 100stel. Schlimm ist das eher nicht, außer du machst Zuviel im anderen Thread. Was wiederum mit dem busywait doof ist... hm.

Schau erstmal das alles prinzipiell klappt, timer-tuning ist Sternchenaufgabe :)
die 0,4 SSekunden sollen nur ein Blinken der LED erzeugen, die anzeigen, dass der Zyklus durch ist. Ob das 0,4 ,02 oder 0,8 sind ist dabei voellig legal
Sirius3
User
Beiträge: 8269
Registriert: Sonntag 21. Oktober 2012, 17:20

Montag 12. März 2018, 10:04

@mollyman: dann schau Dir nochmal genau an, was Du da machst:

Code: Alles auswählen

def rf_modul():
    print (" start rf modul")
    _thread.start_new_thread(receive, ())
    receive()
mollyman
User
Beiträge: 27
Registriert: Samstag 24. Februar 2018, 12:10

Montag 12. März 2018, 10:34

Upps * sich schäm*

Verstanden

Aber bevor das doppelte receive kommt, kommt ein print, das wird aber auch nicht geschrieben.

Ich korrigiere es und probiere es erneut
mollyman
User
Beiträge: 27
Registriert: Samstag 24. Februar 2018, 12:10

Montag 12. März 2018, 11:30

Habe den 2. receive Aufruf entfernt. es hat aber nix verändert.

Mir ist aber aufgefallen, dass wenn cih das Fenster schließe dann der receive startet.

Code: Alles auswählen

GUI()
rf_modul()
die Reihenfolge ist wohl schuld daran.
ein Tausche der Reihenfolge bringt einen neuen Fehler, der aber darauf hindeutet, dass dass das receive arbeitet:

Code: Alles auswählen

signal.signal(signal.SIGINT, exithandler)
ValueError: Signal only works in main thread.
Daraus entnehme ich dass ich das receive Script nicht in einen Thread auslagern kann.
Die GUI kann ich aber auch nicht auslagern.
Jetzt bin ich ziemlich ratlos
Sirius3
User
Beiträge: 8269
Registriert: Sonntag 21. Oktober 2012, 17:20

Montag 12. März 2018, 11:49

@mollyman: wie schon ganz am Anfang geschrieben: Signale sind nicht die richtige Methode um "Aufzuräumen".
mollyman
User
Beiträge: 27
Registriert: Samstag 24. Februar 2018, 12:10

Montag 12. März 2018, 11:59

Mist.

Ich habe keine Ahnung vom parsen .
Leider verstehe ich anhand der jeweiligen Doku weder wie das receive script funktioniert noch was Signal eigentlich wirklich darin macht.

Tja, dann muss ich mir was einfallen lassen.
Dann geht es nicht mit der 433 MHz Lösung.

Danke für eure Hilfe.
__deets__
User
Beiträge: 3296
Registriert: Mittwoch 14. Oktober 2015, 14:29

Montag 12. März 2018, 13:00

Das ist Unfug. Natuerlich geht das. Nur eben nicht mit dem aufeinanderkloppen von Skripten, die du nicht verstehst, und die Dinge machen wie zB Kommandozeilen Argumente parsen oder Fehlerbehandlung durchzufuehren auf (in DEINEM FALL) ungeeignete Art und Weise.

Du brauchst weder eine flexible Angabe des verwandten GPIOs (womit der Argument-Parser rausfaellt), noch willst du dein Skript per Ctrl-C abbrechen und danach schoen aufraeumen. Womit die Signal-Behandlung entfaellt.

Alles was du brauchst ist das RFDevice. Vergiss *ALLES* andere. Es ist ein beliebter Anfaengerfehler, alles auf einmal in einem riesen Klumpatsch zum laufen zu bringen.

Starte nur zum ausprobieren mit einem NEUEN Skript, in dem ausser den Zeilen

Code: Alles auswählen

from rpi_rf import RFDevice

def receive():
      pass
      
if __name__ == "__main__":
      receive()
nix steht. Und arbeite dich dahin, das du das zum funktionieren bekommst. Naechster Schritt: receive im Hintegrund-Thread starten. OHNE das "verbotene" Modul _thread, sondern mit dem dafuer gedachten Modul threading.

Dann hast du eine Grundlage, das in deine bestehende GUI einzubauen.
mollyman
User
Beiträge: 27
Registriert: Samstag 24. Februar 2018, 12:10

Dienstag 13. März 2018, 13:21

Code: Alles auswählen

def receive():
    rfdevice = RFDevice(27)
    rfdevice.enable_rx()
    timestamp = None
    while True:
        if rfdevice.rx_code_timestamp != timestamp:
            timestamp = rfdevice.rx_code_timestamp
            rec_num = (str(rfdevice.rx_code))
            if rec_num == "16404":
                time.sleep(2)
                start()
        time.sleep(0.01)
Das funktioniert jetzt.

das auch:

Code: Alles auswählen

def rf_modul():
    tr = Thread(target = receive)
    tr.start()
__deets__
User
Beiträge: 3296
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dienstag 13. März 2018, 14:01

Na sieht doch gut aus :!: Ich wuerde das time.sleep(2) noch entfernen, und stattdessen in start verschieben, weil das mE eine Entscheidung ist, die da getroffen werden sollte, und mit dem eigentlichen Empfang nichts zu tun hat. Ist aber Kuer.

Dann bau das doch mal in dein Tool ein und schau, ob's besser wird.
mollyman
User
Beiträge: 27
Registriert: Samstag 24. Februar 2018, 12:10

Dienstag 13. März 2018, 16:17

das receive funktioniert im Tool,auch das threading.
Das time Sleep ist noch ein überbleibsel des testens und ist jetzt auch noch rausgeflogen.

Wenn jetzt noch die letzten Einstellungen gespechert werden, wäre das toll.
Ich habe mal mit dem configparser gespielt, aber der scheint nicht so richtig zu funktionieren bei mir.
Seit dem ich die vielen Globals rausgenommen habe scheint die Logik auch nicht mehr zu funktionieren.
__deets__
User
Beiträge: 3296
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dienstag 13. März 2018, 17:32

Ich habe ja auch nicht gesagt du sollst die rausnehmen. Ich habe gesagt das du teilweise unsinnige hast (auf modul-Ebene), und das dir in vielen Funktionen welche *fehlen*.
Antworten