Homematic Wired - Homebrew Devices

Begonnen von Thorsten Pferdekaemper, 27 April 2014, 00:13:17

Vorheriges Thema - Nächstes Thema

Thorsten Pferdekaemper

Zitat von: MarkusO am 03 August 2014, 23:45:59Mit der Eclipse IDE und dem Arduino Plugin läufts jetzt. Komischerweise habe ich mit Deinen importierten Einstellungen weiterhin Fehler beim Build-Prozess gehabt (trotz angepassten Verzeichnissen). Irgend ein Make-File wurde nicht gefunden...
Tja, bei Eclipse weiß ich auch nie so genau was warum funktioniert bzw. nicht funktioniert. ...aber wenn's jetzt klappt, dann ist ja gut.

ZitatAch ja, nochwas:Ich würde das mal mit dem Kondensator probieren. Beim Flashvorgang aus der Arduino-IDE (und vermutlich auch über das Eclipse-Plugin) wird ein Reset auf den Arduino ausgelöst, um ihn in den Bootloader zu schicken.
Ah, es geht dabei um den Programmier-Arduino. Gut, jetzt hab ich's kapiert.
Allerdings lass ich die Versuche jetzt lieber, da ja auch der Pegel nicht stimmt.

Zitat von: Dirk am 04 August 2014, 00:29:14Falls du einen Raspberry Pi "rumliegen" hast, den kannst du auch als ISP-Programmer benutzen:
Das werde ich vielleicht mal probieren, aber erstmal geht's wieder nach London, also nächste Woche vielleicht.
Mit was machst Du das denn?

Gruß,
   Thorsten
FUIP

Dirk

#136
Zitat von: Thorsten Pferdekaemper am 04 August 2014, 13:14:57
Mit was machst Du das denn?
Ich habe mir einen USBASP: http://www.fischl.de/usbasp/ gebaut.
Da der auch mit 5V vom USB-Bus versorgt wird, sind noch 2 Spannungsteiler (4 Widerstände) dabei, damit das Ganze 3,3V Kompatibel wird.

Gruß
Dirk

reneFHEM

Hallo zusammen,

mit meinem letzten commit habe ich nun noch einen ext. Tastereingang für den MDIR geschaffen. Dieser soll auch zum Peering mit anderen Homematic Modulen benutzt werden. Das ist meine nächste Baustelle ....

Gruß Rene

Thorsten Pferdekaemper

Hi,

ich habe auch mal wieder ein klein wenig weitergebaut. In meiner neusten Git-Version sendet das Device die Temperaturen jetzt an die Zentrale, wenn es angelernt wurde. Ansonsten weiterhin an 0xFFFFFFFF (broadcast).
Im Prinzip passiert das im Device selbst (also im Hauptprogramm), aber ich habe die Methode broadcastInfoMessage durch sendInfoMessage ersetzt (sorry Rene). Die Broadcast-Funktion bekommt man, wenn man als letzten Parameter 0xFFFFFFFF einsetzt.

Ich weiß, dass das wie ein Detail aussieht, da es der Zentrale im Prinzip egal ist. Allerdings bekommt man mit Broadcast-Messages kein fehlertolerantes Protokoll hin, da es kein ACK gibt.

Außerdem noch eine kleine Änderung: Beim Reset konnte es passieren, dass das Teil nach der Announce-Message eine Weile (1 Sekunde oder so) nicht reagiert hat. Dadurch konnte das Anlernen schief gehen. Das sollte jetzt nicht mehr der Fall sein.

Gruß,
   Thorsten
FUIP

reneFHEM

Hallo Thorsten,

das kleine syntaktische Detail sollte für mich nicht so schwierig zu lösen sein :-). Was das aus Protokollsicht bedeutet, kann ich für mich (noch) nicht abschätzen. Bisher war ich einfach nur Anwender.

Gruß Rene

Thorsten Pferdekaemper

Hallo Rene,
der Punkt ist, dass man per Broadcast keine sichere Kommunikation hinbekommt. Das Protokoll hat im Prinzip zwei Mechanismen um die Kommunikation zuverlässig zu machen. (Verkürzt dargestellt...)

  • Jedes Gerät wertet mit dem Senden, bis alle anderen still sind (und noch ein bisschen länger).
  • (Fast) jede Nachricht wird mit einem ACK bestätigt. Wenn das ACK nicht kommt, dann wird die Nachricht wiederholt.
Das ACK ist natürlich nur dann sinnvoll, wenn es einen bestimmten Empfänger gibt. Beim Broadcast müssten sonst alle ein ACK schicken. ...aber nichtmal das würde was bringen, da ein Gerät normalerweise nicht weiß, wie viele Geräte vorhanden sind. Es gibt also beim Broadcast kein ACK. Deshalb weiß das Gerät nicht, ob seine Nachricht angekommen ist und kann daher auch keine Wiederholung schicken.
Gruß,
   Thorsten
FUIP

reneFHEM

Hallo Thorsten,

das heißt für mich im Klartext ich muss erstmal die Config-informationen im EEPROM haben. Also das Peering mit Zentrale oder anderen HM-Devices und dafür muss ich die Gerätebeschreibungsdatei (xml-File) vervollständigen. Wenn nichts (also 0xffff) im EEPROM steht wird ein Broadcast gesendet.

Ist das so korrekt ?

Gruß Rene

Thorsten Pferdekaemper

Hi,
Du musst das nicht machen. Das ganze passiert eh im Hauptprogramm. Wenn Du das benutzen willst, dann musst Du das machen, ansonsten nicht.
Theoretisch könnten wir uns aber auch darauf einigen, dass bei "unseren" Geräten die Adresse der Zentrale immer an Speicherplatz 0x0002 im EEPROM beginnt. Dann könnte ich auch sowas wie "sendInfoMsgToCentral" bauen, das das alles automatisch erledigt.
(Es handelt sich dabei um das Pairing an die Zentrale. Peering an andere Devices ist komplizierter.)
Gruß,
   Thorsten
FUIP

Thorsten Pferdekaemper

Hallo nochmal,
ich bin gerade dabei, ein paar "strukturelle" Änderungen vorzunehmen. Falls also jemand das Coding, was er momentan verwendet, auf die neue Version anpassen will, dann bitte noch ein bisschen damit warten. Es wird wahrscheinlich bald noch eine neue Version geben, die ein paar größere Änderungen erfordert.
Das ist notwendig, um die Kommunikation sicherer zu gestalten. Dazu muss das Modul selbst (also im Wesentlichen die Kommunikationsschicht) das Timing im Griff haben. So ziemlich alles, was Device-spezifisch ist (Sensoren auslesen, Tastendrücke verarbeiten, Schalter schalten...) soll dann über Callbacks (C++-Interfaces) erledigt werden. 
Gruß,
   Thorsten
FUIP

Thorsten Pferdekaemper

Hi,
die neuste Git-Version:

  • Wenn das Device eine Nachricht raussendet (außer an Broadcast) und kein ACK empfängt, dann wir die Nachricht bis zu zweimal wiederholt. Die Wartezeit bis zum ACK beträgt 200ms. (Ich weiß, da muss noch ein Zufallsanteil rein.) Broadcasts und eigene ACKs sind ausgenommen.
    Momentan ist das noch blockierend implementiert. D.h. nach einer Sendung wartet das Device (die Kommunikationsschicht) erstmal auf das ACK. Währenddessen passiert gar nichts anderes. Das kann bis zu (etwas mehr als) 400ms dauern. Falls als Antwort nicht nur ein ACK kommt, dann wird der Rest der Message momentan auch noch ignoriert. (Das kommt aber bei normalen Devices wahrscheinlich gar nicht vor.)
  • Ein paar Sachen, die in die Kommunikationsschicht gehören, wurden dorthin verlagert. An's Modul werden jetzt nur noch Nachrichten weitergereicht, die die Kommunikationsschicht nicht alleine abhandeln kann. (Z.B. ACKs, leere Nachrichten oder Nachrichten an andere Empfänger)
  • Die Anbindung des Moduls an die Kommunikationsschicht beim Empfangen erfolgt jetzt über ein Callback (C++-Interface). Das wird in Zukunft wahrscheinlich noch etwas ausgebaut.
  • Unnötiges Coding rausgeworfen und das Senden etwas schneller gemacht (nur noch ein flush() am Ende und Behandlung des TX-Pins nur einmal am Anfang und einmal am Ende).

Zu meiner Begrifflichkeit:

  • Wenn ich von "Kommunikationsschicht" rede, dann meine ich den Teil, der sich mit dem Low-Level Protokoll befasst, also ohne Interpretation der Kommando-Bytes in der "Nutzlast". Das ist implementiert in HMWRS485.
  • Das "Modul" ist der Teil, der die Kommando-Bytes und die "Nutzlast" interpretiert. Dieser Teil weiß auch, welche Nachricht eine Antwort erwartet. Das ist implementiert in HMWModule
  • Das "Device" ist sozusagen das Hauptprogramm, also z.B. das, was in HBW-1W-T10.cpp implementiert ist. Das Modul ruft das Device über Callbacks auf (Interface HMWDeviceBase).

Für Verwender des ganzen (@Rene...): Es gibt jetzt eine neue Methode HMWRS485->loop(). Das ist jetzt die einzige Methode, die das Device in "seiner" loop() zyklisch und möglichst oft aufrufen muss. Aus dem hier:

if(hmwrs485.frameComplete) {
      if(hmwrs485.targetAddress == hmwrs485.txSenderAddress || hmwrs485.targetAddress == 0xFFFFFFFF){
        hmwrs485.parseFrame();
        hmwmodule->processEvents();
      }
   };

...wird also das hier:

hmwrs485.loop();

Wie das Modul mit der Kommunikationsschicht zusammenhängt klären die beiden jetzt unter sich.

Falls jemand in den nächsten zwei Tagen einen schwer wiegenden Fehler findet, dann kann ich den ggf. noch schnell reparieren. Ansonsten geht's erst Mitte/Ende September weiter. Ich fliege nach Hawaii...  :D  (SCNR)
Gruß,
   Thorsten
FUIP

reneFHEM

Hallo Thorsten,

der Umbau sieht erstmal super aus. Ich werde Ihn bei mir mal integrieren.

ZitatDas ist jetzt die einzige Methode, die das Device in "seiner" loop() zyklisch und möglichst oft aufrufen muss

Möglichst oft aufrufen finde ich nicht ganz so hübsch. Da ich in meinem Device ohnehin einen 10 ms Takt über MSTimer erzeuge, hatte ich schon überlegt das Device zwischen den Timerevents in den Stromsparmodus zu schicken (Ob es funktionieren würde, muss ich probieren). Ist es aus Protokollsicht (mit Hardwareserial) unbedingt erforderlich so schnell wie möglich zu arbeiten?

Gruß Rene

Thorsten Pferdekaemper

Zitat von: reneFHEM am 18 August 2014, 20:34:54Möglichst oft aufrufen finde ich nicht ganz so hübsch. Da ich in meinem Device ohnehin einen 10 ms Takt über MSTimer erzeuge, hatte ich schon überlegt das Device zwischen den Timerevents in den Stromsparmodus zu schicken (Ob es funktionieren würde, muss ich probieren). Ist es aus Protokollsicht (mit Hardwareserial) unbedingt erforderlich so schnell wie möglich zu arbeiten?
Hi,

ich weiß nicht, ob das mit dem Stromsparmodus bei kabelgebundenen Sachen so der Hit ist. Wacht das Ding dann automatisch auf, wenn was über den Bus kommt? Funktioniert der Interrupt für die serielle Schnittstelle dann noch? Beim Funkprotokoll gibt es sozusagen ein festgelegtes Timing, aber nicht beim Kabel.
Ansonsten dürfte es kein Problem sein, zwischendurch 10ms zu schlafen. Es ist nur wichtig, dass loop() oft genug aufgerufen wird, dass der Empfangspuffer abgearbeitet werden kann. 

Was genau musst Du denn alle 10ms machen? Das könnte nämlich sowieso problematisch werden, da ich das ganze wahrscheinlich so umbauen will, dass die Library die Kontrolle über das Timing bekommt. Dann gibt es für das Device nur noch Callbacks. Der Grund dafür ist, dass das Protokoll auch bestimmte Vorgaben für das Timing hat.
Auf keinen Fall darfst Du in der Interrupt-Routine für den Timer irgendwas aufrufen, was Nachrichten über RS485 rausschickt. Das kann nämlich deutlich länger als 10ms dauern.

...aber sag' mal, was Du da genau machst, dann schaue ich mir's nochmal an.

Gruß,
  Thorsten
FUIP

reneFHEM

Hallo Thorsten,

ich erzeuge mir mit dem ms-Timer einen Rechteckttakt mir 50 Hz. Diesen benötigt der IR-Sensor um die Pins (Licht Ein/Aus Motion Detection) entsprechend zu schalten. Dies geschieht in der Timer-ISR und liest auch nur paar GPIO's ein und setzt ein Statusbit. Das Versenden der Events wird dann aus der Mainloop gesteuert. Diese kann halt durch den Timer unterbrochen werden. Funktioniert bisher ohne Probleme, allerdings mit einem Rechtecktakt von 1 Hz (Ich erzeuge mit Tastern die Sensor-Impulse). 

Ob es tatsächlich funktioniert den uC schlafen zu legen und ob eine Einsparung an Strom daraus resultiert kann ich (noch) nicht sagen. In den BASCOM-Quellen von denen ich die Logik übernommen habe, wurde es mit dem Watchdog realisiert das der uC wieder aufwacht. 

Deine Neuerungen habe ich gestern bei mir integriert und sie funktionieren.

Gruß Rene

Thorsten Pferdekaemper

Zitat von: reneFHEM am 19 August 2014, 20:58:35ich erzeuge mir mit dem ms-Timer einen Rechteckttakt mir 50 Hz. Diesen benötigt der IR-Sensor um die Pins (Licht Ein/Aus Motion Detection) entsprechend zu schalten. Dies geschieht in der Timer-ISR und liest auch nur paar GPIO's ein und setzt ein Statusbit. Das Versenden der Events wird dann aus der Mainloop gesteuert.
Hi,
das sollte kein Problem sein. Wie gesagt, die serielle Schnittstelle muss halt noch die Chance haben, dazwischen zu kommen und hmwrs485.loop(); muss halt oft genug aufgerufen werden, um den Puffer der seriellen Schnittstelle abzuarbeiten.
Nettes Device, ich glaube, dass baue ich dann auch mal nach. ...aber wohl erst im Oktober oder so.
Gruß,
    Thorsten
FUIP

Dirk

Zitat von: Thorsten Pferdekaemper am 19 August 2014, 22:11:40
Wie gesagt, die serielle Schnittstelle muss halt noch die Chance haben, dazwischen zu kommen und hmwrs485.loop(); muss halt oft genug aufgerufen werden, um den Puffer der seriellen Schnittstelle abzuarbeiten.
In den ISRs sollte halt möglichst wenig passieren. Am besten nur Register lesen/sichern und vielleicht ein paar Flags setzen.
Das Auswerten der Daten sollte dann im Main-Loop passieren. Dann solltes es recht unwahrscheinlich sein, dass sich die Interrupts blockieren.

Gruß
Dirk