Brrr, es ist kalt hier drin!

Nachdem ich vor etwa einem Jahr den AVR entdeckt habe, habe ich inzwischen ein erstes kleines „Projekt“ umgesetzt, welches mir schon seit langem vorschwebte: Ein über USB angebundenes Thermometer. Das Thermometer sollte an den Server angeschlossen und von diesem in regelmäßigen Abständen ausgelesen werden. Aus den so gewonnenen Daten darf dann ein nettes Diagramm erzeugt werden.

Vorüberlegungen

Auf dem Weg zum eigenen Thermometer war zu entscheiden: Welche AVR-CPU soll es werden, welcher Temperatursensor und wie ist das alles (über USB) mit dem Computer zu verbinden?

Die Anbindung an den Computer wollte ich über die schon aus dem Programmierer bekannte AVR-USB-Lib von obdev.at realisieren. Diese Umsetzung des USB-Stacks hat den Vorteil, dass sie frei zu verwenden ist und mit minimaler externer Beschaltung auskommt. Minimale Beschaltung heißt dabei, dass bei den AVR-CPUs mit internem 16-MHz-Taktgenerator dieser benutzt und auf einen externen Quarz verzichtet werden kann.

Eine der CPUs, bei denen der interne Taktgeber geeignet ist, ist der AVR ATTiny 25, der mit seinen 8 Beinchen einer der kleinsten verfügbaren AVR ist. Zwei der Anschlüsse werden für die Stromversorgung, zwei weitere für die USB-Anbindung benötigt. Bleiben vier Verbindungen, die mit einem Sensor sprechen könnten. Drei mehr, als nötig.

Beim Sensor ist meine Wahl auf den, mit circa 5€ zwar relativ teuren, aber dafür auch mächtigen, DS18S20 gefallen. Dieses Bauteil übernimmt komplett die Umwandlung der analogen Messwerte in digitale Werte. Die Steuerung und das Auslesen der Werte erfolgt über ein einfaches Protokoll. Während das Protokoll des DS18S20 einfach aufgebaut ist, haben mir die dabei notwendigerweise einzuhaltenden Zeiten größere Probleme bereitet.

Timing ist alles

Die AVRs besitzen keine Hardware, um bei der Implementierung von USB zu unterstützen. Alles muss in Software gemacht werden. Wenn man bedenkt, dass USB 1.1 die Daten mit 12 MBit auf die Leitung legt, dann erscheinen die 16 MHz plötzlich nicht mehr besonders schnell. Zwischen dem Beginn eines „USB Frames“ und dem Beginn der Verarbeitung dürfen maximal 59 Taktzyklen vergehen. Die Verarbeitung geschieht im Interrupt INT0 und man darf die Interrupts daher nicht länger als 52 Taktzyklen sperren, will man keine USB-Pakete verpassen.

Befehle, beziehungsweise deren einzelne Bits, an den DS18S20 zu schicken, läuft nach einem simplen Schema ab: Signal anlegen, kürzer oder etwas länger warten, Signal zurücknehmen. Das Signal anzulegen oder es zurückzunehmen ist mit wenigen Befehlen getan, so dass man diese Operationen bei gesperrten Interrupts ausführen kann. Probleme bereitet die genaue Zeit, die das Signal an der Leitung anliegen muss: Sie ist länger als die 52 Taktzyklen, aber kürzer als die Verarbeitungszeit der USB-Interrupt-Verarbeitung. Tritt in der Zwischenzeit ein Interrupt auf, so werden die Daten fehlerhaft übertragen.

Mit diesen Voraussetzungen ist es nicht möglich, gleichzeitig die Verarbeitung von USB-Daten und die Ansteuerung des DS18S20 zu betreiben. Als Ausweg habe ich mir überlegt, dass es möglich sein sollte, ein Zeitfenster zu finden, in dem der USB-Interrupt lange genug sicher nicht auftritt. Tatsächlich könnte man solche Zeitfenster finden, wenn man die knapp 1 ms nutzt, die ein USB-Frame noch dauert, nachdem der USB-Stack erkannt hat, dass es ein Frame für ein anderes Gerät ist. Ein Bit zu übertragen dauert, wählt man die kürzesten erlaubten Signalzeiten, 60 µs. Pro USB-Frame könnte man also maximal 16 Bit übertragen. Um das Ergebnis Temperaturmessung vom Sensor zum Prozessor zu übertragen, sind 11 Byte und ein Leitungsreset (dauert mindestens 960 µs, davon 540 µs nicht zu unterbrechen) notwendig.

Möglich wäre es also, die Übertragung so in den USB-Datenstrom einzuflechten, dass dieser nicht gestört wird. Ich habe mich bei meiner Implementierung jedoch für eine etwas einfachere Variante entschieden, in der ich zwar den Beginn der Übertragung mit den USB-Paketen synchronisiere, dann aber den Verlust von folgenden USB-Frames akzeptiere. Dieses Verfahren ist zum einen einfacher umzusetzen, da man nicht über einen endlichen Automaten arbeiten muss, zum anderen belegt der USB-Stack alleine schon etwa 1500 der 2048 Bytes Programmspeicher des ATTiny 25, so dass keine großen Sprünge mehr möglich sind. Da die Software auch auf Rechnerseite unter meiner Kontrolle ist, war es einfacher, sicherstellen, dass nach dem Absetzen eines Befehls für 10 ms kein weiterer Befehl an das Thermometer geschickt wird.

Ergebnis

Das Thermometer ist seit ein paar Monaten aufgebaut und wird alle 6 Minuten (als 10× pro Stunde) nach der gemessene Temperatur gefragt. Die jeweils letzten 240 Werte werden in eine Datei gespeichert und über ein kleines Skript an die Google-Charts-API übergeben. Das Ergebnis gefällt mir ganz gut:

Man kann schön sehen, dass die Temperatur im Gang gegen 8 Uhr zuerst angestiegen ist, nachdem ich aus der Dusche gekommen bin, danach durch Stoßlüften aber kurz gefallen ist. Auch jetzt im Winter heizt die Sonne die Wohnung etwas auf, wie ich an sonnigen Tagen aus der Kurve für den Nachmittag ablesen kann.

Quellen

Da obdev.at die GNU General Public License fordert, gibt es die Quelltexte des Thermometers zum herunterladen. Der zugehörige Schaltplan und die aufgebaute Schaltung:

Auf dem Bild kann man oben die 4 Leitungen zum USB-Stecker und unten die drei Leitungen zum Sensor sehen. Die gesamte Lochrasterplatine ist nur etwa 3 cm auf 2 cm groß, passt also gut zweimal in eine Streichholzschachtel.