home 🌡

PoE Thermolog mit ESP32 und RTC

reinhard@finalmedia.de (Do 28. Nov 20:42:27 CET 2019)

Was ist das hier?

PoE Thermolog ist ein preiswertes DIY Netzwerk-Thermometer, welches über TCP IPv4 via http ausgelesen werden kann und dabei via PoE IEEE 802.3af/at/bt kompatibel bestromt wird. Ich verwende dazu ein Olimex ESP32-POE Board und ein via I²C verbundenes DS3221 Modul. Das DS3221 ist eigentlich nur ein RTC Modul, das uns eine sehr genaue Uhrzeit liefert und diese auch batteriegepuffert vorhält. Das Modul verwendet dafür bereits einen integrierten Temperatursensor, um die Zeitdrift zu kompensieren. Das machen wir uns zu nutze, denn wir können auch diesen reinen Temperatur-Messwert auslesen und weiterverarbeiten. Die RTC ist für uns also nur nettes Nebenprodukt - primär geht es uns um die genaue Temperaturmessung des Sensors.

Wir stellen den ausgelesenen Messwert auf Abruf via http zur Verfügung. Zudem protokollieren wir in der Software bereits die Minima und Maxima und liefern auch stets einen aktuellen Zeitstempel mit. Zudem liefert das PoE Thermolog auf Wunsch bereits auch eine kleine grafische Aufbereitung als Thermometer aus. Desweiteren beschreibe ich hier, wie man die Messwerte serverseitig in einer RRD Datenbank archiviert und bequem als Graphen aufbereiten kann.

Download aller Dateien inkl. meiner Firmware hier als tarball poetherm.tgz

Die Hardware

Du benötigst folgendes:

Pinbelegung

+++ ESP32 Board +++

EXT1            EXT2

1               1
2 +3.3V         2
3 GND           3
4               4
5               5
6               6
7               7 i2c SCL
8               8
9               9
10              10 i2c SDA


+++ RTC DS3231 Modul +++

+ 3.3V
D i2c SDA
C i2c SCL
V
- GND

Die Software

Du kannst die Firmware in Arduino oder hier mit dem Quellcode poetherm_0.24.cpp und Makefile selbst bauen.
xtensa-esp32-elf-g++
xtensa-esp32-elf-gcc
xtensa-esp32-elf-ar
xtensa-esp32-elf-size
make
wirst du dazu benötigen. Alternativ kannst du nachfolgende Binaries verwenden und mit dem nächsten Schritt fortfahren.

1. Firmware auf ESP32 flashen

ESP32 via USB an Rechner anschließen.

Ganz wichtig: 802.3af/at/bt Ethernet und USB darf nicht gleichzeitig angeschlossen sein!

Web Oberfläche des PoE Thermolog

Die Weboberfläche ist sehr minimalistisch gehalten und bietet nur die Möglichkeit aktuelle Werte zu lesen. Das Setzen von Werten oder eine Konfiguration wurde bewusst nicht ermöglicht, um den Angriffsvektor gering zu halten.

Der Aufruf von http://ip_adresse_des_poetherm liefert diese Link-Übersicht und einen Bericht über die Firmwareversion, sowie die uptime.

Der Menüpunkt "gfx", also der Aufruf von http://ip_addresse_des_poetherm/gfx , liefert dann eine live veränderte SVG Grafik mit dem aktuellen Temperaturwert, sowie min und max. Darunter wird im html Text die aktuelle Temperatur, sowie der Zeitstempel im Format YYYYmmDDHHMMSS angezeigt. Die Seite läd dabei automatisch alls 30 Sekunden neu:

Der Menüpunkt "all", also der Aufruf von http://ip_adresse_des_poetherm/all , liefert eine einzelne Text-Zeile, z.B. mit diesen Werten:

Die Felder sind durch Leerzeichen separiert

Das Poetherm protokolliert dabei intern die Minima und Maxima nur über den letzten Power-Zyklus. Verliert es also die Stromversorgung, werden diese Minima und Maxima genullt.

Um nun die Messwerte des PoE Thermologs periodisch auszulesen und auf einem Server zu archivieren, sowie Graphen zu erzeugen und auch über Power-Cycles hinweg verlustfrei und über Jahre hinweg Minima und Maxima zu protokollieren, setze ich nun eine oder mehrere Linux-Server voraus, die diese Daten abholen. Dazu nun mehr...

Wir werden dazu http://ip_adresse_des_poetherm/temp nutzen und somit nur den aktuellen Temperaturwert auslesen und damit serverseitig eine roundrobin Datenbank füttern (rrd).

Begriffserklärung: Eine Round-Robin-Datenbank ist als ein Ringpuffer zu verstehen. Sie hat volle Schärfe für aktuellste Daten. Also z.B. mit Minutenauflösung für unsere erfassten Messwerte. Die älteren Datenbestände hingegen, also Wochen, Monate, Jahre etc. werden hingegen stets unschärfer und nur noch als Mittelwert dann über Stunden etc. vorgehalten - hier werden also ab einem gewissen Alter des Messwerte nur bereits aggregierte/geraffte Mittelwerte in der Datenbank vorgehalten, die für eine Auswertung vollkommen ausreichend sind. Damit wächst die Datenbank nicht aufs Unermessliche an, sondern hat einen konstanten maximalen Speicherbedarf, jeweils über den im Vorfeld bei der Erzeugung der Datenbank definierten benötigten Genauigkeitsgrad im zeitlichen Verlauf. Zudem bietet RRD eine extrem schnelle Einfüge-Rate (Insert-Rate) mit mehreren Millionen Samples auf einem einzigen Host, sowie extrem schneller Zugriff auf die voraggregierte Langezeitstatistik (Anfrage über monatliche Durchschnittswerte über zehntausende Geräte und 10 Jahre wird in weniger als 1 Sekunde prozessiert und beantwortet. Auf https://de.wikipedia.org/wiki/RRDtool und https://de.wikipedia.org/wiki/Data_Stream_Management_System findest du dazu weitere Hintergrundinformationen.)

2. Weitere Pakete auf deinem Server installieren

apt-get update
apt-get install curl rrdtool netpbm

3. Minütlichen Cronjob auf deinem Server anlegen

Die Variablen in nachfolgendem Script anpassen und als root ausführen, um den Cronjob abzulegen.

Du musst inbesondere die Variable host anpassen und dort die IP-Adresse deines poetherm hinterlegen!

#!/bin/bash
# reinhard@finalmedia.de
# Do 28. Nov 21:29:36 CET 2019

# Systemnutzer (default: nobody 65534)
# Dieser User muss dann auch natürlich Schreibzugriff
# auf das Verzeichnis haben, in welchem du Datenbank
# und Graph-Datei anlegen willst.

username="nobody"

# Datenbank-Datei definieren
# Hier im Beispiel an einem volatilen Ort. Für den
# Produktivbetrieb solltest du natürlich ein Ziel wählen,
# an dem du die Datenbank dauerhaft ablegen und somit
# stetig erweitern kannst. 

database="/tmp/temperature.rrd"

# Graph-Datei definieren
# Diese PNG Grafik wird die Temperatur Diagramme enthalten

png="/tmp/temperature.png"

# IP-Adresse des ESP32 Boards (Musst Du auf richtige Adresse/Hostname ändern!)
# Tipp: Benutze hier einen Hostnamen und nenne ggf. in deiner /etc/hosts die
# zugehörige IP. Dann wird später im Graph auch der Hostname, statt der 
# IP-Adresse angezeigt. Die IP-Adresse hast du z.B. in deinem Router als
# static lease für das Board definiert.

host="192.168.0.44"

# Jetzt Cronjob erzeugen
echo "* * * * * $username /usr/bin/poetherm.sh \
\"$host\" \"$database\" > \"$png\"" | tee -a /etc/crontab

Damit hast du deiner crontab eine neue Zeile hinzugefügt. Diese sieht (je nach gewählten Werten) z.B. so aus:

* * * * * user /usr/bin/poetherm.sh temp01 /home/user/temp01.rrd > /home/user/temp01.png

4. Das Script /usr/bin/poetherm.sh installieren

Das Script dient zum Erzeugen der Round Robin Datenbank, Abholen der Daten und Erzeugen eines Graphen

Schnellinstallation

Wenn du keine Details wissen willst, kannst du als root das Script einfach wie folgt installieren:

base64 -d << @@@|zcat> /usr/bin/poetherm.sh && chmod +x /usr/bin/poetherm.sh
H4sIAJxv5V0CA72T30/bMBDH3/NXHCYDqs35RcvAGmjdKIhpdBNjbA97cZNrEzWxI8ehA+2P3zkt
BSS0t/Up9d35/P3cfbu9FU4KFU5kk3vboGSFAsK2MV201mhzNFXQJWVrc20EGCxULk32flooWVaY
FTLIkAoyaen2qYbkMICxvoUkFsmR6O/Dx9E1JFF85HkWGwv+NvDSQgI7O/DLwzTXwD5g0dQFlrKd
mnYqwI8gPkqC+OAwiIJ+n+pCW9WhxapGI21rMDAmg5OHhL5Fc1vgIqjVjMHJTtccfxcWYnq1sCUe
s+v1ZeaRWEnUFPUT5uW6sfQrZh5xnBKHmkg1h0J1hwLQ3GM7Q/UGFqgUKJ3moIo0t+6JhsrtkmwL
+BSY/9CcLQlJqNW6hNQgdYN1GjinuzUcRFR0+k04OHE+/H4+EgdRJHjydl8Moshlr66GYngzuhpS
LgoGIhZxsk5cXoyXwUQk/cfo8OcL0WdNVplOJG1h4cgmOrsTSs+MbuunKG4yw7ltsSwRHie5nBbI
Sa5L+rYqezK/tnaeUOsBLM/PBjQW/l7ampJmkWqlMLXcFhXq1sKAVPEGWG5tLcLQd0vqDMAgOQkz
vA1VW5bwh8qsAZ6lwCJ+FDCK5Cgz4Ckc9HtO902BBs6NrHOS+EXx6xz5WXm3XmsnW5YNkLWdk+lz
X8wQvo7PVwYgd9liLkuotLLUjaBqVaXSArcTEvBu73HNM/cQdAIbmzkSLrtWnK+M6HdfRvfY6eis
23t8vB7K0gerRXVFny/Go3hZtj2dkiMi0U3DoZLfrSYtVNcDJ6Ox0jgz73Wz5hmwUpI1F4hzBq93
XzW7Peahyp6VKL14TP4DhNbhdw8w4G6R1McpfBkQ9n7QHwV7mwV1G8o3TnqplbQbJr1DaTYO+knm
ZnOcfNBRNhvHHIAD/S/edcfKHWee9xfCSqSlgAcAAA==
@@@

Du bist dann fertig und kannst den Befehl "poetherm.sh" verwenden.

Hinweis: Der Cronjob muss dann schon einige Zeit laufen, bevor du genügend Daten in deiner Datenbank hast, sodass im Graph auch etwas sichtbar wird.

5. Detaillierte Erklärung des Scripts

Lass den Cronjob also mal eine zeitlang Daten sammeln. In der Zwischenzeit kannst du dir durchlesen, was das Script genau tut:

Datei /usr/bin/poetherm.sh beinhaltet das nachfolgende Script.

#!/bin/bash
# name: /usr/bin/poetherm.sh
# author: reinhard@finalmedia.de
# date: Do 28. Nov 21:29:43 CET 2019

test $# -lt 2 && \
echo "Beispielaufruf: $0 192.168.0.44 \
/tmp/temperature.rrd > \
/tmp/overview.png" >&2 && exit 1

title="Temperatur"
database="$2"
host="$1"

# Datenbank in Datei erzeugen, wenn noch nicht existent
test ! -f "$database" && \
rrdtool create $database --step 60 \
DS:temp:GAUGE:600:-273:5000 \
RRA:AVERAGE:0.5:1:1200 \
RRA:MIN:0.5:12:2400 \
RRA:MAX:0.5:12:2400 \
RRA:AVERAGE:0.5:12:2400 && \
chown nobody:nogroup "$database"

# Aktuelle Temperatur Daten abholen und Datenbank updaten
rrdtool update "$database" N:$(curl --connect-timeout 5 \
-s "http://$host/temp" 2>/dev/null | \
tr -dc "0-9." | head -c 64)

# Vier Graphen On-The-Fly erzeugen und als eine
# einzige PNG Datei vertikal montieren
pnmcat -tb \
<(\
rrdtool graph /dev/stdout -a PNG --title="$title" \
"DEF:temp1=$database:temp:AVERAGE" \
"LINE1:temp1#ff0000:$host" | pngtopnm \
) <(\
start="$(date -d "last week" +'%s')"
end="$(date -d "now" +'%s')"
rrdtool graph /dev/stdout -s "$start" -e "$end" \
-a PNG --title="$title (Woche)" \
"DEF:temp1=$database:temp:AVERAGE" \
"LINE1:temp1#ff0000:$host" | pngtopnm \
) <(\
start="$(date -d "last month" +'%s')"
end="$(date -d "now" +'%s')"
rrdtool graph /dev/stdout -s "$start" -e "$end" \
-a PNG --title="$title (Monat)" \
"DEF:temp1=$database:temp:AVERAGE" \
"LINE1:temp1#ff0000:$host" | pngtopnm \
) <(\
start="$(date -d "last year" +'%s')"
end="$(date -d "now" +'%s')"
rrdtool graph /dev/stdout -s "$start" -e "$end" \
-a PNG --title="$title (Jahr)" \
"DEF:temp1=$database:temp:AVERAGE" \
"LINE1:temp1#ff0000:$host" | pngtopnm \
) <(\
start="$(date -d "-5 years" +'%s')"
end="$(date -d "now" +'%s')"
rrdtool graph /dev/stdout -s "$start" -e "$end" \
-a PNG --title="$title (5 Jahre)" \
"DEF:temp1=$database:temp:AVERAGE" \
"LINE1:temp1#ff0000:$host" | pngtopnm \
) | pnmtopng

Das erzeugt dann z.b. solche schönen Graphen in einer PNG

Man kann die Werte aus der Datenbank jedoch auch auslesen und anderweitig verarbeiten. Siehe dazu auch die Doku von rrdtool.

rrdtool dump /tmp/temperature.rrd
rrdtool dump /tmp/temperature.rrd | grep row | cut -d / -f2 | tr -dc "0-9.e+ \n"

Alternativ

Hier noch eine minimalistische Alternative, um ohne rrd eine geschützte Datenbank als CSV Datei zu erzeugen, die dabei so definiert wird, dass Einträge nur angehängt aber nicht mehr nachträglich verändert werden können. Der periodische Abruf ist via daemontools, statt via cron realisiert. Es erfolgt keine grafische Auswertung, sondern dieser Dienst ist nur als ewiges Logbuch zu verstehen.


apt-get install daemontools daemontools-run
mkdir -p /srv/poetherm_csv

Dann ebenso als root die Datei /srv/thermolog/run erzeugen und dort dieses Script hinterlegen:


#!/bin/sh
# So 19. Dez 12:20:56 CET 2021
# reinhard@finalmedia.de

export poetherm_ip="192.168.0.14"
export database="data.csv"

# wir rufen alle 10 Sekunden einen Messwert ab
sleep 10

# wenn data file noch nicht existiert, dann erzeugen und
# im dateisystem so definieren, dass daten kuenftig
# nur angehaengt werden duerfen, aber kein replace
# oder bearbeiten moeglich ist.
test ! -f ${database} && touch ${database} && chattr +a ${database}

(
# hash aller vorherigen eintraege
sha256sum "${database}" | \
cut -d " " -f1 | tr -d "\n";  echo -n " ";
# aktuelle daten vom thermolog abholen
timeout 10 setuidgid nobody curl -s "http://${poetherm_ip}/all" | tr -d "\n"
echo
) | \
head -n 1 | head -c 512 | tr -dc "0-9a-f@. \n" | \
tai64n | tr " " "," | tee -a "${database}"

Dann das Script ausführbar machen und mit den daemontools als Dienst starten:


chmod +x /srv/poetherm_csv/run
ln -s /srv/poetherm_csv/ /etc/service/

Die hashes des ewigen logfiles sind dann z.B. mit diesem Script prüfbar:


#!/bin/sh
# dieses script heisst check_hashes.sh
# reinhard@finalmedia.de
# So 19. Dez 14:12:31 CET 2021
export database="data.csv"
test -r "${database}" || exit 1
n=0
maxlines=$(wc -l "${database}" | cut -d ' ' -f1)
while test "$n" -lt "$maxlines"
do
n=$((n+1))
head -n $n "${database}" | tail -n +$n | cut -d "," -f2-
head -n $n "${database}" | sha256sum
done

Das obige Script check_hashes.sh kann dann wie folgt verwendet werden. Ist die Ausgabe 0, dann ist die Kette sauber.


./check_hashes.sh | cut -c 1-64 | tail -n +2 | head -n -1 | uniq -u | wc -l