Firmata mit TSL2561

Begonnen von eitschdie, 09 September 2015, 15:54:56

Vorheriges Thema - Nächstes Thema

thymjan

Hätt' ich auch drauf kommen können, bin da neulich bei dem usleep auch drauf reingefallen.
Habe die Schrittweite auf 1/10 s in usleep gestellt, damit das Log noch einigermaßen erträglich bleibt.

2015.11.22 15:50:51 5: I2C_TSL2561_Poll: start
2015.11.22 15:50:51 5: I2C_TSL2561_GetLuminosity: calc state 0 acqui state 0
2015.11.22 15:50:51 5: I2C_TSL2561_GetLuminosity: starting new measurement
2015.11.22 15:50:51 5: I2C_TSL2561_Enable: start
2015.11.22 15:50:51 4: Lichtsensor: I2C_TSL2561_i2cread: INIT i2cOpInProgress=3000000
2015.11.22 15:50:51 5: FRM: Kommunikation mit I2C ...
2015.11.22 15:50:51 5: FRM: auf I2C lesen ...
2015.11.22 15:50:51 5: FRM:>f07639080a010100f7
2015.11.22 15:50:51 5: FRM: Adresse: 57|Register: 138|1 byte(s)
2015.11.22 15:50:51 4: Lichtsensor: I2C_TSL2561_i2cread: START i2cOpInProgress=3000000
2015.11.22 15:50:51 4: Lichtsensor: I2C_TSL2561_i2cread: LOOP i2cOpInProgress=2900000
[...]
2015.11.22 15:50:54 4: Lichtsensor: I2C_TSL2561_i2cread: LOOP i2cOpInProgress=200000
2015.11.22 15:50:54 4: Lichtsensor: I2C_TSL2561_i2cread: LOOP i2cOpInProgress=100000
2015.11.22 15:50:54 4: Lichtsensor: I2C_TSL2561_i2cread: LOOP i2cOpInProgress=0
2015.11.22 15:50:54 4: Lichtsensor: I2C_TSL2561_i2cread: END i2cOpInProgress=0
2015.11.22 15:50:54 5: I2C_TSL2561_Enable: end
2015.11.22 15:50:54 5: I2C_TSL2561_Disable: start
2015.11.22 15:50:54 4: Lichtsensor: I2C_TSL2561_i2cwrite: INIT i2cOpInProgress=3000000
2015.11.22 15:50:54 5: FRM: Kommunikation mit I2C ...
2015.11.22 15:50:54 5: FRM: auf I2C schreiben ...
2015.11.22 15:50:54 5: FRM: Adresse: 57|Register: 128|Wert: 0
2015.11.22 15:50:54 5: FRM:>f076390000010000f7
2015.11.22 15:50:54 4: Lichtsensor: I2C_TSL2561_i2cwrite: START i2cOpInProgress=3000000
2015.11.22 15:50:54 4: Lichtsensor: I2C_TSL2561_i2cwrite: LOOP i2cOpInProgress=2900000
[...]
2015.11.22 15:50:57 4: Lichtsensor: I2C_TSL2561_i2cwrite: LOOP i2cOpInProgress=100000
2015.11.22 15:50:57 4: Lichtsensor: I2C_TSL2561_i2cwrite: LOOP i2cOpInProgress=0
2015.11.22 15:50:57 4: Lichtsensor: I2C_TSL2561_i2cwrite: END i2cOpInProgress=0
2015.11.22 15:50:57 5: I2C_TSL2561_Disable: end
2015.11.22 15:50:57 5: I2C_TSL2561_GetLuminosity: calc state 3 acqui state 0
2015.11.22 15:50:57 5: I2C_TSL2561_GetLuminosity: error, aborting
2015.11.22 15:50:57 1: PERL WARNING: Use of uninitialized value in multiplication (*) at ./FHEM/51_I2C_TSL2561.pm line 585.
2015.11.22 15:50:57 1: PERL WARNING: Use of uninitialized value in multiplication (*) at ./FHEM/51_I2C_TSL2561.pm line 586.
2015.11.22 15:50:57 5: I2C_TSL2561_Poll: 60 s
2015.11.22 15:50:58 5: FRM:<f07739000a015000f7
2015.11.22 15:50:58 5: --------------------------
2015.11.22 15:50:58 5: FRM: onI2CMessage address: '57', register: '138' data: [80]
2015.11.22 15:50:58 5: FRM: UPDATE Adresse: 57|Register: 138|Daten: 80
2015.11.22 15:50:58 5: FRM: UPDATE FRM_1_SENDSTAT => "Ok"
2015.11.22 15:50:58 5: Lichtsensor RX register 10, 1 byte: 80
2015.11.22 15:50:58 5: I2C_TSL2561_I2CRcvID: sensorId TSL2561 Package T/FN/CL Rev. 0
2015.11.22 15:50:58 4: Lichtsensor: I2C_TSL2561_I2CRec: END

jensb

Insgesamt verwendet das Modul 2 Zustandsautomaten: GetLuminosity als äußere Ablaufsteuerung, um FHEM nicht während der Integrationszeit des Sensors zu blockieren und GetData für das Lesen der Messdaten. Diese Abläufe kann man natürlich so umbauen, dass die Entscheidungen alle in I2CRec getroffen werden. Sollte das Blockieren in i2cread/write nicht funktionieren, bleibt möglicherweise auch nichts anderes übrig.
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

thymjan

Ich bin mit meiner rudimentären Version nur über diesen Weg (I2CRec) weitergekommen.

jensb

Das neue Logging zeigt, dass jetzt zwar tatsächlich Zeit vergeht, aber das IODev wie befürchtet in dieser Zeit nicht arbeiten kann, weil FHEM blockiert ist. Deine Anmerkung mit dem Poll ist damit der richtige Hinweis gewesen.

Unter Firmata muss der I2C-Zugriff also komplett asynchron laufen. Damit müssten alle Methoden, die mehr als einen I2C-Zugriff machen, so aufgeteilt werden, dass die Folgeabläufe über I2CRec angestoßen werden, wie du bei deinen Tests bereits festgestellt hast. Werde mir das mal ansehen, aber das wird etwas dauern.
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

jensb

Man könnte vielleicht folgendes machen: Betroffen von mehrfachen I2C-Zugriffen sind direkt die Methoden Enable, SetIntegrationTime, SetGain und indirekt vor allem GetData. Da Enable der Henkel für alle andere Abläufe ist, sollte man hier anfangen. Leider ist Enable auch eine der komplexeren Methoden, da sie insgesamt 4 I2C-Zugriffe enthält (2x I2C_TSL2561_i2cread, 1x I2C_TSL2561_i2cwrite und 1x I2C_TSL2561_SetGain) und macht damit den Einstieg nicht leicht. Trotzdem könnte man so das Modul schrittweise umbauen, ohne die Abwärtskompatibilität zu verlieren.

Da meine Zeit überwiegend auf das Wochenende begrenzt ist, wäre es von Vorteil, wenn wir das zusammen machen könnten. Ich würde mit Enable einen Anfang machen, den du testen könntest. Wenn es klappt, könntest du die anderen Methoden analog umsetzten. Aber wie du mit deinem Versuch bereits gemerkt hast, wird das nicht "mal eben" erledigt sein.
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

jensb

Anbei ein erster Ansatz. Habe Enable entsprechend aufgeteilt, lokale Variablen in den Hash verlagert und lasse die Methode I2CRec den Zustand von Enable über nextOperation weiter schalten. Das Ganze ist noch sehr rudimentär, aber der Weg sollte sich erkennen lassen. Schau dir insbesondere auch die Änderungen am Ende von I2CRec an.

Die nächste Herausforderung ist es, die Methode SetGain genauso umzubauen. Allerdings muss man dabei zusätzlich berücksichtigen, dass SetGain von verschiedenen Methoden aufgerufen wird, so dass man für den Rücksprung ein 2stufiges "nextOperation" braucht. Vielleicht macht es Sinn, dazu nextOperation in den Hash aufzunehmen und daraus ein Array zu machen, um einen Call-Stack nachzubilden.

Für mich ist heute erst mal Schluss. Wenn du möchtest, kannst du dir gerne SetGain vornehmen.
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

thymjan

Hallo Jens,

wird das alles so nicht wahnsinnig kompliziert?

Muss gestehen, dass dieser Weg meine Vorstellungskraft momentan etwas überfordert. Kann das grob nachvollziehen, was Du mit nextOperation gemacht hast. Aber wenn wir jetzt mehr als eine "Subroutine" haben, die von I2CRec aufgerufen werden, wie wird das anhand von nextOperation dann gemacht? Da müssen ja bestimmte nexOperation-Sets für jede Subroutine definiert werden, um unterscheiden zu können.

Also für SetGain z.B. Enable_WriteTimingRegister und Enable_ReadTimingRegister. Oder verstehe ich etwas falsch?

Für mich schreit das Modul nach Vereinfachung. Zum Beispiel die Funktionen SetGain und SetIntegrationTime machen doch eigentlich das selbe, oder? Könnte man da nicht eine draus machen?

Und ein Messwert auslesen mache ich immer mit
- Falls Gain oder IntegrationTime verändert wurde:
  TimingRegister schreiben (write) (Gain, IntegrationTime) und Auslesen (read) zur Kontrolle
- Messung starten mit Sensor einschalten (write), überprüfen ob Sensor eingeschaltet ist (read),
  nach IntegrationTime sind Messwerte broadbband und ir da
- mit zwei Lesezugriffen (read, read) Messwerte auslesen
- Sensor ausschalten (write, read)
Das könnte doch wie eine Worklist von *einer* Funktion abgearbeitet werden?

Dann Messwerte skalieren und Lux berechnen.
Falls Sensor gesättigt oder unterbelichtet, nach einem festgelegten Algorithmus Gain und IntegrationTime optimieren und ggf. sofort neue Messung starten.
Ansonsten warten bis zum nächsten Poll.

Sorry, wenn ich etwas im Mecker-Modus bin...

Wenn die von Dir genannten sub's mit nextOperation angepasst wurden, muss dann an GetLuminosity nichts geändert werden?

Was ist der Vorteil für den Benutzer wenn das intern mit so viel Sicherheitsüberprüfungen abläuft? Können da wirklich so viele Dinge schief laufen?

Mir fehlen da einfach die Grundlagen. Bin an die Sache eher mit "keep it simple" im Hinterkopf drangegangen.
Das Du mich jetzt nicht falsch verstehst, ich möchte hier nicht abspringen.
Aber falls Du etwas zu meiner Motivation beitragen kannst, bin ich Dir dankbar!  ;)

Gruß,
Stefan

jensb

Hallo Stefan,

im Kern hast du Recht, das schreit nach Vereinfachung, sonst versteht den Code bald niemand mehr.

Allerdings sollte man dabei die vorhandenen Features mitnehmen. Außerdem steckt in den Details des jetztigen Code viel Know-How und Testaufwand. Vor allem GetLuminosity sollte dabei nicht unter die Räder kommen.

Man könnte in Poll nach IODev-Init mit einem i2cwrite PowerOn starten und in I2CRec ein modifiziertes GetLuminosity integrieren, das immer mit einem PowerOff abschließt. Behält man i2cread und i2cwrite bei, könnte das sogar mit HiPi noch funktionieren. Zumindest im IODev-Modus dürfte FHEM durch die asynchrone  Verarbeitung noch etwas mehr entblockt werden.

Ich würde dazu wieder mit meiner Version vom 17.11. anfangen, und ähnlich wie von dir vorgeschlagen zuerst die Funktionalität von GetData umsetzten und dann die von GetLuminosity hinzufügen.

LG, Jens
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

thymjan

#68
Guten Morgen Jens,

wenn Du mir noch ein weiteres Brainstorming erlaubst:
Vorschlag:
Die Messungs-Worklist wird in Set gestartet. Nach Starten entweder "unsauber" mit einem usleep(integrationTime) oder entblockend mit einem weiteren Timer der den zweiten Teil – das Auslesen und Ausschalten des Sensors – nach der integrationTime aufruft.
Danach wird in Rec, getriggert mit der Meldung "Sensor aus", eine Low-Level Auswertung der Messwerte gestartet. Ist der Sensor unterbelichtet oder gesättigt, nach einem festgelegten Algorithmus gain und integrationTime angepasst und die Messung mit einem Aufruf von Set respektive update wiederholt.

Liegen dann valide Messwerte vor, dann diese skalieren.
Habe ich skalierte Messwerte vorliegen, wird Lux berechnet.

Was denkst Du?

Gruß,
Stefan

jensb

Hallo Stefan,

ich möchte deine Begeistung nicht abwürgen, aber die äußeren Anwendungsfunktionen des Moduls müssen unverändert bleiben. Dazu zählen u.a. das periodische Pollen von Messwerten, die diversen Auto-Funktionen, die IO-Fehlerbehandlung und die alternativen Berechnungsfunktionen.

Damit das Rad nicht nochmal erfunden werden muss, sollte dazu so viel wie möglich vorhandener Code verwendet werden. Die Änderungen sollten sich also vor allem auf die asynchrone Folgeverarbeitung konzentrieren und so übersichtlich wie möglich ausfallen, obwohl dies gerade bei asynchroner Verarbeitung nicht ganz so leicht ist.

Ich glaube nicht, dass wir mehr als einen Timer für den Grundtakt brauchen und der kann weiterhin Poll aufrufen. In Poll wird der 1. I2C-Zugriff gestartet und in I2CRec kommt hinter die vorhandene Empfangsverarbeitung eine neue Statemachine,  die sich an GetLuminosity und GetData orientiert, den Fehlertest aus Poll enthält und im Endeffekt ein paar States mehr hat, um die Hilffunktionen wie Enable, SetGain, etc. zu integrieren. Im Gutfall läuft alles I2C-Receive-Ereignisgesteuert und ein usleep wird dazu nicht benötigt.

Wenn du deinen Ansatz umsetzten würdest, hättest du zwar schnell die ersten Ergebnisse, aber es wäre viel Aufwand, alle  aktuellen Features funktionsidentisch nachträglich zu integrieren. Wahrscheinlich wird der Code dadurch am Ende nicht kürzer und die Entwicklung dauert dadurch länger.

LG, Jens
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

jensb

Hallo,

anbei eine Testversion für das Modul I2C_TSL2561 mit folgenden Änderungen:

  • Die neue Version erkennt, ob die I2C-Schnittstelle Blockend oder Nicht-Blockend arbeitet und stellt sich darauf ein.
  • Fix für das state-Reading, das nach Setzen des Attributes disable=1 dauerhaft auf "Disabled" blieb
  • Dokumentation erweitert
Die neuer Version ist getestet mit RPII2C (Blockend) und FRM mit FirmataEthernet (Nicht-Blockend). Weitere Tests vor allem auch mit anderen I2C-Schnittstellen sind wünschenswert. Gibt es vielleicht jemanden, der HiPi im Einsatz hat?

Viel Spaß,
Jens
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

thymjan

#71
Gute Arbeit, Jens!

Funktioniert jetzt ohne Fehlermeldungen mit meinem Arduino Nano mit ConfigurableFirmata über Ethernet auch.
Danke.

Habe eben noch etwas mit gain und integrationTime rum gespielt. Dabei ist mir aufgefallen, dass die Einstellungen (Readings/Internals) nur verzögert angezeigt werden.
Zunächst habe ich autoGain und autoIntegrationTime auf Null gesetzt.
Dann die Empfindlichkeit ganz runter gedreht (Gain 1, integrationTime 13ms). Bis die eingestellten Werte angezeigt werden, muss ich attr-ändern 2-3x ausführen bis die Werte in den readings/internals angekommen sind.

Folgendes stimmt doch?
Internals:
tsl2561Gain
"0" bedeutet gain=1
"16" bedeutet gain=16

tsl2561IntegrationTime
"0" bedeutet integrationTime=13ms bzw. 0.0137s
"1" bedeutet integrationTime=101ms bzw. 0.101s
"2" bedeutet integrationTime=402ms bzw. 0.402s


Bei mir stand i2c-config bisher auf 1. Damit hat's bei mir funktioniert. Das Umstellen auf 30000 ergibt für mich keine sichtbare Veränderung.
Kann es sein, dass dies nur ein Schalter ist?

Grüße,
Stefan

jensb

Hallo Stefan,

freut mich zu höhren, dass es auch bei dir grundsätzlich funktioniert.

Die z.T. scheinbar hakelige Übernahme von gain und integrationTime ist mir auch schon aufgefallen. Werde versuchen herauszufinden, was dahinter steckt. Deine Wertezuordnung müsste stimmen, schau dir dazu die Konstanten im Kopfteil des Moduls an.

Den richtigen Wert für i2c-config beim TSL2561 wollte ich noch austesten. Dieser Parameter ist Teil der Firmata-Protokollspezifikation. Um z.B. den BMP180 mit 3fachem Oversampling nutzen zu können, braucht man knapp 30 Millisekunden Verzögerung zwischen Write und Read (Datenblatt) also 30000 für i2c-config. Wenn ich bei mir z.B. nur 20000 eintrage, klappt es nicht mehr. Du verwendest allerding ConfigureableFirmata und ich StandardFirmataEthernet - vielleicht gibt es da kleine Unterschiede in der Implementierung.

Grüße,
Jens
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

jensb

Hallo Stefan,

dein Hinweis bzgl. der Timing-Attribute war hilfreich. Das Timing wurde bisher sowohl beim Ändern der Attribute als auch bei der automatischen Regelung von Gain und Integration Time direkt zum TSL2561 geschickt. Vor allem das Setzen als Attribut konnte erst im 2. Anlauf gelingen, da der neue Werte für ein Attribut innerhalb der AttrFn über AttrVal() noch nicht zur Verfügung steht. Habe das Schreiben des Timings nun an einer festen Stelle in die State-Machine integriert.

Auch kann ich bestätigen, dass der TSL2561 keinen bestimmten Mindestwert für i2c-config im FRM-Modul benötigt.

Viel Spaß mit der überarbeiten Version,
Jens
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

thymjan

#74
Hallo Jens,

das Übernehmen der Attribute geht jetzt schneller. Sobald die nächste Messung ausgelöst wird, tauchen sie in den Readings auf.
Habe mich gerade gefragt, ob es hier sinnvoll wäre, direkt durch einen Attribut-Wechsel eine Messung auszulösen?

Weiter ist mir aufgefallen – stelle die Messwerte in einem Diagramm dar – dass es im IR-Kanal zu unregelmäßigen Fehlmessungen kommt, bzw. dieser gelegentlich Null-Werte liefert.
Weis nicht so recht wie ich den Fehler präzisieren kann.
Werd mal verbose hochdrehen.

Gruß,
Stefan

Im Anhang das Diagramm von gestern. Sensorempfindlichkeit ist maximal eingestellt. Sensor befindet sich im Keller. Die roten Spikes sind die Nullmessungen des IR-Kanals. Werte werden logarithmisch dargestellt.
In dem blau-roten Block am Morgen habe ich eine LED im 2-Minuten-Takt blinken lassen. Abtastfrequenz des Sensors war bis auf die letzte Stunde am Abend auf 1 Minute eingestellt.