Čtení teploměru přes Bluetooth v CoreELEC

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

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *