Pullup-Widerstände über Firmata setzen

Begonnen von Christian., 23 Juli 2013, 06:57:09

Vorheriges Thema - Nächstes Thema

Christian.

Hallo zusammen,

ich habe vor kurzem einige S0-Stromzähler über ein Arduino-Board und das Firmata-Protokoll an FHEM angebunden. Das funktioniert gut bis auf eine Kleinigkeit: um ein sauberes Signal zu erhalten, möchte ich gern die im Arduino integrierten Pullup-Widerstände nutzen. Nach allem, was ich gelesen habe, ist das aber noch nicht im Firmata-Protokoll abgebildet. Ich habe nun überlegt, ob und wie man das ändern kann.

In DigitalInputFirmata.cpp wird der Pullup-Widerstand bei der Initialisierung explizit deaktiviert. Man könnte ihn durch digitalWrite(pin, TRUE); aktivieren, das müsste aber von einem Firmata-Client aus möglich sein. Das Modul FRM_IN hat diese Funktion derzeit nicht, auch in DigitalInputFirmata.cpp habe ich dafür keine passende Funktion gefunden.

Man kann alternativ auch die in der Arduino-Bibliothek definierte Konstante INPUT_PULLUP als pin mode setzen, was den digitalWrite()-Aufruf überflüssig macht. Dafür sehe ich folgende Möglichkeiten.

Möglichkeit A
INPUT_PULLUP wird als neuer pin mode behandelt (wie im Arduino modelliert)
  • Änderungen:
    • FHEM:
    Device/Firmata/Constants.pm: Erweiterung von $BASE um Konstante INPUT_PULLUP mit Wert 9
  • FHEM: Device/Firmata/Constants.pm: Erweiterung von $COMMANDS um V_2_06, als Kopie von V_2_05 mit pin mode-Konstante INPUT_PULLUP mit Wert 0x09
  • FHEM: Device/Firmata/Platform.pm: Änderung von pin_mode, Abschnitt PIN_MODE_HANDLER:
    ( $mode == PIN_INPUT or $mode == PIN_OUTPUT or $mode == PIN_INPUT_PULLUP) and do {
  • FHEM: 20_FRM_IN.pm: Erweiterung um Variable für pullup mit Werten true und false, Default false
  • FHEM: 20_FRM_IN.pm: Erweiterung von FRM_IN_Init um Abfrage von pullup und passenden Aufruf von FRM_Init_Pin_Client
  • Arduino: Firmata.h: Neue pin mode-Konstante INPUT_PULLUP mit Wert 0x09 (Achtung: obwohl INPUT_PULLUP in Arduino.h mit anderem Wert definiert ist)
  • Arduino: Firmata.h: Änderung von TOTAL_PIN_MODES von 10 auf 11
  • Arduino: utility/DigitalInputFirmata.cpp: Erweiterung von handlePinMode um Zweig else if (mode == INPUT_PULLUP)[/list]
  • Problem: Konflikt zwischen Arduino.h und Firmata.h; Firmata.h definiert INPUT_PULLUP = 9, Arduino.h definiert aber bereits INPUT_PULLUP = 2; Firmata.h definiert außerdem schon ANALOG = 2[/list]Möglichkeit B
    • wie A, aber Wert der Konstante in
    Firmata.h = 2, Verschieben aller folgenden Konstanten um 1 (also z.B. ANALOG = 3)
  • Der Konflikt von Möglichkeit A ist damit gelöst.
  • Problem: Inkompatibel zu Firmata V_2_05[/list]Möglichkeit C
    • wie A, aber Name der Konstante in
    Firmata.h nicht INPUT_PULLUP, sondern FIRMATA_INPUT_PULLUP (oder ähnlich)
  • Das ist voll kompatibel zum bestehenden Verhalten, Firmata verwendet aber nicht die bestehende Arduino-Konstante.[/list]Möglichkeit D
    INPUT_PULLUP wird nicht als neuer pin mode behandelt; pin mode bleibt INPUT, die Option PULLUP wird unabhängig davon behandelt
  • Änderungen:
    • FHEM:
    20_FRM_IN.pm: Erweiterung um Variable für pullup mit Werten true und false, Default false
  • FHEM: Übertragung von pullup - wie?
  • Arduino: Empfang von pullup und Übergabe an DigitalInputFirmata - wie?
  • Arduino: DigitalInputFirmata: Aufruf von digitalWrite(PIN_TO_DIGITAL(pin), HIGH);[/list]
  • Problem: Implementierung von D.2) und D.3) ungeklärt[/list]Mein Favorit ist Möglichkeit C, da sie kompatibel zu vorigen Versionen ist.

    Da alle Möglichkeiten eine Veränderung des bestehenden Firmata-Protokolls bedeuten, bitte ich alle Firmata-Experten um eine Einschätzung. Habe ich etwas übersehen? Gibt es weitere Alternativen? Wie könnte ich eine solche Erweiterung angehen?

    Komme ich durch die geschickte Kombination von FRM_IN und FRM_OUT vielleicht auch ohne eine Änderung des Firmata-Protokolls aus?

    Schöne Grüße,
    Christian
    Raspberry Pi 3 mit FHEM; Arduino Nano mit ConfigurableFirmata (S0-Stromzähler); nanoCUL (MAX!); SIGNALduino (RXB6, 433 MHz); eBus; RS485 & D0 (SolarView); DVB-T (Thermo-/Hygrometer); Z-Wave; ZigBee

    ntruchsess

    Möglichkeit D sollte sich mit geringem Aufwand umsetzen lassen.

    Die Pull-ups aktiviert man über das Firmataprotokoll so wie in den älteren Arduino-versionen (in denen die Konstante INPUT_PULLUP noch nicht definiert war):

    den Pin als Input konfigurieren, danach auf High setzen.

    Das ist im Firmata-protocoll auch explizit vorgesehen: die DigitalOutputFirmata schreibt die Pins auch, wenn der jeweilige Pin als Input konfiguriert ist.

    Ich habe jetzt nicht geprüft, ob die Perl-firmata das auch erlaubt (da sind Prüfungen auf den gesetzten PinMode drin, aber das ließe sich ganz einfach und ohne Bruch der Kompatibilität anpassen). Und natürlich muss dann noch ein passendes Attribut 'attr pullup [high/low]' in das FRM_IN-modul.

    Gruß,

    Norbert
    while (!asleep()) {sheep++};

    Christian.

    Hallo Norbert,

    danke für den Hinweis, das klingt ja nach ausgesprochen wenig Arbeit. Ich werde es ausprobieren.
    Raspberry Pi 3 mit FHEM; Arduino Nano mit ConfigurableFirmata (S0-Stromzähler); nanoCUL (MAX!); SIGNALduino (RXB6, 433 MHz); eBus; RS485 & D0 (SolarView); DVB-T (Thermo-/Hygrometer); Z-Wave; ZigBee

    ntruchsess

    Ich habe grade dem FRM_IN das Attribut 'internal-pullup' hinzugefügt. Mögliche Werte: 'on' oder 'off' (default 'off').

    Findest Du hier zum testen:

    https://github.com/ntruchsess/fhem-mirror/blob/dev/fhem/FHEM/20_FRM_IN.pm
    https://github.com/ntruchsess/fhem-mirror/blob/dev/fhem/FHEM/lib/Device/Firmata/Platform.pm

    gib mir bitte Feedback, ob das so passt, damit ich es ins FHEM SVN mergen kann.

    Gruß,

    Norbert
    while (!asleep()) {sheep++};

    Christian.

    Hallo Norbert,

    vielen Dank! Ich werde Deine Änderungen in den nächsten Tagen testen und gebe dann Feedback.
    Raspberry Pi 3 mit FHEM; Arduino Nano mit ConfigurableFirmata (S0-Stromzähler); nanoCUL (MAX!); SIGNALduino (RXB6, 433 MHz); eBus; RS485 & D0 (SolarView); DVB-T (Thermo-/Hygrometer); Z-Wave; ZigBee

    Christian.

    Ich habe einen Software-Test (also zunächst ohne Anschluss von Stromzählern) gemacht. Dabei fiel auf:
    • In
    20_FRM_IN.pm fehlt vor Zeile 173 noch          my $hash = $main::defs{$name};
  • internal-pullup ist per Default on.
  • Im Log finde ich
    2013.07.27 14:52:52 3: Firmata Firmware Version: ConfigurableFirmata.ino V_2_05
    2013.07.27 14:52:52 3: received String_data: Unknown pin mode
    2013.07.27 14:52:52 3: received String_data: Unknown pin mode
    2013.07.27 14:52:52 3: received String_data: Unhandled sysex command
    2013.07.27 14:52:54 1: Including /var/log/fhem/fhem.save
    2013.07.27 14:52:54 3: received String_data: Unhandled sysex command
    [/list]
    Sobald ich mehr Zeit habe, schließe ich einen Zähler an und berichte weiter.
    Raspberry Pi 3 mit FHEM; Arduino Nano mit ConfigurableFirmata (S0-Stromzähler); nanoCUL (MAX!); SIGNALduino (RXB6, 433 MHz); eBus; RS485 & D0 (SolarView); DVB-T (Thermo-/Hygrometer); Z-Wave; ZigBee

    Christian.

    Ich habe weiter getestet, kriege es aber noch nicht hin.

    Ich habe den S0+-Ausgang des Stromzählers mit Pin 2 am Arduino verbunden und S0- mit GND am Arduino. Auf dem Arduino läuft ConfigurableFirmata. Das FRM_IN-Gerät hat das Attribut internal-pullup mit Wert on. Das führt zu folgendem Verhalten: solange der Zähler keinen Impuls abgibt, leuchtet die Sende-LED des Arduino dauerhaft; gibt der Zähler einen Impuls ab, erlischt die Sende-LED kurz (ca. 1 Sekunde).

    Wenn ich das mit meinen mäßigen elektrotechnischen Vorkenntnissen richtig verstehe, sendet die ConfigurableFirmata einen Impuls, wenn an Pin 2 HIGH anliegt. Sie sendet nicht, wenn LOW anliegt.

    Ich hätte gedacht, dass nur beim Signalwechsel etwas gesendet wird (LOW bei fallender Flanke, HIGH bei steigender Flanke), nicht dauerhaft. Denn in DigitalInputFirmata::outputPort() steht der Kommentar  // only send if the value is different than previously sent
    Ist das Verhalten denn so zu erwarten? Ich würde dauerhaftes Senden gern vermeiden.
    Raspberry Pi 3 mit FHEM; Arduino Nano mit ConfigurableFirmata (S0-Stromzähler); nanoCUL (MAX!); SIGNALduino (RXB6, 433 MHz); eBus; RS485 & D0 (SolarView); DVB-T (Thermo-/Hygrometer); Z-Wave; ZigBee

    ntruchsess

    Zitat von: fhem-user schrieb am Di, 30 Juli 2013 07:09Denn in DigitalInputFirmata::outputPort() steht der Kommentar  // only send if the value is different than previously sent
    Ist das Verhalten denn so zu erwarten? Ich würde dauerhaftes Senden gern vermeiden.

    Die DigitalInputFirmata-klasse sendet immer dann, wenn sich der (am Pin eingelesene) Wert ändert, also einmal bei fallender und einmal bei steigender Flanke. Die Sende-led sollte also jeweils beim Anfang und Ende des Pulses kurz leuchten. Wenn der Spannungswert am Pin mit dem internen Pullup nicht hoch genug gezogen wird (weil der S0-Ausgang im Vergleich zum Pullup zu niederohmig ist), dann ist er zwischen den Impulsen des Zählers eventuell nicht logisch High, sondern undefiniert und das gelesene Bit zappelt zufällig hin und her. (Das sollte man auch am rasch steiegenden FRM-counter sehen können). Schließ doch zum Testen statt des S0-Zählers mal einen Taster (gegen Masse) an.

    Gruß,

    Norbert

    P.S. ich bin ab heute nachmittag eine Woche in Urlaub, also nicht wundern, wenn erst mal keine Antwort kommt.
    while (!asleep()) {sheep++};

    Christian.

    Hallo Norbert,

    ich habe weiter experimentiert und denke, dass ich Hardware und Verdrahtung als Ursache des Problems ausschließen kann.

    Zur Diagnose habe ich den Arduino von der Fritz!Box getrennt und prüfe jetzt die Sende-LED bzw. die Ausgaben auf dem Serial Monitor der Arduino-Software. Ich habe zunächst einen Positiv-Test mit der SimpleDigitalFirmata gemacht: in der setup()-Funktion habe ich am Ende    pinMode(2, INPUT);
        digitalWriteCallback(0, 1<<2);

    hinzugefügt, also Pin 2 als Eingang mit internem Pullup konfiguriert. Damit funktioniert alles wie gewünscht: solange kein Impuls vom Zähler kommt, wird nichts gesendet; ein Impuls führt zu kurzem Blinken der Sende-LED.

    Ich bin dann zu ConfigurableFirmata gewechselt. Dort habe ich als letzte Befehle in systemResetCallback() ergänzt:
       Firmata.setPinMode(2, INPUT);
        digitalOutput.digitalWrite(0, 1<<2);

    Damit erwarte ich das gleiche Verhalten wie vorher; die Sende-LED bleibt aber aus, auch wenn ein Impuls eingeht.

    Bei meinem letzten Problem hatte ich etwas falsch verstanden und konfiguriert (Pin und Port verwechselt); ich vermute, dass es diesmal ähnlich ist... rufe ich vielleicht eine falsche Funktion auf, oder zum falschen Zeitpunkt?
    Raspberry Pi 3 mit FHEM; Arduino Nano mit ConfigurableFirmata (S0-Stromzähler); nanoCUL (MAX!); SIGNALduino (RXB6, 433 MHz); eBus; RS485 & D0 (SolarView); DVB-T (Thermo-/Hygrometer); Z-Wave; ZigBee

    Christian.

    Kurzer Zwischenstand: es fehlte noch ein Aufruf von digitalInput.reportDigital(). Ich habe jetzt am Ende von ConfigurableFirmata.systemResetCallback() insgesamt folgendes hinzugefügt:

    byte pin=2;
    Firmata.setPinMode(pin, INPUT);
    digitalInput.reportDigital(0, 1<<pin);
    digitalOutput.digitalWrite(0, 1<<pin);

    Damit verhält sich der Arduino wie gewünscht: ohne S0-Signal wird nichts gesendet, bei einem eingehenden S0-Signal wird kurz gesendet. Ich werde den Arduino jetzt wieder mit FHEM verbinden und weitertesten.
    Raspberry Pi 3 mit FHEM; Arduino Nano mit ConfigurableFirmata (S0-Stromzähler); nanoCUL (MAX!); SIGNALduino (RXB6, 433 MHz); eBus; RS485 & D0 (SolarView); DVB-T (Thermo-/Hygrometer); Z-Wave; ZigBee

    ntruchsess

    Hallo 'fhem-user',
    bin grade aus dem Kurzurlaub zurück, kann hier aber grade nichts 'in Hardware' testen.

    Genau die von Dir der ConfigurableFirmata hinzugefügten Aufrufe sollte das FRM_IN Modul selber (d.h. remote über das Firmata-protokoll) erledigen, wenn das Attribute 'internal-pullup' auf 'on' steht. Sowas im Sketch hardcoded zu hinterlegen ist natürlich zum Testen das Mittel der Wahl, aber eigentlich nicht Ziel des Ganzen (nämlich mit einer universellen Firmware möglichst viele Einsatzszenarien abzudecken).

    Zitat von: fhem-user schrieb am Mi, 07 August 2013 12:23
    byte pin=2;
    Firmata.setPinMode(pin, INPUT);
    digitalInput.reportDigital(0, 1<<pin);
    digitalOutput.digitalWrite(0, 1<<pin);


    Ich hab grade den Fix mit der fehlenden $hash-referenz in der FRM_IN_Attr-function im dev-branch auf Github committed. Wichtig ist, dass Du sowohl das FRM_IN-modul, als auch die gepatchte lib/Device/Firmata/Platform.pm verwendest, sonst klappt der digitalWrite-Aufruf zum aktivieren des internal Pullups nicht.

    Heute Abend kann ich das selber auch mal 'in Echt' testen, letzte Woche hab ich das mangels Zugriff auf die reale Hardware quasi 'blind' implementiert (daher auch nur im dev-branch auf Github und nicht gleich ins SVN).

    Gruß,

    Norbert
    while (!asleep()) {sheep++};

    ntruchsess

    so, ich hab den Fehler gefunden (und gefixed). Der Check nach 'is_configured_mode' im Plattform.pm war so implementiert, dass er eine Exception geworfen hat, wenn der Pin nicht passend configuriert war. So ließ sich in der digitalWrite-methode natürlich nicht sinnvoll darauf testen, ob INPUT oder OUTPUT gesetzt ist, weil es beim Test auf OUTPUT schon ausgestiegen ist. Deshalb ging die Firmata-message beim setzen des 'internal-pullup'-attributes nie raus (und der pullup wurde nicht aktiviert).

    ... diesmal auf 'echter' Hardware getestet ;-)

    Wegen der zahlreichen Änderungen im Platform.pm erstmal nur im 'dev'-branch auf Github (siehe vorige Posts). Ich hab wegen der fortgeschrittenen Stunde grade nur die Funktion mit FRM_IN getestet - nicht dass da jetzt ein Typo drin ist, der eines der anderen FRM-module bricht...

    Gruß,

    Norbert
    while (!asleep()) {sheep++};

    Christian.

    Hallo Norbert!

    Ich hoffe, Du hattest einen schönen Urlaub.

    Ich habe die beiden Module 20_FRM_IN.pm und Platform.pm gerade aus dem Github-Repository heruntergeladen und auf die Fritz!Box kopiert, das Modul 20_FRM_IN neu geladen, ein shutdown restart abgesetzt und danach nochmal die Fritz!Box vom Strom getrennt und wieder verbunden - sicher ist sicher ;-).

    Ergebnis: für Pin 2 funktioniert es jetzt - großartig! Einen herzlichen Dank, das war das fehlende Mosaik-Steinchen für mein Projekt!

    Danach habe ich einen zufälligen anderen Pin probiert, Pin 31. Hier sendet der Arduino nach wie vor, sobald ich S0+ mit dem Arduino-Pin verbinde (S0- ist mit GND verbunden)?! Ich habe in die Sourcen der ConfigurableFirmata einige Debug-Ausgaben eingebaut. Denen kann ich entnehmen, dass immer abwechselnd

    DigitalInputFirmata::outputPort(3,128,0);
    FirmataClass::sendDigitalPort(3,128);

    DigitalInputFirmata::outputPort(3,0,0);
    FirmataClass::sendDigitalPort(3,0);
    aufgerufen wird. Dabei ist immer portConfigInputs[portNumber]=128.

    Hast Du dazu auch noch eine Idee?

    Schöne Grüße
    Christian
    Raspberry Pi 3 mit FHEM; Arduino Nano mit ConfigurableFirmata (S0-Stromzähler); nanoCUL (MAX!); SIGNALduino (RXB6, 433 MHz); eBus; RS485 & D0 (SolarView); DVB-T (Thermo-/Hygrometer); Z-Wave; ZigBee

    Christian.

    Ich habe noch ein paar andere Pins probiert: 8, 16, 30, 32, 33 funktionieren; 7, 15, 23 funktionieren nicht. Ich vermute deshalb, dass jeder (8n-1)-te Pin nicht funktioniert. Passt da vielleicht irgendwo eine Bitmaske nicht?

    Um den Fehler weiter einzukreisen, habe ich den Arduino nochmal von FHEM getrennt und in systemResetCallback()
    byte pin=31;
    Firmata.setPinMode(pin, INPUT);
    digitalOutput.digitalWrite(pin/8, 1<<(pin%8));
    digitalInput.reportDigital(pin/8, 1<<(pin%8));

    hinzugefügt. Damit funktioniert das Senden wie gewünscht - die Fehlerursache muss also auf der Perl-Seite liegen. Ich tippe auf digital_write in Platform.pm, weil da Bits hin- und hergeschoben werden, blicke da aber grad nicht durch.
    Raspberry Pi 3 mit FHEM; Arduino Nano mit ConfigurableFirmata (S0-Stromzähler); nanoCUL (MAX!); SIGNALduino (RXB6, 433 MHz); eBus; RS485 & D0 (SolarView); DVB-T (Thermo-/Hygrometer); Z-Wave; ZigBee

    ntruchsess

    danke für das konstruktive Feedback. Um genauer einzugrenzen welche Seite da Mist baut braucht man sich eigentlich nur die Firmata-messages ansehen (indem man 'attr globale verbose 5' setzt), Protokollbeschreibung ist hier. Ich bin zwar eher ein Freund der wirklich kleinen Geräte, habe mir zum Testen mittlerweile aber auch einen MEGA256 geholt, mit dem ich auch mal höhere Pinnummern testen kann als mit Uno oder Nano.
    Um sich die Sache zu vereinfachen kann man die Perl-firmata kann amn auch ganz einfach außerhalb von fhem/FRM zum Testen hernehmen.

    Gruß,

    Norbert
    while (!asleep()) {sheep++};