home    Inhaltsverzeichnis
erste Version am 02.01.2017
letzte Änderung am 11.01.2017

Temperatur- und Helligkeits-Logger - Seite 4

eine kleine Änderung

Erstmal der (mittlerweile unübersichtlich gewordene) Schaltplan mit der Änderung für den LDR:
Schaltplan V2
Damit zieht die Schaltung im Leerlauf bzw. während ihrer Tiefschlaf-Phasen laut meinem Multimeter etwa 270µA.
Was für ein Scheiß. Bei der Rollladen-Steuerung hatte ich 13µA gemessen.
Zwei Unterschiede gibt es, die der Grund dafür sein könnten:
- der DS18B20
- der MOSFET (und damit auch Pin D8 vom ATmega328, der im Tiefschlaf jetzt auf HIGH bleiben muss)

Eben habe ich mir nochmal die Specs zum DS18B20 angesehen und bin über die sog. "Temperature Conversion Time" gestolpert, die bei "12-bit resolution" maximal 750ms dauern kann. Das müsste mein Programm eigentlich merkbar durcheinander bringen. Passiert ja aber nicht.
Lasse ich mir die Dauer für das Lesen eines Temperatur-Wertes anzeigen, liegt die zwischen 17 bis 19 Millisekunden. Das erklärt, warum mein Programm nicht durcheinander kommt.
Nicht klar ist mir, was es mit diesem tconv von 750ms auf sich haben mag... Es wird als die Dauer angegeben, die der DQ-Pin vor einer conversion (was genau damit auch immer gemeint sein mag) HIGH sein muss, wenn an VDD keine Spannung anliegt. Gilt dann vielleicht nur beim Betriebsmodus "parasite power" - den ich nicht nutze.
Egal....jedenfalls werde ich dem DS18B20 dann wohl besser nicht seine Versorgungsspannung abschalten, wenn ich ihn gerade nicht brauche.


die erste Einheit ist fertig

Eben habe ich das erste Gehäuse zugeschraubt. Ein Sensor-Paar steckt im Gehäuse, ein zweites Sensor-Paar ist über ein drei Meter langes Flachbandkabel angeschlossen und außen neben dem Fenster befestigt.
Gehäuse
Statt des 5-poligen DIN-Steckers habe ich mich (aus Gehäuse-Bearbeitungs-Faulheit) für Flachbandkabel und einen 9-poligen Sub-D-Stecker mit Schneid-Klemm-Technik entschieden.
Derzeit erfolgt die Stromversorgung über einen Akku-Pack aus vier Mignon-Zellen mit 1900mAh. Bei nächster Gelegenheit werde ich mir eine kleine USB-Powerbank besorgen und diese statt des Akku-Packs anschließen. Vorab muss ich allerdings erstmal ein Modell finden, das auch dann noch seine 5V liefert, wenn es selbst gerade mit 5V geladen wird.


Minusgrade am Außen-Sensor

Um 19:00 Uhr kamen die Daten von den ersten 90 Minuten. Die Werte für das interne Sensor-Paar sahen valide aus, die des externen Sensor-Paares waren jedoch Quatsch.
Beim externen LDR war der Fehler schnell gefunden: ich hatte den Pin-5 am Stecker nicht mit dem ATmega328-Pin-D6, sondern mit D7 verbunden. Das wäre nicht passiert, wenn ich den aktualisierten Schaltplan ausgedruckt und in die Werkstatt bzw. zum Lötkolben mitgenommen hätte.
Der DS18B20 war allerdings korrekt verdrahtet. Trotzdem kamen Werte zwischen 0.1°C und 255.6°C.
Daraufhin wollte ich nachsehen, ob ich bei der OneWire-Library vielleicht die Datenrate reduzieren kann (sind ja schließlich drei Meter Kabel dazwischen). War aber nix zu finden.
Stattdessen habe ich quasi überall gefunden, dass immer diese 750ms zwischen 0x44 (initiate conversion) und 0xBE (read scratchpad) gewartet werden.
Vielleicht sollte ich es damit mal probieren. Es würde ja ohnehin reichen, einen Messwert pro Minute zu holen. Dann kann ich auch gerne pro DS18B20 jeweils 750ms warten. Vorzugsweise im Tiefschlaf.
Etwas sonderbar ist es schon, dass ich bisher auch ohne Wartezeit valide erscheinende Werte bekommen habe.
Und zwar ebenfalls für das externe Sensor-Paar mit seinen die drei Metern Flachbandkabel, als ich dessen Funktionstest am Test-Aufbau mit Arduino UNO durchgeführt habe.


Wie gut, dass gestern ein Mittwoch war und die Mittwoch-Abende ab 20:15 Uhr für Dr. Who auf ARD ONE reserviert sind. Direkt im Anschluss kam noch Ein Abenteuer in Raum und Zeit. Danach gings ins Bett und ich musste somit zwangsläufig nochmal "drüber schlafen".
Hätte ich gestern Abend weitergemacht, hätte ich garantiert mehr Fehler ein- als ausgebaut.

Heute habe ich meinen letzten Urlaubstag damit verbracht, mir in aller Ruhe eine spezielle Sensor-Daten-Analyse-Firmware zu bauen, die in setup() einmalig eine Verbindung zum Python-SocketServer aufbaut, um dann in loop() ständig INFO-Meldungen senden zu können.
Die erste Erkenntnis war, dass identische Temperatur-Werte geliefert werden - egal ob zwischen 0x44 und 0xBE die angeblich nötigen 750ms lang gewartet wird, oder nicht.
Zweite Erkenntnis war, dass die Funktion readDS18B20_CRC() zwar völlig korrekt einen "signed int" zurückliefert, allerdings dem hochwertigen Byte vorher fälschlicherweise per &0x0F die Vorzeichen-Information kaputt-maskiert hat. Das hatte ich gerade erst eingebaut, um den Rückgabewert 0xFFFF als CRC-Fehlerkennung nutzen zu können. Jetzt wird stattdessen 0x8FFF verwendet, weil 0x8FFF (auch ohne Maskierung) keinen validen Temperatur-Wert darstellt.
Ein zweiter Fehler fand sich in der Datenstruktur SensorData: die Temperatur war als "unsigned int temp : 12;" deklariert.
Natürlich lässt sich damit eher schlecht eine negative Temperatur abbilden....
Entstanden ist es dadurch, dass ursprünglich vorgesehen war, auf ATmega328-Seite nur mit uninterpretierten Bit-Folgen zu hantieren und diesen erst auf Python-Seite als z.B. "vorzeichenbehaftete Zahl mit 4 Bit Nachkommastellen" einen Sinn zu geben. Das sollte auch klappen, solange ich auf ATmega328-Seite nicht damit rechnen würde. Aber dummerweise berechne ich ja den Mittelwert der in einer Minute gesammelten Temperatur-Werte....
Nach ein paar Tests an einem struct mit 12 Bit langem "signed integer" war ich erstmal positiv überrascht, wie schön C das jeweils höchstwertige Bit als Vorzeichen-Bit verarbeitet.
Danach mussten lediglich ein paar Variablen von unsigned nach signed geändert werden, um negative Werte korrekt verarbeiten zu können. Zusätzlich fehlte auf Python-Seite die Behandlung des Vorzeichen-Bits noch völlig.
Nach Einbau der Änderungen bin ich nun guter Hoffnung, um 19:00 Uhr die ersten 4,5 Stunden mit validen Daten zu bekommen, bei denen auch negative Temperatur-Werte dabei sind.

Wenn die gelbe Lebenszeichen-LED mit ihrem 220Ω Vorwiderstand für 16ms aufblinkt, ist das deutlich zu hell und nervt regelrecht. Vor dem nächsten Zuschrauben des Gehäuses sollte ich noch einen weiteren Vorwiderstand in Reihe schalten.

Statt eines weiteren Vorwiderstandes habe ich die Leuchtdauer der Lebenszeichen-LED jetzt auf 100µs oder auch 0.1ms geändert. Das langt, um auch bei Tageslicht noch gut erkennbar zu sein, wenn man bewusst hinschaut. Bei Zwielicht ist es schwach genug, damit man im Augenwinkel nicht ständig ein Blinken sieht.

Der erste Temperatur-Messwert nach Neustart der Schaltung ist gelegentlich deutlich falsch. Das könnte so zu deuten sein, dass dem DS18B20 in diesem Fall die o.g. 750ms Wartezeit fehlen. Daher wird jetzt in setup() je erkanntem Sensor ein leerer readDS18B20_CRC() aufgerufen, bevor die aktuelle Uhrzeit per WLAN geholt wird. Somit sollte nun hinreichend Zeit vergangen sein, bevor der erste echte Temperatur-Wert abgefragt wird.


Visualisierung der Daten

Im ersten Ansatz habe ich ein kleines Export-Script geschrieben, das die Daten von einem Sensor-Paar für einen Tag in eine Log-Datei schreibt, die von tun0graf verarbeitet werden kann. Weil tun0graf nicht darauf ausgelegt ist, negative Werte darzustellen, wurden die (blauen) Temperatur-Werte beim unteren Screenshot durch ein +10 in den positiven Bereich geholt. Weiterhin mussten beide Temperatur-Kurven etwas skaliert werden, um ein sinnvolles Gesamtbild zu ergeben. Folglich kann an den Bildern nur die Form der Kurven abgelesen werden - nicht deren absolute Werte.

Messwerte innen

Messwerte aussen

Irgendwann werde ich ein ähnliches Visualisierungs-Programm für die Temperatur- und Helligkeits-Daten bauen, das seine Daten direkt aus der Logger-Datenbank ausliest, mit mehr als einem Sensor-Paar umgehen kann und ggf. die Daten eines kompletten Jahres darstellt.


zusätzliche Prüfungen und Erweiterungen

Mittlerweile habe ich jedem Sensor-Datum noch ein Prüfbit zugefügt. Die Datenstruktur SensorData hatte noch zwei ungenutzte Bit, von denen eins nun als "bad-Flag" fungiert.
Per Default ist jedes Sensor-Datum bad. Erst wenn die Werte für Temperatur und Helligkeit eingetragen wurden und beim Temperatur-Wert außerdem noch die CRC-Prüfsumme gepasst hat, wird das "bad-Flag" zurückgesetzt.
Beim pro Minute gebildeten Mittelwert werden nur solche Werte berücksichtigt, die nicht bad sind. Und nur, wenn davon mindestens ein Wert nicht bad war, ist auch der Mittelwert nicht bad.
Das Python-Script schreibt ausschließlich Sensor-Daten in die Datenbank, bei denen das "bad-Flag" ausgeschaltet ist.

Die Funktion crc8() aus der OneWire-Library ist übrigens mit Vorsicht zu genießen. Trennt man nämlich die Verbindung am ATmega-Pin-D2 (die zum DQ-Pin vom DS18B20 geht), liefert der ds18b20.read() durchgängig den Wert 0x00.
Damit ist das CRC-Byte im sog. scratchpad ebenfalls 0x00. Ärgerlicherweise liefert der OneWire::crc8() für ein scratchpad, das komplett mit 0x00 gefüllt ist, auch den Wert 0x00.
Somit passt der berechnete CRC zum gespeicherten CRC und kennzeichnet auf diese Weise vermeintlich korrekt empfangene Daten.
Erst wenn das fünfte und/oder siebte Byte aus dem scratchpad in die Gültigkeitsprüfung mit einbezogen wird, kann man den CRC-Wert sinnvoll gebrauchen.
Die Byte 5 und 7 sind laut Doku (Figure 9. DS18B20 Memory Map) nämlich als "Reserved" gekennzeichnet und mit den Werten 0xFF und 0x10 vorbelegt.
Also sieht die Funktion jetzt so aus:
int readDS18B20_CRC(byte ROMcodeIdx) {
  static byte scratchpad[9];

  for(int i=0; i<3; i++) {  // maximal 3 Versuche, einen Wert mit korrekter CRC zu lesen
    ds18b20.reset();
    ds18b20.select(ROMcode_g[ROMcodeIdx]);
    ds18b20.write(0x44);  // This command initiates a single temperature conversion
    ds18b20.reset();
    ds18b20.select(ROMcode_g[ROMcodeIdx]);
    ds18b20.write(0xBE);  // Read Scratchpad
    for(int j=0; j<9; j++) {
      scratchpad[j]=ds18b20.read();
    }
    if(scratchpad[5]==0xFF && scratchpad[7]==0x10 && OneWire::crc8(scratchpad, 8)==scratchpad[8]) {
      // BIT    15 14 1  1  11 10  09  08  07  06  05  04   03   02   01   00
      //        S  S  S  S  S  2^6 2^5 2^4 2^3 2^2 2^1 2^0 2^-1 2^-2 2^-3 2^-4
      return((scratchpad[1]<<8)|scratchpad[0]);
    }
  }
  return(0x8FFF); // Fehlerkennung (0x8FFF kann nicht als Temperatur-Wert vorkommen)
}

Weiterhin wird neuerdings nach einem Sonder-Sync die Restzeit der aktuellen Minute anhand der RTC neu berechnet. Dadurch wird erreicht, dass alle Messwerte selbst dann zu der Minute abgelegt werden, in der sie erfasst wurden, wenn in der Minute für die Dauer von ein oder zwei Tiefschlaf-Phasen zusätzliche Arbeit angefallen ist.

Beim Senden der Sensor-Daten werden nun auch Daten der aktuell laufenden Stunde aus dem RAM übermittelt.
Dadurch erhält man bei einem Sonder-Sync zum einen die bisher erfassten Daten der aktuell laufenden Stunde auf Python-Seite. Zum anderen verliert man  bei einem Standort-Wechsel nicht die in der aktuell laufenden Stunde bereits erfassten Daten bzw. muss nicht den Beginn einer neuen Stunde abpassen, um möglichst wenig Daten zu verlieren.
Dank des "bad-Flags" können einfach alle 60 Werte pro Sensor-Paar gesendet werden. Auf Python-Seite werden nur die Werte verarbeitet bzw. in die Datenbank geschrieben, die bereits valide Werte enthalten.

Hier der aktuelle Stand von ATmega328-Firmware und vom Python-SocketServer.


USB-Powerbank als Stromversorgung

Bei der letzten Amazon-Bestellung habe ich u.a. diese USB-Powerbank in meinen Warenkorb gelegt. Laut Kunden-Infos lässt sie sich laden, während sie gleichzeitig lädt. Aber leider schaltet sie sich nach kurzer Zeit ab, wenn das zu ladende Gerät nicht genug Strom zieht.
Meine älteste USB-Powerbank hat das selbe Problem.
Die RaspPi3-Stromversorgung für das Wardriving war erstens unverhältnismäßig teuer für diesen Zweck, zweitens schaltet auch sie sich nach kurzer Zeit ab.
Die auf den ersten Blick tauglichste USB-Powerbank hat Sohn #1 bei Saturn gekauft. Ich habe sie ihm kurzerhand gegen meine älteste Powerbank getauscht. Jedoch leider .... regelmäßig nach ca. 10 bis 15 Minuten macht die Schaltung einen Reset, wenn sie darüber versorgt wird. Vielleicht könnte man das Ding ja mit einem richtig dicken Kondensator austricksen, aber irgendwie habe ich keine Lust auf diese Art von Hacks.

Eigentlich fand ich die Idee mit der USB-Powerbank als Stromversorgung ja eine tolle Idee. Aber offensichtlich stellt es ein größeres Problem dar, ein Modell zu finden, dass
- sich nachladen lässt, während es selbst gerade Strom liefert,
- keine ständig leuchtenden LEDs hat,
- konstant 5V liefert, auch wenn nur minimaler Strom entnommen wird und
- nicht soviel kostet, wie mehrere 4er-Packs Mignon-Akkus samt Batteriehalter.

Notgedrungen bleibt es daher erstmal bei den Mignon-Akkus.

Weiter gehts auf Seite 5.