Data z databáze zpracujeme, zobrazíme a analyzujeme
3. Ukládání měření do databáze
Pro sběr měřených dat je nejvhodnější databáze časových řad (TSDB – Time Series Database), protože je přizpůsobena pro optimální uložení velkého množství dat v časové řadě a pro další zpracování disponuje optimalizovanými agregačními funkcemi. Velmi často používanou databází je InfluxDB, která je zdarma i s užitečnými nástroji ke zpracování dat a díky připraveným knihovnám se s ní snadno pracuje v různých programovacích jazycích.
3.1 Instalace vlastní InfluxDB
Databázový stroj InfluxDB můžeme v oblíbeném operačním systému Debian GNU/Linux či Ubuntu/Mint Linux jednoduše nainstalovat ze standardního instalačního balíku influxdb. Případně na jiných platformách podle návodu k instalaci na webu influxdata.com.
Předpokladem je, že máme k dispozici hodně úložného prostoru, protože data mohou časem narůst do obřích rozměrů.
$ apt install influxdb
Nepotřebujeme-li zaznamenávat dotazy a přístupy přes HTTP, tak můžeme vypnout logování v konfiguračním souboru /etc/influxdb/influxdb.conf.
InfluxDB lze používat také v cloudu a zde se budeme věnovat platformě InfluxDB Cloud 2.0, která nabízí provoz připraveného databázového stroje v cloudu AWS (v Evropě Frankfurt n.M.), v Azure (v Evropě Amsterdam) nebo v Google Cloud (v Evropě Belgie).
Varianta Free je k dispozici všem zdarma s několika omezeními: zápis 5 MB za 5 vteřin, dotazy 300 MB za 5 minut, 10000 řad dat a maximálně 2 databáze (Buckets) se zachováním dat 30 dní. Pro nenáročné domácí použití naprosto dostačující.
Pro další práci s databází jsou klíčové následující kroky s poznamenáním údajů potřebných k přístupu k databázi:
Program Telegraf obsahuje modul MQTT Consumer, který pracuje jako MQTT klient zapisuje odebírané zprávy do InfluxDB. V konfiguračním souboru /etc/telegraf/telegraf.conf stačí nastavit jen pár údajů nebo si vygenerovat potřebnou konfiguraci v administraci InfluxDB Cloud.
Do souboru /etc/default/telegraf vložíme token pro přístup k databázi InfluxDB v podobě nastavení proměnné prostředí:
INFLUX_TOKEN=CkJhYmnFoWUgZG8ga2/FoWUuCgo=
Definice toku dat ve vývojovém nástroji Node-RED – tuto variantu popíšu podrobněji níže.
3.4 Node-RED jako MQTT klient zapisující data do InfluxDB
Můžeme použít vlastní instalaci prostředí Node-RED na počítači nebo v cloudu. Nejzajímavější je spuštění v IBM Cloud v rámci free tier, kde lze celé používat zdarma (stačí úroveň Lite). Instalaci provedeme po počáteční registraci podle návodu. Během instalace se pro uložení dat programu vytvoří databáze Cloudant, spustí instance aplikace Node-RED a nakonfigurují nějaké další služby potřebné pro běh prostředí.
Po přihlášení do editoru Node-RED nastavíme:
Menu / Manage palette / Install – do vyhledávacího pole zadáme influxdb a nainstalujeme doplněk node-red-contrib-stackhero-influxdb-v2
Založíme a přejmenujeme nové Flow
Vytvoříme node typu „mqtt-in“ a nastavíme:
Server (Connection – hostname, port, Use TLS; Security – uživatelské jméno a heslo)
Topic
Output – a parsed JSON object
Vytvoříme node typu „function“, pojmenujeme „toFloat“ a na záložce onMessage zadáme kód:
Jak načíst hodnoty aktuální teploty a vlhkosti z bezdrátového teploměru Xiaomi Temperature and Humidity Monitor 2 nebo podobného přes Bluetooth v TV boxu se systémem CoreELEC.
Proč zrovna v TV boxu? Protože bych rád údaje o teplotě dále zpracovával do přehledných statistik a grafů, a k tomu je zapotřebí trvale běžící zařízení, které bude naměřené údaje pravidelně přijímat, a posílat k dalšímu zpracování na MQTT broker nebo do databáze. Mobilní telefon nebo obecně Android se na to moc nehodí a jiné vhodné zařízení kompatibilní s Bluetooth 4.2 Low Energy (BLE) nemám k dispozici.
Prerekvizity
Co všechno budeme potřebovat:
Zařízení s přijímačem Bluetooth minimálně 4.2,
nainstalovaný systém CoreELEC s volným místem na paměťovém úložišti, se zapnutým SSH přístupem a s funkčním připojením k Internetu (na podobném systému LibreELEC možná bude fungovat také),
SSH terminál,
bezdrátový teploměr Xiaomi LYWSD03MMC nebo podobný s komunikačním modulem Bluetooth.
Jak zjistit, jakou verzi Bluetooth podporuje zařízení, kde chceme teplotu načítat z teploměru:
Entware je repozitář balíčků z kterého lze nainstalovat mnoho programů na jakékoliv podporované zařízení s operačním systémem založeným na Linuxu – třeba na NAS nebo WiFI router s DD-WRT. Na zařízení s CoreELEC se základní instalace Entware spustí z příkazového řádku vestavěným skriptem installentware a po restartu máme k dispozici program opkg, kterým můžeme instalovat další balíčky s programy.
$ installentware
Entware is already installed.
Příprava jazyka Python
Začneme přípravou virtuálního prostředí jazyka Python, abychom měli všechny moduly soustředěné na jednom místě a změnami neovlivnili zbytek systému.
Alternativně je možno nainstalovat novější verzi Python 3.9 z Entware. S výchozím Pythonem 3.7 v CoreELEC jsem měl potíže při výpočtech s desetinnými čísly, kdy výsledkem dělení 2347/100.0 nebylo hezkých 23.47, ale výsledek byl ošklivých 23.469999999999999, což se nepříjemně projevuje v zobrazení naměřených teplot.
Tímto máme připravené virtuální prostředí Pythonu pro další pokračování instalace.
Instalace programu lywsd03mmc
(Účelem tohoto článku není popsat jak to funguje, ale naopak zdokumentovat, co přesně nefunguje a proč, takže se předem omlouvám všem netrpělivým, že na to jdu od lesa.)
K načtení hodnot z teploměru jsem zvolil skript lywsd03mmc, který splňuje všechny požadavky. Má možnost jednorázového načtení teploty a vlhkosti nebo přečtení a uložení celé historie teplot od zapnutí teploměru do souboru CSV.
Začneme dle dokumentace s instalací vyžadované knihovny glib2 pomocí programu opkg a program pip3 slouží obecně k instalaci skriptů a modulů Pythonu.
(mitemp) $ opkg install glib2
Installing glib2 (2.68.1-3) to root...
Downloading http://bin.entware.net/aarch64-k3.10/glib2_2.68.1-3_aarch64-3.10.ipk
Installing libiconv-full (1.16-1) to root...
Downloading http://bin.entware.net/aarch64-k3.10/libiconv-full_1.16-1_aarch64-3.10.ipk
Installing libintl-full (0.21-2) to root...
Downloading http://bin.entware.net/aarch64-k3.10/libintl-full_0.21-2_aarch64-3.10.ipk
Installing zlib (1.2.11-3) to root...
Downloading http://bin.entware.net/aarch64-k3.10/zlib_1.2.11-3_aarch64-3.10.ipk
Installing libffi (3.3-2) to root...
Downloading http://bin.entware.net/aarch64-k3.10/libffi_3.3-2_aarch64-3.10.ipk
Installing libattr (2.5.1-3) to root...
Downloading http://bin.entware.net/aarch64-k3.10/libattr_2.5.1-3_aarch64-3.10.ipk
Configuring libiconv-full.
Configuring libintl-full.
Configuring zlib.
Configuring libffi.
Configuring libattr.
Configuring glib2.
(mitemp) $ pip3 install lywsd03mmc
Collecting lywsd03mmc
Using cached lywsd03mmc-0.1.0-py3-none-any.whl (6.4 kB)
Collecting lywsd02==0.0.9
Using cached lywsd02-0.0.9-py3-none-any.whl (4.3 kB)
Collecting bluepy==1.3.0
Using cached bluepy-1.3.0.tar.gz (217 kB)
Using legacy 'setup.py install' for bluepy, since package 'wheel' is not installed.
Installing collected packages: bluepy, lywsd02, lywsd03mmc
Running setup.py install for bluepy ... error
ERROR: Command errored out with exit status 1:
command: /storage/.venv/mitemp/bin/python3 -u -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/storage/.opt/tmp/pip-install-09_n_amk/bluepy_f3934a7a656342c1a87ef752f6bc3e9b/setup.py'"'"'; __file__='"'"'/storage/.opt/tmp/pip-install-09_n_amk/bluepy_f3934a7a656342c1a87ef752f6bc3e9b/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(__file__) if os.path.exists(__file__) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /storage/.opt/tmp/pip-record-wbxlv4uq/install-record.txt --single-version-externally-managed --compile --install-headers /storage/.venv/mitemp/include/site/python3.7/bluepy
cwd: /storage/.opt/tmp/pip-install-09_n_amk/bluepy_f3934a7a656342c1a87ef752f6bc3e9b/
Complete output (6 lines):
running install
running build
running build_py
Working dir is /storage/.opt/tmp/pip-install-09_n_amk/bluepy_f3934a7a656342c1a87ef752f6bc3e9b
execute make -C ./bluepy clean
error: [Errno 2] No such file or directory: 'make': 'make'
----------------------------------------
ERROR: Command errored out with exit status 1: /storage/.venv/mitemp/bin/python3 -u -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/storage/.opt/tmp/pip-install-09_n_amk/bluepy_f3934a7a656342c1a87ef752f6bc3e9b/setup.py'"'"'; __file__='"'"'/storage/.opt/tmp/pip-install-09_n_amk/bluepy_f3934a7a656342c1a87ef752f6bc3e9b/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(__file__) if os.path.exists(__file__) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /storage/.opt/tmp/pip-record-wbxlv4uq/install-record.txt --single-version-externally-managed --compile --install-headers /storage/.venv/mitemp/include/site/python3.7/bluepy Check the logs for full command output.
Instalace závislosti – pythonovského modulu bluepy – zhavarovala, protože není nainstalovaný program make, a proto ho nainstalujeme společně s překladačem a dalšími programy, které budou také potřeba pro překlad modulu z jazyka C.
(mitemp) $ opkg install make gcc
Installing make (4.3-1) to root...
Downloading http://bin.entware.net/aarch64-k3.10/make_4.3-1_aarch64-3.10.ipk
Installing gcc (7.4.0-6a) to root...
Downloading http://bin.entware.net/aarch64-k3.10/gcc_7.4.0-6a_aarch64-3.10.ipk
Installing libbfd (2.35.2-1) to root...
Downloading http://bin.entware.net/aarch64-k3.10/libbfd_2.35.2-1_aarch64-3.10.ipk
Installing libopcodes (2.35.2-1) to root...
Downloading http://bin.entware.net/aarch64-k3.10/libopcodes_2.35.2-1_aarch64-3.10.ipk
Installing libctf (2.35.2-1) to root...
Downloading http://bin.entware.net/aarch64-k3.10/libctf_2.35.2-1_aarch64-3.10.ipk
Installing objdump (2.35.2-1) to root...
Downloading http://bin.entware.net/aarch64-k3.10/objdump_2.35.2-1_aarch64-3.10.ipk
Installing ar (2.35.2-1) to root...
Downloading http://bin.entware.net/aarch64-k3.10/ar_2.35.2-1_aarch64-3.10.ipk
Installing binutils (2.35.2-1) to root...
Downloading http://bin.entware.net/aarch64-k3.10/binutils_2.35.2-1_aarch64-3.10.ipk
Configuring libbfd.
Configuring libopcodes.
Configuring libctf.
Configuring objdump.
Configuring ar.
Configuring binutils.
Configuring gcc.
There are no *-dev packages in Entware(with few exceptions)!
Please install headers as described in the wiki:
https://github.com/Entware/Entware/wiki
Configuring make.
Na konci jsme vyzváni k instalaci hlavičkových souborů podle popisu v dokumentaci. Ukázkový příkaz z popisu pro stažení a rozbalení hlavičkových souborů do adresáře /opt/include je pro potřeby CoreELEC zapotřebí upravit na magický příkaz:
(mitemp) $ pip3 install lywsd03mmc
...
bluepy-helper.c:33:10: fatal error: glib.h: No such file or directory
#include <glib.h>
^~~~~~~~
compilation terminated.
./bluez-5.47/attrib/att.c:33:10: fatal error: glib.h: No such file or directory
#include <glib.h>
^~~~~~~~
compilation terminated.
./bluez-5.47/attrib/gatt.c:32:10: fatal error: glib.h: No such file or directory
#include <glib.h>
^~~~~~~~
compilation terminated.
./bluez-5.47/attrib/gattrib.c:34:10: fatal error: glib.h: No such file or directory
#include <glib.h>
^~~~~~~~
compilation terminated.
./bluez-5.47/attrib/utils.c:30:10: fatal error: glib.h: No such file or directory
#include <glib.h>
^~~~~~~~
compilation terminated.
./bluez-5.47/btio/btio.c:37:10: fatal error: glib.h: No such file or directory
#include <glib.h>
^~~~~~~~
compilation terminated.
./bluez-5.47/src/log.c:38:10: fatal error: glib.h: No such file or directory
#include <glib.h>
^~~~~~~~
compilation terminated.
./bluez-5.47/src/shared/io-glib.c:30:10: fatal error: glib.h: No such file or directory
#include <glib.h>
^~~~~~~~
compilation terminated.
./bluez-5.47/src/shared/timeout-glib.c:22:10: fatal error: glib.h: No such file or directory
#include <glib.h>
^~~~~~~~
compilation terminated.
...
Další pokus o instalaci modulu bluepy opět selhal, protože tentokrát nebyl nalezen hlavičkový soubor glib.h. Ten totiž není přímo v adresáři /opt/include, ale je v podadresáři:
Nyní se tedy vrátíme ke konfiguraci prostředí překladače a vytvoříme soubor gcc_env.sh s proměnnými definujícími, kde se má hledat hlavičkový soubor a knihovna glib-2.0. Následně vytvořený soubor použijeme před instalací nového skriptu. (Další možností by byla instalace balíku pkg-config z Entware, ale ten by vyžadoval ještě další konfiguraci, a proto jsem se touto cestou nevydal.)
(mitemp) $ cat > gcc_env.sh <<'END'
PREFIX=/opt
LIBDIR="${PREFIX}/lib"
INCLUDEDIR="${PREFIX}/include"
export LDFLAGS="-Wl,-rpath=${LIBDIR} -Wl,--dynamic-linker=${LIBDIR}/ld-linux.so.3 -L${LIBDIR} "
export CFLAGS="-O2 -pipe -march=native -fno-caller-saves "
export CPPFLAGS="${CFLAGS} -I${INCLUDEDIR} -I${INCLUDEDIR}/glib-2.0 -lglib-2.0 "
export CXXFLAGS="${CFLAGS} ${LDFLAGS} "
END
(mitemp) $ source ./gcc_env.sh && pip3 install lywsd03mmc
Collecting lywsd03mmc
Using cached lywsd03mmc-0.1.0-py3-none-any.whl (6.4 kB)
Collecting lywsd02==0.0.9
Using cached lywsd02-0.0.9-py3-none-any.whl (4.3 kB)
Collecting bluepy==1.3.0
Using cached bluepy-1.3.0.tar.gz (217 kB)
Using legacy 'setup.py install' for bluepy, since package 'wheel' is not installed.
Installing collected packages: bluepy, lywsd02, lywsd03mmc
Running setup.py install for bluepy ... done
Successfully installed bluepy-1.3.0 lywsd02-0.0.9 lywsd03mmc-0.1.0
Nyní je skript lywsd03mmc se všemi potřebnými moduly úspěšně nainstalovaný a můžeme ho vyzkoušet k načtení hodnot z teploměru s MAC adresou, kterou jsme zjistili na začátku tohoto článku.
(mitemp) $ lywsd03mmc A4:C1:38:98:76:54
Fetching data from A4:C1:38:98:76:54
Temperature: 24.36°C
Humidity: 61%
Battery: 89%
Nebo můžeme uložit historii teplot od zapnutí teploměru do CSV souboru k dalšímu zpracování.
(mitemp) $ lywsd03mmc2csv A4:C1:38:98:76:54 --output data.csv
Fetching data from A4:C1:38:98:76:54
Temperature: 24.36°C
Humidity: 61%
Battery: 89%
Device start time: 2021-07-22 18:39:52.948031
Fetching history from A4:C1:38:98:76:54
2021-07-22 19:39:52.948031: 24.1 to 26.8
2021-07-22 20:39:52.948031: 24.1 to 24.2
2021-07-22 21:39:52.948031: 23.7 to 24.1
2021-07-22 22:39:52.948031: 23.4 to 23.7
2021-07-22 23:39:52.948031: 23.3 to 23.8
...
Done