Mes passions, le boulots, mes coups de gueule...




Flower Care / MiFlora de Xiaomi : Récupérer les données avec Gatttool sur un RPi 3

Categorie : Domotique, Geek, Informatique, Linux, Raspberry Pi · par 21 Déc 2017

Introduction

Dans un article précédent, j’avais évoqué une manière de récupérer des données depuis un capteur Flower Care / MiFlora de Xiaomi.

Pour ce faire, on utilisait l’outil « miflora » publié par BarnyBug sur GitHub.

Mais nous allons voir ici comment importer ces données avec l’outil gatttool sur Linux.

Importer les données

Installation du bluetooth

En principe, si vous utilisez un RPi 3, avec Jessie ou Stretch, les outils gatttool et hcitool que nous allons utiliser plus loin devraient déjà être installés.
Si ce n’est pas le cas, je vous renvoie vers cet article : https://thepihut.com/

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install bluetooth bluez-utils blueman bluez

Déterminer l’adresse MAC du Miflora

Pour déterminer cette adresse, nous allons utiliser l’outil hcitool.

$ sudo hcitool lescan

Ce qui devrait nous retourner la liste des périphérique Bluetooth LE (Low Energy) qui se trouvent à proximité :

LE Scan ...
00:A0:50:44:0B:F3 (unknown)
00:A0:50:44:0B:F3 SensePeanut
C4:7C:8D:63:85:B1 (unknown)
C4:7C:8D:63:85:B1 Flower care
C4:7C:8D:63:85:B1 (unknown)

Nous avons donc un Flower care avec l’adresse MAC : C4:7C:8D:63:85:B1

Importer les données avec gatttool

Premiers pas avec gatttool

Pour nous connecter, nous allons taper cette commande :

$ sudo gatttool --device=C4:7C:8D:63:85:B1 -I

Une fois démarré, l’outil est prêt à recevoir des commandes et ceci devrait apparaitre à l’écran :

[C4:7C:8D:63:85:B1][LE]>

On va se connecter avec cette commande :

connect

L’outil devrait répondre quelque chose comme ceci et l’adresse MAC devrait passer en bleu lorsque la connexion est établie :

[C4:7C:8D:63:85:B1][LE]> connect
Attempting to connect to C4:7C:8D:63:85:B1
Connection successful
Notification handle = 0x0021 value: 00 
Notification handle = 0x0021 value: 00 
[C4:7C:8D:63:85:B1][LE]

Un petit help va nous retourner toutes les commandes disponibles :

[C4:7C:8D:63:85:B1][LE]> help
help                                          Show this help
exit                                          Exit interactive mode
quit                                          Exit interactive mode
connect [address [address type]]              Connect to a remote device
disconnect                                    Disconnect from a remote device
primary [UUID]                                Primary Service Discovery
included [start hnd [end hnd]]                Find Included Services
characteristics [start hnd [end hnd [UUID]]]  Characteristics Discovery
char-desc [start hnd] [end hnd]               Characteristics Descriptor Discovery
char-read-hnd <handle>                        Characteristics Value/Descriptor Read by handle
char-read-uuid <UUID> [start hnd] [end hnd]   Characteristics Value/Descriptor Read by UUID
char-write-req <handle> <new value>           Characteristic Value Write (Write Request)
char-write-cmd <handle> <new value>           Characteristic Value Write (No response)
sec-level [low | medium | high]               Set security level. Default: low
mtu <value>                                   Exchange MTU for GATT/ATT

La commande primary va explorer les handles des services primaires :

[C4:7C:8D:63:85:B1][LE]> primary
attr handle: 0x0001, end grp handle: 0x0009 uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x000f uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x0010, end grp handle: 0x0022 uuid: 0000fe95-0000-1000-8000-00805f9b34fb
attr handle: 0x0023, end grp handle: 0x0030 uuid: 0000fef5-0000-1000-8000-00805f9b34fb
attr handle: 0x0031, end grp handle: 0x0039 uuid: 00001204-0000-1000-8000-00805f9b34fb
attr handle: 0x003a, end grp handle: 0x0042 uuid: 00001206-0000-1000-8000-00805f9b34fb

La commande char-desc va explorer les descripteurs de caractéristiques :

[C4:7C:8D:63:85:B1][LE]> char-desc 0x0001
Notification handle = 0x0021 value: 00 
handle: 0x0001, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0002, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb

handle: 0x0033, uuid: 00001a00-0000-1000-8000-00805f9b34fb

handle: 0x0035, uuid: 00001a01-0000-1000-8000-00805f9b34fb

handle: 0x0038, uuid: 00001a02-0000-1000-8000-00805f9b34fb

handle: 0x0041, uuid: 00001a12-0000-1000-8000-00805f9b34fb
handle: 0x0042, uuid: 00001a12-0000-1000-8000-00805f9b34fb

Lire un handle

Pour lire le contenu d’un handle, on utilise la commande char-read-hnd suivie du n° du handle qui nous intéresse :

[C4:7C:8D:63:85:B1][LE]> char-read-hnd 0x0035
Characteristic value/descriptor: aa bb cc dd ee ff 99 88 77 66 00 00 00 00 00 00

Les handles qui nous intéressent

Dans un post sur la plateforme de Jeedom, j’ai trouvé une petite liste reprenant les handles qui vont nous intéresser. Il s’agit du 0x0003, du 0x0033, du 0x0035 et du 0x0038.

Un autre post de la communauté Home Assistant, nous apprend que jusqu’à la version 2.6.2 du firmware du MiFlora, les données étaient directement lisibles dans le handle 0x0035.

Mais depuis la version 2.6.6, il faut d’abord inscrire la valeur A01F dans le handle 0x0033 pour déclencher le live data et pouvoir lire les données en 0x0035.

Sans ça, les données retournées seraient celles par défaut :

[C4:7C:8D:63:85:B1][LE]> char-read-hnd 0x0035
Characteristic value/descriptor: aa bb cc dd ee ff 99 88 77 66 00 00 00 00 00 00

Pour ce faire, on utilise la commande :

[C4:7C:8D:63:85:B1][LE]> char-write-req 0x0033 A01F

Une nouvelle lecture de 0x0035 devrait nous retourner quelque chose comme ceci :

[C4:7C:8D:63:85:B1][LE]> char-read-hnd 0x0035
Characteristic value/descriptor: 8f 00 00 10 00 00 00 23 dc 01 02 3c 00 fb 34 9b

NB: Les commandes write et read doivent être passées rapidement sans quoi les valeurs retournées seront celles de base.

[C4:7C:8D:63:85:B1][LE]> connect
[C4:7C:8D:63:85:B1][LE]> char-write-req 0x33 A01F
Characteristic value was written successfully
[C4:7C:8D:63:85:B1][LE]> char-read-hnd 0x35
Characteristic value/descriptor: 8f 00 00 10 00 00 00 23 dc 01 02 3c 00 fb 34 9b 
[C4:7C:8D:63:85:B1][LE]> quit

En ligne de commande

Nous allons voir comment utiliser gatttool en ligne de commande :

$ sudo gatttool --device=C4:7C:8D:63:85:B1 --char-write-req -a 0x33 -n A01F

$ sudo gatttool --device=C4:7C:8D:63:85:B1 --char-read -a 0x35

Structure des données

Nombres entiers sur plusieurs octets

Les données sont codées sur des octets en Little endian. C.-à-d. avec l’octet de poids le plus faible en premier.

Pour comprendre la représentation des nombres entiers sur plusieurs octets, vous pouvez consulter cette page Wikipedia : https://fr.wikipedia.org/wiki/Endianness

Handle 0x0035

Correction le 06/01/2018 selon le commentaire de Rene Jullien :
Température codée sur 2 bytes et non 3.

Exemple : 8f 00 00 10 00 00 00 23 dc 01 02 3c 00 fb 34 9b

8f00001000000023dc01023c00fb349b
1431635476
Température??EnsoleillementHumiditéEngrais????????????

Le handle 0x0035 retourne ces données :

  • Bytes 1 & 2 : Température : Hex 00 8f : 143 = 14,3 °C

Attention !
Lorsque la température est négative, les 2 premiers bytes affichent :
FF FF pour -0.1 °C / F6 FF pour -1.0 °C / 9C FF pour -10.0 °C
Soit la formule : Température = -65536 + Température

  • Bytes 4, 5, 6 & 7 : Ensoleillement : Hex 00 00 00 10 : 16 = 16 lux
  • Byte 8 : Humidité : Hex 23 : 35 %
  • Bytes 9 & 10 : Engrais : Hex 01 dc : 476 uS/cm

Handle 0x0038

Exemple : 63 27 33 2e 31 2e 38

6327332e312e38
993.1.8
Batterie??Version du Firmware

Le handle 0x0038 retourne ces données :

  • Byte 1 : Batterie : Hex 63 : 99 %
  • Bytes 3, 4, 5, 6 & 7 : Version du Firmware : ASCII 33 2e 31 2e 38 : 3.1.8

Handle 0x0003

L’UUID du handle 0x0003 commence par 00002a00. On apprend sur cette page que 0x2A00 correspond à un Device Name.

handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb

Exemple : 46 6c 6f 77 65 72 20 63 61 72 65

466c6f7765722063617265
Flowercare
Device Name

Le handle 0x0003 retourne ces données :

  • Les Bytes de 1 à 11 indiquent le nom du périphérique en ASCII : Flower care

Script

Pour conclure, voici un petit script qui va récupérer les données et les afficher à l’écran :


#!/bin/bash

# Vérifie que l'on est en Root
if [[ $EUID -ne 0 ]];
then
	echo "Ce script doit être exécuté avec les privilèges administrateur"
   	exit 1
fi

MACadd="C4:7C:8D:63:85:B1"

# On désactive, puis on réactive le bluetooth pour éviter les erreurs
hciconfig hci0 down; hciconfig hci0 up

# On écrit A01F dans le handle 0x0033 pour activer le live data dans le handle 0x0035
gatttool --device=$MACadd --char-write-req -a 0x33 -n A01F &amp;gt; /dev/null

# Le handle 0x0003 retourne le nom du periphérique bluetooth en ASCII
#gatttool --device=$MACadd --char-read -a 0x03
#Characteristic value/descriptor: 46 6c 6f 77 65 72 20 63 61 72 65
#Flower care

# Le handle 0x0035 retourne : Temp (Int16) / Light (Int32) / Moisture (Int8) / Fertility (Int16)
#gatttool --device=$MACadd --char-read -a 0x35
#Characteristic value/descriptor: 8f 00 00 11 00 00 00 23 d9 01 02 3c 00 fb 34 9b 

# Le handle 0x0038 retourne : Battery (Int8) / Firmware version (5 bytes ASCII)
#gatttool --device=$MACadd --char-read -a 0x38
#Characteristic value/descriptor: 63 27 33 2e 31 2e 38

# Lecture des handle 0x0003, 0x0035 et 0x0038
hnd03=$(gatttool --device=$MACadd --char-read -a 0x03)
hnd35=$(gatttool --device=$MACadd --char-read -a 0x35)
hnd38=$(gatttool --device=$MACadd --char-read -a 0x38)

# Affichage du nom
name=$(echo "$hnd03" |cut -c34-65 | xxd -r -p)
echo Nom : $name

# Affichage de la température
temperature=$((16#${hnd35:36:2}${hnd35:33:2}))

# Lorsque la température est négative, les 2 premiers bytes affichent :
# FF FF pour -0.1 °C / F6 FF pour -1.0 °C / 9C FF pour -10.0 °C
# Soit la formule : Température = -65536 + Température
# J'ai arbitrairement fixé la température pivot à 32768 soit 3276.8 °C !!!

if [[ $temperature -gt 32768 ]];
then
	temperature=$((-65536 + $temperature))
fi

# Division par 10 avec 1 chiffre après la virgule
temperature=`echo "scale=1;$temperature/10" | bc`
# Forcer un "0" avant le point décimal pour éviter par exemple ".1" ou "-.1" 
# Chaque fois qu'un nombre commence par "-.", on remplace "-." par "-0."
# Chaque fois qu'un nombre commence par ".", on remplace "." par&amp;lt;span style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" data-mce-type="bookmark" class="mce_SELRES_start"&amp;gt;&amp;lt;/span&amp;gt; "0."
temperature=$(echo $temperature | bc -l | sed -e 's/^-\./-0./' -e 's/^\./0./')
echo Température : $temperature °C

# Affichage de l'ensoleillement
light=$((16#${hnd35:51:2}${hnd35:48:2}${hnd35:45:2}${hnd35:42:2}))
echo Ensoleillement : $light lux

# Affichage de l'humidité
moisture=$((16#${hnd35:54:2}))
echo Humidité : $moisture %

# Affichage de l'engrais
fertility=$((16#${hnd35:60:2}${hnd35:57:2}))
echo Engrais : $fertility uS/cm

# Affichage de la batterie
battery=$((16#${hnd38:33:2}))
echo Batterie : $battery %

# Affichage de la version du firmware
version=$(echo "$hnd38" |cut -c40-53 | xxd -r -p)
echo Version : $version

# On désactive, puis on réactive le bluetooth pour éviter les erreurs
hciconfig hci0 down; hciconfig hci0 up

Vous devriez obtenir ce genre de résultat pour ce script :

Nom : Flower care
Température : 14.3 °C
Ensoleillement : 17 lux
Humidité : 35 %
Engrais : 473 uS/cm
Batterie : 99 %
Version : 3.1.8

(2) comments

Rene Jullien
3 semaines ago · Répondre

Bonsoir

Description super claire, je me suis permis de la referencer pour répondre à une question dans le forum Jeedom.

J’ai 4 petits commentaires:
– Dans les doc que j’avais, la temperature et l’ensoleillement étaient codés sur 2 octets seulement, je suis surpris de pouvoir coder des temperatures > 3000 degrés.
– Le post que tu cites s’appelle Jeedom et pas Jemdoo
– Attention aux températures négatives, je n’ai pas vérifié ton script, j’avais un bug dans ma premiere version du plugin Jeedom.
– Je n’ai jamais réussi à avoir des résultats stables, du coup je gère les principales erreurs et je tente des retry.

René

    fanjoe
    3 semaines ago · Répondre

    Bonjour,

    – Effectivement une limite à 2 octets semble logique. Mais à quoi sert le 3ème octet ?
    – La référence à Jeedom est corrigée.
    – Je n’ai jamais testé les températures négatives. Peut-être plus tard.
    – Mes résultats sont stables et me servent à tracer des graphiques qui semblent cohérents.

    Merci de vos commentaires,
    Didier.

Laisser un commentaire