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:
$ hciconfig -a
hci0: Type: Primary Bus: UART
BD Address: 12:34:56:78:9A:BC ACL MTU: 1021:5 SCO MTU: 255:11
UP RUNNING PSCAN
RX bytes:2504 acl:4 sco:0 events:95 errors:0
TX bytes:4463 acl:6 sco:0 commands:65 errors:0
Features: 0xff 0xff 0xff 0xfe 0xdb 0xfd 0x7b 0x87
Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
Link policy: RSWITCH HOLD SNIFF PARK
Link mode: SLAVE ACCEPT
Name: 'tv-box-coreelec-9.2'
Class: 0x0c0000
Service Classes: Rendering, Capturing
Device Class: Miscellaneous,
HCI Version: (0xa) Revision: 0x599
LMP Version: (0xa) Subversion: 0xd54
Manufacturer: Realtek Semiconductor Corporation (93)
Zde je ve výpisu vlastností zařízení HCI verze 0xa a LMP verze 0xa, což znamená dle specifikace v tabulce níže Bluetooth 5.1.
Host Controller Interface (HCI) Version |
Link Manager Protocol (LMP) Version |
Bluetooth Core Specification |
---|---|---|
0 | 0 | 1.0b |
1 | 1 | 1.1 |
2 | 2 | 1.2 |
3 | 3 | 2.0 + EDR |
4 | 4 | 2.1 + EDR |
5 | 5 | 3.0 + HS |
6 | 6 | 4.0 |
7 | 7 | 4.1 |
8 | 8 | 4.2 |
9 | 9 | 5.0 |
10 (0xA) | 10 (0xA) | 5.1 |
11 (0xB) | 11 (0xB) | 5.2 |
12 (0xC) | 12 (0xC) | 5.3 |
Zjištění MAC adresy bezdrátového teploměru a jakou verzi Bluetooth zařízení podporuje:
$ hcitool lescan
LE Scan ...
A4:C1:38:98:76:54 (unknown)
A4:C1:38:98:76:54 LYWSD03MMC
$ hcitool leinfo A4:C1:38:98:76:54
Requesting information ...
Handle: 16 (0x0010)
LMP Version: 5.0 (0x9) LMP Subversion: 0x1c1c
Manufacturer: Telink Semiconductor Co. Ltd (529)
Features: 0x3d 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Instalace Entware
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.
$ python3 -m venv ~/.venv/mitemp
$ source ~/.venv/mitemp/bin/activate
(mitemp) $ python3 -m pip install --upgrade pip
Collecting pip
Using cached https://files.pythonhosted.org/packages/8a/d7/f505e91e2cdea53cfcf51f4ac478a8cd64fb0bc1042629cedde20d9a6a9b/pip-21.2.2-py3-none-any.whl
Installing collected packages: pip
Found existing installation: pip 19.0.3
Uninstalling pip-19.0.3:
Successfully uninstalled pip-19.0.3
Successfully installed pip-21.2.2
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.
$ opkg install python3
$ python3 -m venv --without-pip mitemp
$ source ~/.venv/mitemp/bin/activate
(mitemp) $ curl https://bootstrap.pypa.io/get-pip.py | python
Collecting pip
Using cached pip-21.2.2-py3-none-any.whl (1.6 MB)
Collecting setuptools
Downloading setuptools-57.4.0-py3-none-any.whl (819 kB)
|████████████████████████████████| 819 kB 5.8 MB/s
Collecting wheel
Downloading wheel-0.36.2-py2.py3-none-any.whl (35 kB)
Installing collected packages: wheel, setuptools, pip
Successfully installed pip-21.2.2 setuptools-57.4.0 wheel-0.36.2
(mitemp) $ deactivate
$ source ~/.venv/mitemp/bin/activate
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) $ wget -qO- \
"$(sed -Ene 's|^src/gz[[:space:]]entware[[:space:]]([[:graph:]]+)|\1/include/include.tar.gz|p' /opt/etc/opkg.conf)" \
| tar xvzC /opt/include
(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:
(mitemp) $ find /opt/include -name 'glib.h'
/opt/include/glib-2.0/glib.h
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