Posts mit dem Label Hardware werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Hardware werden angezeigt. Alle Posts anzeigen

Mittwoch, 15. Januar 2014

Wie erklärt man Idempotenz ?

"Man bezeichnet ein Element einer Menge (also auch eine Funktion), das mit sich selbst verknüpft wieder sich selbst ergibt, als idempotent." (wikipedia). Dass es sich hierbei um eine der zentralen Anforderungen bei der Programmierung verteilter Systeme handelt, und dass das ganz erhebliche Auswirkungen auf Architektur und Entwurf solcher Systeme hat, ist (nach meiner Erfahrung) für Studenten nicht grade leicht verständlich.

Um das anschaulicher zu machen, habe ich eine "Lampe" gebaut:


Dazu gibt es einen Schalter, genauer gesagt einen Taster


Bei jedem Druck auf den Taster (ganz klein, rechts an der Platine) ändert sich der Zustand der Lampe: Ist sie aus, dann geht sie an, ist sie an, dann geht sie aus.

Soweit ganz einfach. Der Taster sendet eine Nachricht ("wurde gedrückt") an den Empfänger, der schaltet entsprechend hin und her. Kompliziert wird das dadurch, dass die Übertragung über Funk erfolgt. Die Übertragung ist deshalb nicht verläßlich. Insbesondere haben die Module nur eine beschränkte Reichweite. Vergrössert man den Abstand weit genug, gehen Nachrichten verloren.

Steht die Lampe in einem Raum und der Schalter in einem anderen, weiss man nach dem Drücken der Taste nicht mehr sicher, ob die Nachricht angekommen, die Lampe also an oder aus ist. Dies wäre nur der Fall, wenn jede Nachricht genau einmal zugestellt würde ("exactly once").

Das ist aber nicht realisierbar: Ein Ansatz wäre, dass der Empfänger eine Bestätigung schickt, wenn er die Nachricht erhält. Kommt diese beim ursprünglichen Absender an, weiss er, dass die Nachricht erfolgreich zugestellt wurde und damit, ob das Licht jetzt an oder aus ist. Er kennt also den Zustand der Lampe ("shared state", beide sind sich einig darüber, ob die Lampe brennt oder nicht).

Aber was passiert, wenn die Bestätigung nicht ankommt ? Wenn die Nachricht tatsächlich nicht angekommen ist, könnte sie ein weiteres Mal gesendet werden. Aber eben das läßt sich nicht feststellen, denn das Ausbleiben der Bestätigung könnte mehrere Ursachen haben: Entweder ist die ursprüngliche Nachricht verloren gegangen, oder die Nachricht ist zwar angekommen, aber der Empfänger ist abgestürzt, bevor er die Bestätigung schicken konnte oder die Bestätigung ist verloren gegangen. Je nachdem, welche Ursache vorliegt, ist die Lampe an oder aus, der Taster (Absender) weiss es nicht.

Wie ließe sich dieses Problem lösen ? Man könnte die Bestätigung bestätigen. Kommt diese an, ist alles OK, aber wenn nicht, gibt es verschiedene Ursachen usw.

So wird das Problem also nicht zufriedenstellend gelöst, da zumindest die letzte Nachricht in einer Kommunikation nicht verlässlich übertragen werden kann. Deshalb kann man nicht vermeiden, dass eine Nachricht gelegentlich entweder verloren geht, oder mehrfach übertragen wird. Es gibt nur "at most once" oder "at least once".

Bestätigungen sind also für diesen Fall nicht geeignet. So wird die Grundannahme, dass mit einem solchen Protokoll (wie es etwa TCP realisiert) eine verläßliche Übertragung von Nachrichten, die den Zustand ändern, möglich ist, erschüttert. Doch wie löst man das jetzt ?

Man betrachtet den Eingang einer Bestätigung nicht als verläßliche Bestätigung, sondern schickt eine Nachricht, falls nicht innerhalb einer bestimmten Zeitspanne eine Bestätigung eintrifft, einfach nochmal ("at least once"). Oder man überträgt gleich die Nachricht immer wieder. Dazu braucht man natürlich andere Nachrichten, mit unserem Beispiel würde die Lampe ständig an und aus geschaltet werden.

Man muss die Anwendung so konstruieren, dass kein Schaden entsteht, wenn eine Nachricht mehrfach empfangen wird. Das ist Idempotenz.


Im Bild sieht man einen Schalter, der zwei Zustände hat (an und aus) und den aktiven Zustand immer wieder (im Beispiel alle 200 Millisekunden) überträgt. Diese Nachricht (entweder "An" oder "Aus") kann beliebig oft übertragen werden, der Empfänger ist, sobald er mindestens eine (aktuelle) Nachricht erhält, im richtigen Zustand. Ist die Lampe an, und es wird nochmal "An" empfangen, passiert nichts (unerwünschtes).


Nächste Woche werde ich das in der Vorlesung ausprobieren, mal schauen, ob dieses Konzept so besser verständlich ist.

Realisiert wurde das System mit ZigBee zur Übertragung, hierfür wurden XBee-Module der Firma Digi verwendet. Das würde natürlich auch mit anderen Übertragungstechniken funktionieren, aber diese Module sind sehr bequem zu verwenden, die Funktionen, die man hier braucht, sind schon eingebaut: Im einen Fall wird der Zustand eines digitalen Eingangs bei jeder Änderung übertragen, im anderen Fall werden die Eingänge in einem festen Zeitintervall abgetastet und übertragen.

Für den Empfänger bräuchte man an sich nur ein bisschen Logik (im Taster-Betrieb ein Flip-Flop, das den aktuellen Zustand speichert und eine Erkennung mit entsprechender Logik, die erkennt, welche Betriebsart grade vorliegt, das wird durch einen weiteren Eingang mit entsprechend fixem Wert festgelegt). Das liesse sich leicht diskret ohne Controller realisieren, wenn nicht ... die XBee-Module mit 3.3 Volt arbeiten würden ... Man bräuchte also auch noch Level-Shifter. Und da ich den Arduino grade rumliegen hatte ....

Donnerstag, 17. Oktober 2013

Wie lang hält die Batterie ....

Grau mein Freund ist alle Theorie ....

Also messen. Und zwar in der endgültigen (na ja ... zumindest mal für dieses Jahr) Konfiguration. Arduino FIO, Standard-XBee, LiPo-Akku mit 850 mAh, Messung alle 4 Sekunden, Datenübertragung einmal pro Minute, keine Routing-Funktion in den Sensorknoten, Pin-Sleep-Mode, d.h. die XBee-Module werden vom Arduino einmal pro Minute per Hardware aufgeweckt (das ist der größte Unterschied zur "endgültigen" Version, bei der werden die XBee-Module den Arduino wecken, um ZigBee-Routing zu ermöglichen. Aber eins nach dem anderen).

Nach etwa sechs Wochen ist die Spannung von anfänglich etwa knapp 4,2 Volt auf gut 3,7 Volt gefallen.

Gehen wir mal davon aus, dass wir dann weniger als die Hälfte der Kapazität verbraucht haben. Eine genaue Abschätzung ist schwierig, da üblicherweise für LiPos nur Entladekurven bei hohen Strömen publiziert werden. Die würden uns allerdings wenig nutzen, da solche Messungen idR nicht bei den für uns relevanten Temperaturen von 80-95 Grad durchgeführt würden.

Gehen wir also mal davon aus, dass wir dann noch die Hälfte der Kapazität haben, sind wir was die Akkus betrifft auf der sicheren Seite.



Samstag, 10. August 2013

AD7714 - die Software

Der AD7714 - ein 24 Bit Sigma-Delta-Wandler mit integrierter Signalaufbereitung - ist ein toller Chip (und wenn man bedenkt, wie aufwendig es wäre, all diese Funktionen selbst aufzubauen, nicht mal teuer), aber leider gibt es (bisher) keine Beispiele im Web, wie man ihn an einen Arduino anbindet, das ist nämlich nicht ganz trivial. Also los.

Angebunden wird er über die SPI-Schnittstelle. Da ich nur einen SPI-Chip verwende, wird das /CS-Signal (Pin 19) nicht verwendet, sondern fest auf low gelegt. So braucht man nur drei Leitungen, MISO (Daten vom Wandler, Pin 21), MOSI (Daten vom Arduino, Pin 22) und SCK (Takt, Pin 1).

SPI ist kein "absoluter" Standard, sondern ein de-facto-Industriestandard, heisst, es gibt verscheidene Ausprägungen. Deshalb muss zunächst festgelegt werden, wie die Daten übertragen werden: Zunächst der SPI-Mode, der festlegt, welche Polarität das Taktsignal hat und welche Flanke zur Signalübernahme dient. Wir nehmen  Mode 2, das bedeutet Takt ist aktiv low und die Daten werden bei der fallenden Flanke übernommen. Das wird chipseitig durch die entsprechenden Pins festgelegt (POL, Pin 4 auf high) und Arduino-seitig durch

SPI.setDataMode(SPI_MODE2);

Der AD7714 liefert (und erwartet) die Daten MSB first, das wird mit

SPI.setBitOrder(MSBFIRST);

definiert. Bleibt noch die Taktrate, wir haben es nicht allzu eilig und nehmen

SPI.setClockDivider(32);

das müssten 500kHz sein.

Der Zugriff auf die internen Register erfordert zunächst die Auswahl des zu schreibenden oder zu lesenden Registers. Dies geschieht durch Schreiben des Communication Registers. Dieses legt in den unteren drei Bits den Kanal des Wandlers fest. Wir verwenden nur einen Kanal, den aber differentiell an AIN1 und AIN2, dieser hat den Code 100 (also 4 dezimal). Das nächste Bit definiert, ob gelesen oder geschrieben wird, 1 bedeutet Lesen (also 8 dezimal).  Die nächsten drei Bits geben das Register an, auf das als nächstes zugegriffen werden soll.


CodedezimalRegister
0000Communications Register
00116Mode Register
01032Filter High Register
01148Filter Low Register
10064Test Register
10180Data Register

Die Codes 110 und 111 sind die Calibration Registers und im Moment uninteressant.

Das oberste Bit des Communication Registers ist das /DRDY muss beim Schreiben immer 0 sein, beim Lesen liefert es den Wert des /DRDY-Pins (low, wenn Daten gelesen werden können).

Zur Initialisierung des AD7714 wird zunächst das Filter konfiguriert, das durch Schreiben von 4 (Kanal AN1+AN2, Schreibzugriff) + 32 (Filter High Register) = 36 (0x24) ausgewählt wird.

Im Filter-Register werden im oberen Byte einige Parameter konfiguriert, hier unipolare Operation, 24 Bit Auflösung der zu lesenden Daten, Current Boost (da wir mit hoher Verstärkung und hohem Takt arbeiten) und ohne Clock Disable. Dies ergibt 1110 für die oberen 4 Bit. Die unteren 4 Bit sind die höherwertigen Bits des Filter-Teilers. Die niedrigste Filterfrequenz ergibt sich mit dem Teiler 4000 (0xfa0), nämlich fCLOCK/128/4000 = 4,8 Hz. Da die Zeitkonstante des zu messenden Temperatursignals sicher länger ist, ist die Genauigkeit umso besser, je niedriger die Filterfrequenz.

Also zunächst das Filter High-Register auswählen (0x24), dann die Werte schreiben (0x4f), dann das Filter-Low-Register auswählen (0x34) und das untere Byte (0xa0) schreiben.

Als nächstes muss das Mode-Register konfiguriert werden, dazu zunächst Mode-Register für einen Schreibzugriff auswählen 0x14 (20)