apsched basierte Relaissteuerung über ein MCP23017

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
duri.rau
User
Beiträge: 12
Registriert: Sonntag 1. Mai 2016, 18:52

Hallo zusammen,

ich habe ein MCP23017 mit der A-Seite an ein 8er Relais angeschlossen. Die B-Seite soll später Sensordaten einlesen.
In der Vergangenheit habe ich die Outputs über eine Funktion "Schalten(Pin, Zustand)" geschaltet.
1. das Byte einlesen
2. bei Zustand == 1 --> gelesene Byte "oder" 2^Stelle
bei Zustand == 0 --> gelesene Byte - 2^Stelle
3. Byte auf MCP schreiben

Leider war die Funktion in Verbindung mit dem backgroundscheduler sehr unzuverlässig. Nach einer gewissen Zeit wurden alle GPIO auf high gestellt (Zustand --> 0x00). Ich vermute das, wenn die Methode "Schalten" von zwei Jobs gleichzeitig aufgerufen wurde, die Überlappung zu Fehlern beim schrieben führt.

Nun bin ich über die Bibliothek MCP230XX von Adafruit gestolpert.
https://github.com/adafruit/Adafruit_Py ... CP230xx.py
Da ich sie bedauerlicherweise nicht richtig zum laufen bekomme, würde ich mich über ein paar Denkanstöße freuen.

Code: Alles auswählen

import Adafruit_GPIO as GPIO
import Adafruit_GPIO.I2C as I2C
import time

mcp = MCP23017() #Objekt erstellen, Übergabeparameter wie 1 oder 0x20 für die Adresse funktionieren nicht...
for i in range (7):
	mcp.setup(i,GPIO.OUT)
while True:
	for i in range (7):
		mcp.output(i,0)
		time.sleep(0.5)
		mcp.output(i,1)
Meiner Meinung nach müssten die Relais jetzt von 1-7 einzeln hoch blinken.
Leider gehen alle LEDs ganz kurz an und bleiben dann aus.

Kann jemand den Fehler finden?

Beste Grüße
Zuletzt geändert von Anonymous am Donnerstag 25. August 2016, 09:50, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

#Objekt erstellen, Übergabeparameter wie 1 oder 0x20 für die Adresse funktionieren nicht...
Was heißt funktioniert nicht?

Ich bin mir nicht sicher aber ich denke ohne einen Plan was jetzt wie wo angeschlossen ist ist dein Problem schwer zu fassen.
duri.rau
User
Beiträge: 12
Registriert: Sonntag 1. Mai 2016, 18:52

Oh sorry, der Einwand macht Sinn.
Der PI ist über die I2C GPIO Ports (1, 3, 5, 9) an den MCP23017 angeschlossen.
Der MCP23017 ist über über die A-Seite (GPA0 - GPA7) an eine 8er Relais-Leiste angeschlossen (3,3V und GND liegen auch am PI).

Wenn ich das Objekt mit Übergabeparametern wie

Code: Alles auswählen

mcp = MCP23017(adress=0x00, busnum=1) #habe ein Raspberry PI 3 B
zeigt er mir an, dass er drei Übergabeparameter erwartet. Wenn ich das Objekt hingegen ohne Übergabeparameter

Code: Alles auswählen

mcp = MCP23017() #ohne Übergabeparameter
initialisiere, macht er dies auch. Ich kann auch die Methoden anstandslos verwenden.
Nur schalten alle Methoden (output(self, pin, value), def output_pins(self, pins), etc.) alle GPA des MCP23017 für ein Sekundenbruchteil auf HIGH und dann durchgehend auf LOW.
Entweder nutze ich die Methode falsch oder die Methode funktioniert nur mit den MCP23017 von Adafruit aber ein MCP23017 bleibt ja wohl ein MCP23017, egal woher ich ihn beziehe....oder?
BlackJack

@duri.rau: Aus Deiner ersten Beschreibung mit dem was Du ”früher” gemacht hast, bin ich nicht so ganz Schlau geworden. Was hast Du bei 2. bei Zustand 0 gemacht? Falls dort das Bit im Byte ausgeschaltet werden soll, dann fehlt da eine bitweise Negation des 2^Stelle-Wertes und eine „und“-Verknüpfung mit dem Bytewert. Sonst wird das Bit ja nicht ausgeschaltet und behält einfach den alten Wert. Was erklärt warum immer mehr Bits angeschaltet werden, wenn das ausschalten nicht tatsächlich passiert.

Bezüglich der Übergabeparameter hast Du wohl das `address`-Argument vergessen. Dafür macht das `adress`-Argument keinen Sinn, denn das gibt es nicht (die `__init__()` nimmt aber beliebige Schlüsselwortargumente).
duri.rau
User
Beiträge: 12
Registriert: Sonntag 1. Mai 2016, 18:52

@BlackJack

Code: Alles auswählen

import time, smbus, threading
bus = smbus.SMBus(1) # Rev 2 Pi

bus.write_byte_data(0x20, 0x00, 0x00)
bus.write_byte_data(0x20, 0x01, 0xFF)

def Schalten (Zustand, Stelle):
    aktuell = bus.read_byte_data(0x20, 0x12)
    Potenz=[1,2,4,8,16,32,64,128]
    if Zustand==1:
        neu=aktuell|Potenz[Stelle]			#eigentlich ^(XOR) aber das hat warum auch immer nicht funktioniert
    else:
        neu=aktuell-Potenz[Stelle]			#eigentlich logisches UND (&) hat aber auch nicht funktuioniert
    bus.write_byte_data(0x20, 0x12, neu)
Diese Methode läuft eine Weile super mit APscheduler aber irgendwann schaltet sich das Byte des MCP23017 auf 0x00 (0b0).

Deshalb will ich die library von Adafruit nutzen. :/

Okay das ist echt total dumm mit dem Attributnamen. Ich habe es jetzt nochmal getestet und diesmal nicht in der gleichen *.py Datei.

Code: Alles auswählen


from datetime import datetime
from apscheduler.schedulers.background import BackgroundScheduler
from Adafruit_I2C import Adafruit_I2C
from Adafruit_GPIO.MCP230xx import MCP230xxBase
import smbus

mcp =  MCP230xxBase(adress=0x20, i2c=1)      #MCP23017
Als Fehler gibt er aus

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/pi/Desktop/MaxGarden.py", line 8, in <module>
    mcp =  MCP230xxBase(adress=0x20, i2c=1)      #MCP23017
TypeError: __init__() takes at least 2 arguments (2 given)
Ich dachte erst ich rufe einfach von der anderen Datei den Konstruktur von MCP230xxBase über

Code: Alles auswählen

MCP23017(MCP230xxBase)
oder

Code: Alles auswählen

MCP230xxBase.MCP23017(MCP230xxBase)
aus. Das funktioniert aber alles nicht. Ich verstehe einfach nicht wie ich ein Objekt mit dem MCP23017 anlegen kann. Das kann doch echt nicht so schwer sein?
BlackJack

@duri.rau: Deine Kommentare bezüglich OR, XOR, -, und AND beim setzen/löschen von Bits zeigen ziemlich deutlich das Du nicht verstanden hast wie das funktioniert. Beim setzen kann XOR so nicht funktionieren, da ist das OR schon richtig, und das - kann nicht funktionieren sondern da braucht man, wie ich schon geschrieben habe, die negierte Maske und ein AND. Das - funktioniert nur wenn das Bit tatsächlich gesetzt ist, macht aber Murks falls nicht.

Und noch mal: `adress` ist falsch! Richtig wäre `address`!
duri.rau
User
Beiträge: 12
Registriert: Sonntag 1. Mai 2016, 18:52

Danke für den Hinweis. ich bin tatsächlich nicht so gut in Informatik (Studiere Wirtschaftsingenieurwesen. ;))

Ich habe den offensichtlichen Fehler angepasst, leider stimmt immer noch etwas nicht.

Code: Alles auswählen

from datetime import datetime
from apscheduler.schedulers.background import BackgroundScheduler
from Adafruit_I2C import Adafruit_I2C
from Adafruit_GPIO.MCP230xx import MCP23017				#ich habe die ganze Zeit das falsche Package importiert
import smbus, time

#Initialisierung mit der Adresse <-- mit doppel d
mcp =  MCP23017(address=0x20)       #MCP23017		
#komplette A-Seite --> Output		
mcp.setup(0,valuae=0)
mcp.setup(1,value=0)
mcp.setup(2,value=0)
mcp.setup(3,value=0)
mcp.setup(4,value=0)
mcp.setup(5,value=0)
mcp.setup(6,value=0)
mcp.setup(7,value=0)


#Magnetventil zum gegebenem Intervall schalten
if __name__ == '__main__':										
    scheduler = BackgroundScheduler()
    scheduler.add_job(lambda mcp.output(0,0), 'cron',minute='*/2', second='0', id='eins')
    scheduler.add_job(lambda mcp.output(0,1), 'cron', minute='*/2', second='1', id='zwei')
    scheduler.add_job(lambda mcp.output(1,0), 'cron', hour='20',minute='04', second='0', id='drei')
    scheduler.add_job(lambda mcp.output(1,1), 'cron', hour='8',minute='00', second='0', id='vier')
    scheduler.add_job(lambda mcp.output(2,0), 'cron', hour='6', second='0', id='fünf')
    scheduler.add_job(lambda mcp.output(2,1), 'cron', hour='0', second='0', id='sechs)
    scheduler.add_job(lambda mcp.output(3,0), 'cron', minute='1,10,20,30,40,50', second='0', id='sieben')
    scheduler.add_job(lambda mcp.output(3,1), 'cron', minute='4,13,23,33,43,53', second='0', id='acht')
    scheduler.add_job(lambda mcp.output(4,0), 'cron', minute='30', second='0', id='neun')
    scheduler.add_job(lambda mcp.output(4,1), 'cron', minute='00', second='0', id='zehn')
    scheduler.print_jobs()
    scheduler.start()
#keine Ahnung ob das notwendig ist
    try:
        while True:
            time.sleep(1)           
    except (KeyboardInterrupt, SystemExit):
        scheduler.shutdown()
Obwohl ich die Deklaration angepasst habe (adress --> address), wird augenblicklich das Byte der A-Seite auf 0x00 geschaltet.
Sprich, alle Magnetventile sind an. Wenn ich sie über die Konsole ausschalte, werden bei dem nächste Event wieder alles angeschaltet.

Kann es sein das im Package etwas nicht stimmt?
Zuletzt geändert von Anonymous am Samstag 27. August 2016, 10:27, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

Den `address`-Wert beim Aufruf von `MCP23017` kannst Du weg lassen, denn 0x20 ist der Defaultwert: https://github.com/adafruit/Adafruit_Py ... xx.py#L152

Der Code müsste in Zeile 10 eigentlich in eine Ausnahme laufen, denn die `setup()`-Methode erwartet zwei Argumente und kennt kein `valuae`-Argument: https://github.com/adafruit/Adafruit_Py ... 0xx.py#L54

Ich würde dort beim Aufruf auch die Konstanten `IN` und `OUT` aus dem `Adafruit_GPIO`-Modul verwenden. Ich wüsste jetzt beispielsweise nicht aus dem Kopf welche davon den Wert 0 hat.

Ab Zeile 29 sieht man an der Syntaxhervorhebung deutlich das dort etwas nicht stimmt und dass das nicht der Quelltext ist, den Du tatsächlich hast laufen lassen.

Zum Kommentar in Zeile 35: Wenn Du nicht willst, dass der `scheduler` im Hintergrund läuft, dann nimm doch nicht den der im Hintergrund läuft. `apscheduler` bietet ja auch einen Blockierenden.
duri.rau
User
Beiträge: 12
Registriert: Sonntag 1. Mai 2016, 18:52

Hi, danke für die schnelle Antwort.

Du hast recht. Ich habe die Beschriftungen etwas funktionaler gestaltet.

Ich habe jetzt
  • address entfernt
  • den BlockingScheduler verwendet
  • versucht die IO per mcp.setup(0,'GIPO.OUT') --> ging nicht (er erwartet GPIO.OUT oder GPIO.IN was witzig ist weil ich genau das als String übergeben habe
  • versucht die IO per mcp.setup(0,'OUT') --> ging nicht (erwartet GPIO.OUT....)
  • versucht die IO per mcp.setup(0,GIPO.OUT) --> ging nicht (Er denkt GPIO wäre ein Objekt)
  • versucht die IO per mcp.setup(0,OUT) --> ging nicht (Er denkt OUT wäre ein Objekt
  • versucht die IO per mcp.setup(0,0) --> ging (Ich weiß das 0 Output ist
Kann es sein das ich ein Objekt der Klasse GPIO zusätzlich initialisieren muss? Ich wüsste dann aber auch nicht wie mir das bei der Initialisierung weiterhelfen soll bzw. ob ich dann ein Objekt "mcp" und ein Objekt "gpio" habe...

Und wieder schaltet er komplett alle Magnetventile auf ein (Byte = 0x00)
BlackJack

@duri.rau: Eine Zeichenkette 'GPIO.OUT' ist natürlich nicht das selbe wie GPIO.OUT. Wo denkst Du denn das der Name GPIO oder OUT her kommt? Das muss ja irgendwo definiert sein in Deinem Programm. Derjenige der die Methode programmiert hat, verwendet wie gesagt die Konstanten `IN` und `OUT` aus dem `Adafruit_GPIO`-Modul. Also sollte man das importieren (eventuell mit ``as`` an den Namen `GPIO` binden) und die Konstanten über das Modul ansprechen, oder direkt die Konstanten aus dem Modul importieren. Sonst kennt Python die natürlich nicht auf magische Weise.

Es gibt eine Klasse `GPIO`? Wo käme die denn her? Und was repräsentiert die und welche Methoden hat die?
duri.rau
User
Beiträge: 12
Registriert: Sonntag 1. Mai 2016, 18:52

Hi,

ich meine die "BaseGPIO" von der die "MCP230xxBase" als Übergabeparamter hat, welche wiederum der Übergabeparameter der "MCP23017" ist. Eine schlichte GPIO ist in der library natürlich nicht enthalten.
https://github.com/adafruit/Adafruit_Py ... IO/GPIO.py

Auch wenn die Initialisierung nicht über die definierten Werte der Klasse "MCP230xxBase" ist, müsste das Programm doch dennoch laufen?
Ich kürze im Prinzip doch nur die Übersetzung OUT --> 0 // IN --> 1 ab und schreibe direkt eine 0 rein...
Bzw. dürfte das Programm nicht alle Pins einfach auf HIGH schalten, jedesmal wenn die Methode "output()" des "mcp" Objekts aufgerufen wird?
BlackJack

Das was Du da Übergabeparameter nennst, sind keine. Das sind die Klassen von denen geerbt wird. Und ein extra `BaseGPIO`-Exemplar brauchst Du nicht, denn ein `MCP23017`-Exemplar *ist* ein `BaseGPIO`-Exemplar. Über den Zwischenschritt, das es ein `MCP230xxBase`-Exemplar ist, weil es davon erbt.

Das Programm müsste auch mit den 0en statt der definierten Konstanten laufen, wenn denn sonst alles stimmt. Also Beispielsweise die Verkabelung, die Annahmen darüber ob die angeschlossenen Geräte high- oder low-aktiv sind. Denn wann ein Relais geschaltet ist und wann Du einen Pin als High bezeichnest, scheint mir nicht immer deckungsgleich zu sein.

Was bei `apscheduler` noch ein Problem sein könnte, währe Nebenläufigkeit. Also ich würde das erst einmal ohne `apscheduler` mit ganz normalem linearen Code zum laufen bringen, also das man alles wie gewünscht schalten kann. Bevor man sich eine weitere potentielle Fehlerquelle an Bord holt.
duri.rau
User
Beiträge: 12
Registriert: Sonntag 1. Mai 2016, 18:52

Da hast du eigentlich recht.
Ich werde mal wie du sagst mit linearem Code testen.

Vielen Dank für deine Tipps. :)
Antworten