I2C Klasse aus Unter-Funktion aufrufen

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Lutz59
User
Beiträge: 4
Registriert: Mittwoch 1. Juli 2020, 10:32

Hallo liebe Gemeinde

ich habe folgendes Problem.
Ich möchte einen Portexpander IC PCA 9555 als zusätzliche Ausgänge benutzen.
Dazu habe ich eine Klasse geladen, die aus dem i2c-test.py Program von Frank Buss stammt.
Ich möchte nur Outputs benutzen.

Wenn ich das Programm starte werden alle Ports so gesetzt wie ich es möchte und es funktioniert.
In der Unterfunktion def execute meines Programms benutze ich genau die gleichen Variable ioExpander.setOutput(v)
um das Ergebnis an den I2C Klasse zu senden.
Und dann kommt es zum TimeOut mit dem I2C. (Fehlermeldung unten im Text)

irgendwie kann ich die Variable nicht an die Klasse übergeben, obwohl diese laut class PCA9555 () def set.output global sein müsste.
Was mache ich da falsch?

Danke für Eure Antworten !

Lutz

Code: Alles auswählen

#####################################################################
# Programm zur Bedienung der Outputs des PCA9555 Expander
#####################################################################
import os
import sys
import subprocess
import io
import smbus
import time
import socket
#I2C INITIALISIERUNG PCA9555
class PCA9555():
	# open Linux device /dev/ic2-0
	i2c = smbus.SMBus(1)

	# construct a new object with the I2C address of the PCA9555
	
	def __init__(self, address):
		self.address = address
  
	# write a 16 bit value to a register pair
	# write low byte of value to register reg,
	# and high byte of value to register reg+1
	
	def writeRegisterPair(self, reg, value):
		low = value & 0xff
		high = (value >> 8) & 0xff
		self.i2c.write_byte_data(self.address, reg, low)
		self.i2c.write_byte_data(self.address, reg + 1, high)

	# read a 16 bit value from a register pair
	def readRegisterPair(self, reg):
		low = self.i2c.read_byte_data(self.address, reg)
		high = self.i2c.read_byte_data(self.address, reg + 1)
		return low | (high << 8)

	# set IO ports to input, if the corresponding direction bit is 1,
	# otherwise set it to output
	def setInputDirection(self, direction):
	    self.writeRegisterPair(6, direction)

	# set the IO port outputs
	
	def setOutput(self, value):
		self.writeRegisterPair(2, value)
		
	# read the IO port inputs
	
	def getInput(self):
		return self.readRegisterPair(0)
 



# create a new PCA9555 object
global ioExpander
ioExpander = PCA9555(0x20)                #Adresse des PCA9555-Chip A0/A1/A2 = Ground
# test output value
v = 3
# direction of the LED animation
directionLeft = False

# set input for IO pin 15, rest output
ioExpander.setInputDirection(1 << 16);

#Initalzustand nach Start Raspberry
#Alle Ports Output und 0 Zustand
p0_str = "port0"
globals()[p0_str]= ('00000000')
#print(port0)
p1_str = "port1"
globals()[p1_str]= ('00000000')
#print(port0)

[color=#0000FF]#ini I2C Ausgang RaspiStart
port0v=('00000000')                        #Grundzustand nach Einschalten PORT0
port1v=('00000000')                        #Grundzustand nach Einschalten PORT0
port_bin=port1v+port0v                      #Beide 8Bit Vorgaben zum 16 Bit Wort zusammensetzen
PCA_data=int(port_bin, 2)                 #16 Bit-Wort in Dezimal-Wert wandeln 
v=PCA_data                                #Übergabe an Variable v der class PCA9555
ioExpander.setOutput(v);                #Ausgänge PCA9555 setzen[/color][/color]
#---------------------------------------------------------------------------------------------


def execute(datagram):
    global restart
    
    port0x=(globals()[p0_str])
    port1x=(globals()[p1_str])
    if  m2==1:                                               #Merkervariable = 1 Befehl mit nur Punkt im Befehlswort
        instring1= (instring +(adr))                         #Abfragestring generieren
        ad=int(adr)                                          #adr in intWert wandeln 
    if  m1==1:                                               #Merkervariable = 1 Befehl mit Gleichheitszeichen im Befehlswort
        instring1 = (instring +(z_nach_gleich))              #Variable z_nach_gleich entspricht Inhalt Befehl nach Gleichheitszeichen
        
#**************EIN Schalten von I2C Outputs PORT-0-PCA9555***************************
   if instring1 == ("SET:PORT0="+(z_nach_gleich)):
        port0A=(z_nach_gleich)
        if port1x != ('00000000'):                      #Abfrage wenn Werte in Port1 ungleich der global InitPort1 Variable
          port_bin=(port1x)+(port0A)                    #Beide 8Bit Vorgaben zum 16 Bit Wort zusammensetzen
          (globals()[p0_str])=(port0A)                  #Ändern der globalen Variable die nach dem Einschalten als Grundzustand gesetzt wurde
          
        else:
          port_bin=('00000000')+(port0A )               #Beide 8Bit Vorgaben zum 16 Bit Wort zusammensetzen
          (globals()[p0_str])=(port0A)
        
 
        PCA_data=int(port_bin, 2)                       #16 Bit-Wort in Dezimal-Wert wandeln 
        v=PCA_data                                     #Übergabe an Variable v der class PCA9555
        
       [color=#4000BF] ioExpander.setOutput(v); [/color]                     #Ausgänge PCA9555 setzen
        datagram = ("VAL_PORT.0="+(port_bin)+chr(13))
        return datagram
#**************EIN Schalten von I2C Outputs PORT-1-PCA9555***************************
    if instring1 == ("SET:PORT1="+(z_nach_gleich)):
        port1A=(z_nach_gleich)
        
        if port0x != ('00000000'):
           port_bin=(port1A)+(port0x)
           (globals()[p1_str])=(port1A)
           
        else:
           port_bin=(port1A)+('00000000')
           (globals()[p1_str])=(port1A)
        
               
        PCA_data=int(port_bin, 2)
        v=PCA_data                                     #Übergabe an Variable v der class PCA9555
      [color=#0000BF]  ioExpander.setOutput(v));[/color]                  #Ausgänge PCA9555 setzen
        datagram = ("VAL_PORT.1="+(port_bin)+chr(13))
        return datagram
'-----------------------------------------------------------------------------------------------------------
def get_datagram(get_nachricht):
    global adr
    global z_nach_gleich
    global m1
    global m2
    #print(get_nachricht)
    zeichen=str(get_nachricht)
    zl= len(zeichen)                                           #Zeichenlänge ermitteln  
    z_arr=[]                                                   #Zeichen Array
    for i in range(2,(zl-1)):                                  #Die ersten beide(b')aus socket StringtoByte und letztes Zeichen(')abtrennen 
        z_arr.append(zeichen[i])                               #Zeichen auf Zeichen Array schreiben
    z_arr="".join(z_arr)                                       #Zeichen zusammenführen
    zl= len(z_arr)                                             #Stringlänge ermitteln
    #print(zl)
    #print(z_arr)
    pkt= z_arr.find(".")                                       #Position Punkt im Befehl ermitteln
    #print(pkt)
    gleich_zeich = z_arr.find("=")                             #Position Gleich Zeichnen im Befehl ermitteln
    #print(gleich_zeich)
    
                                   #++++++++IN Datenprüfung++++++++++++++
    if (gleich_zeich > 0):                        # Prüfung ob Gleich Zeichen im String
        m1=1
        m2=0
        zl_nach_gleich = zl - (gleich_zeich+1)    # Anzahl Zeichen nach Gleich Zeichen ermitteln
        z_nach_gleich = z_arr[-(zl_nach_gleich):] # Zeichen nach Gleich Zeichen auf Variable schreiben
        befehl = z_arr[0:(gleich_zeich+1)]        # Zeichen vor Gleich Zeichen auf Variable schreiben 
        if (zl_nach_gleich > 0):
            #print("Gleich="+(befehl))
            return befehl
        else:
            error="Fehler"
            return error
    
        
    if (pkt > 0 ):                 #Prüfung ob Punkt im String
        m1=0
        m2=1 
        adr_zl = zl - (pkt+1)      # Anzahl Zeichen nach Punkt ermitteln
        adr = z_arr[-(adr_zl):]    # Zeichen nach Punkt auf Variable schreiben
        befehl = z_arr[0:(pkt+1)]  # Zeichen vor Punkt auf Variable schreiben
        #print('ADR=',adr_zl)
        #print('Befehl=',befehl)
        if (adr_zl > 0):           # Prüfung auf Zeichen nach Punkt
            #print("Punkt="+(befehl))####!!!!!!!!!!!!!!!
            return befehl
        else:
            error="Fehler"
            return error
    else:
        error="Fehler"
        return error
########################### FUNKTIONEN ENDE
		
############################Hauptschleife###############################

while True:
        
 try:                # Versuch 
  daten, addr = s.recvfrom(1024)        # Versuch Puffer lesen
 except:                                # falls keine Daten
  t = 0                                 # keine Daten, Zustand merken
  get_nachricht = ""                    # Daten-Puffer leeren
 if (t == 1):                           # wenn Daten verfuegbar
  get_nachricht = daten                 # Daten kopieren
  quell_ip = str(addr[0])               # Quell-IP selektieren
  quell_port = str(addr[1])             # Quell-Port selektieren
  #print ("[%s]:[%i] %s" % (addr[0], addr[1], daten)) #IP, Port und Daten ausgeben 
  #print (get_nachricht)
  
  instring= get_datagram(get_nachricht)#Rückgabewert auf Variable schreiben
  

  if (instring == "Fehler"):             #Wenn Fehler im Eingangstelegramm 
      nachricht="Fehler"                 #Wort "error" an Absender senden
      s.sendto(nachricht.encode('ascii'),(addr[0],addr[1])) # Nachricht an Absender zuruecksenden
  else:                                 #sonst
          
      nachricht=(execute("datagram"))   #Rückgabewert auf Variable schreiben 
      print (nachricht)
      s.sendto(nachricht.encode('ascii'),(addr[0],addr[1])) # Nachricht an Absender zuruecksenden
 t = 1
s.close() 
Die Fehlermeldung sieht wie folgt aus:

Traceback (most recent call last):

File "PCA9555_Test.py", line 41, in writeRegisterPair
self.i2c.write_byte_data(self.address, reg, low)
TimeoutError: [Errno 110] Connection timed out
pi@raspberrypi:~ $
Benutzeravatar
__blackjack__
User
Beiträge: 14047
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Lutz59: ``global`` dürfte ein Stichpunkt sein warum hier noch niemand geantwortet hat. Das gesamte Programm ist extrem unübersichtlich. Vergiss bitte das es ``global`` gibt. Das hat in einem sauberen Programm nichts zu suchen. Und so Verrenkungen wie folgendes erst recht nicht:

Code: Alles auswählen

p0_str = "port0"
globals()[p0_str]= ('00000000')
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Eingerückt wird in Python mit vier Leerzeichen pro Ebene. Keine Tabs, nicht zwei Leerzeichen, und schon mal gar nicht nur ein einziges Leerzeichen.

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. Da sind extrem überflüssige Kommentare im Code.

Ausgerichtete Kommentare an Zeilenenden sind auch viel zu arbeitsintensiv bei der Pflege weil man bei Änderungen am Code auch immer die Ausrichtung der Kommentare anpassen muss.

Es sind ein Haufen überflüssiger Klammern im Code. Und ein paar Semikolons. Und unsinnige Zuweisungen an zusätzliche Namen die nicht wirklich benötigt werden. An mindestens einer Stelle steht im Kommentar dazu etwas von kopieren — da wird nichts kopiert, da wird einfach nur ein zusätzlicher Name an ein und den selben Wert gebunden.

Viele Namen sind kryptisch, Abkürzungen, nummeriert, oder Mischungen aus diesen unschönen Eigenschaften.

Es gibt Code der eindeutig durch kopieren, einfügen, und leicht verändern entstanden ist.

Man operiert nicht auf der Zeichenkettendarstellung von Objekten mit Zeichenkettenoperationen herum. Wenn man eine Zeichenkette aus einem `bytes`-Objekt erstellen will, denn dekodiert man die Bytes und schneided nicht von der Zeichenkettendarstellung das "b'" und "'" an Anfang und Ende weg. Selbst wenn man das machen *würde* dann einfach per slicing und nicht so furchtbar umständlich mit einer ``for i in range(…)``-Schleife die einzelne Zeichen in eine Liste kopiert und die dann wieder zu einer Zeichenkette zusammensetzt.

Diese Liste sollte man nicht Array nennen, denn Listen sind keine Arrays. Python kennt auch Arrays, deshalb ist das verwirrend.

Man verwendet keine ”nackten” ``ecxept``\s ohne konkrete Ausnahmen. Damit werden *alle* Ausnahmen behandelt, inklusive solchen mit denen man gar nicht rechnet. Es gibt nur wenige Möglichkeiten wirklich auf *alle* Ausnahmen sinnvoll zu reagieren.

Es werden sieben Module importiert — und nur eines davon wird tatsächlich verwendet.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Lutz59
User
Beiträge: 4
Registriert: Mittwoch 1. Juli 2020, 10:32

Danke für deine Antwort

Das mit den importierten Modulen ist der Tatsache geschuldet, dass es nur ein Teil des Programms ist.
Ich habe das bewusst gekürzt da es zur Beantwortung meine Frage ?
bestimmt nicht dienlich ist, sämtliche Prozeduren für ADC's , MedianFilter, SoundAusgabe und IO Ports mit aufzuführen.

Es geht wirklich nur darum, warum aus der def execute die Übergabe der ioExpander.setOutput(v) an die class PCA9555( ) nicht erfolgt,
denn am Anfang steht selbige und die macht die geforderten Grundeinstellungen. Also übergibt ordnungsgemäß den Wert an die Klasse.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das hat erstmal so nix mit Python zu tun. Denn diese Antwort kommt vom Kernel. Wenn man danach googelt, findet man so diverse Dinge, zB:

https://raspberrypi.stackexchange.com/q ... onnections

Ich wuerde also zuvorderst die Verkabelung pruefen, und dann ggf. mit einem Oszilloskop schauen, was da auf dem I2C-Bus passiert, wenn du diesen Fehler bekommst.
Lutz59
User
Beiträge: 4
Registriert: Mittwoch 1. Juli 2020, 10:32

Hallo _deets_

Verkabelung habe ich geprüft.
Nach i2cdetect -y 1 findet er das modul auch an der Adresse 0x20
in der Ini am Anfang führt er auch den Befehl ioExpander.setOutput(v) ordnungsgemäß aus.
Nur nicht aus der def execute. Da kommt es zum Problem.
hab das auch mit der os.system(i2cset -y 1 0x07 0x00) Output Port1 PCA9555 gefolgt von Befehl os.system(i2cset -y 1 0x03 0xff) Output Port1 für alle LED's =AN und gemacht. In der Ini funktioniert es und aus der def execute kommt write failed zurück.

Gruss
Lutz
Lutz59
User
Beiträge: 4
Registriert: Mittwoch 1. Juli 2020, 10:32

Ich habe den Fehler gefunden.
Die PXA9555 funktioniert.
Hatte die beiden Leitungen SDA und SCL in einer Unterroutine fälschlich auf Output gesetzt.

Ist alles erledigt
Antworten