Reverse engineering the Mi flora plant sensor

The Xiaomi Mi plant sensor is a well-made plant sensor that does not only measure the humidity, but also fertility, temperature and environment light. Pretty cool for a small device like this. And even cooler is that it uses Bluetooth Low energy for communication with your mobile device. This should make it easily hackable.

Ok, let’s start and check what characteristics are available:

handle = 0x0002, char properties = 0x02, char value handle = 0x0003, uuid = 00002a00-0000-1000-8000-00805f9b34fb
handle = 0x0004, char properties = 0x02, char value handle = 0x0005, uuid = 00002a01-0000-1000-8000-00805f9b34fb
handle = 0x0006, char properties = 0x0a, char value handle = 0x0007, uuid = 00002a02-0000-1000-8000-00805f9b34fb
handle = 0x0008, char properties = 0x02, char value handle = 0x0009, uuid = 00002a04-0000-1000-8000-00805f9b34fb
handle = 0x000d, char properties = 0x22, char value handle = 0x000e, uuid = 00002a05-0000-1000-8000-00805f9b34fb
handle = 0x0011, char properties = 0x1a, char value handle = 0x0012, uuid = 00000001-0000-1000-8000-00805f9b34fb
handle = 0x0014, char properties = 0x02, char value handle = 0x0015, uuid = 00000002-0000-1000-8000-00805f9b34fb
handle = 0x0016, char properties = 0x12, char value handle = 0x0017, uuid = 00000004-0000-1000-8000-00805f9b34fb
handle = 0x0018, char properties = 0x08, char value handle = 0x0019, uuid = 00000007-0000-1000-8000-00805f9b34fb
handle = 0x001a, char properties = 0x08, char value handle = 0x001b, uuid = 00000010-0000-1000-8000-00805f9b34fb
handle = 0x001c, char properties = 0x0a, char value handle = 0x001d, uuid = 00000013-0000-1000-8000-00805f9b34fb
handle = 0x001e, char properties = 0x02, char value handle = 0x001f, uuid = 00000014-0000-1000-8000-00805f9b34fb
handle = 0x0020, char properties = 0x10, char value handle = 0x0021, uuid = 00001001-0000-1000-8000-00805f9b34fb
handle = 0x0024, char properties = 0x0a, char value handle = 0x0025, uuid = 8082caa8-41a6-4021-91c6-56f9b954cc34
handle = 0x0026, char properties = 0x0a, char value handle = 0x0027, uuid = 724249f0-5ec3-4b5f-8804-42345af08651
handle = 0x0028, char properties = 0x02, char value handle = 0x0029, uuid = 6c53db25-47a1-45fe-a022-7c92fb334fd4
handle = 0x002a, char properties = 0x0a, char value handle = 0x002b, uuid = 9d84b9a3-000c-49d8-9183-855b673fda31
handle = 0x002c, char properties = 0x0e, char value handle = 0x002d, uuid = 457871e8-d516-4ca1-9116-57d0b17b9cb2
handle = 0x002e, char properties = 0x12, char value handle = 0x002f, uuid = 5f78df94-798c-46f5-990a-b3eb6a065c88
handle = 0x0032, char properties = 0x0a, char value handle = 0x0033, uuid = 00001a00-0000-1000-8000-00805f9b34fb
handle = 0x0034, char properties = 0x1a, char value handle = 0x0035, uuid = 00001a01-0000-1000-8000-00805f9b34fb
handle = 0x0037, char properties = 0x02, char value handle = 0x0038, uuid = 00001a02-0000-1000-8000-00805f9b34fb
handle = 0x003b, char properties = 0x02, char value handle = 0x003c, uuid = 00001a11-0000-1000-8000-00805f9b34fb
handle = 0x003d, char properties = 0x1a, char value handle = 0x003e, uuid = 00001a10-0000-1000-8000-00805f9b34fb
handle = 0x0040, char properties = 0x02, char value handle = 0x0041, uuid = 00001a12-0000-1000-8000-00805f9b34fb

Looks like some standard characteristics (the uuid’s starting with 00002) and a lot of non-standard. Let’s check the standard characteristics first. UUID 00002a00-0000-1000-8000-00805f9b34fb should contain the device name:

# gatttool --device=C4:7C:8D:60:8F:E6 --char-read -a 0x03
Characteristic value/descriptor: 46 6c 6f 77 65 72 20 6d 61 74 65 

“46 6c 6f 77 65 72 20 6d 61 74 65” is “Flower mate” in ASCII. Cool. Seems that Xiaomi is using the standards – not something we see from every Chinese company.

How do you know that this UUID contains the name? Check out the list of GATT characteristics on the Bluetooth web site.

Ok, let’s have a look at all characteristics that return values:

0x03 : 160823-164329 : 46 6c 6f 77 65 72 20 6d 61 74 65
0x05 : 160823-164329 : 00 00
0x07 : 160823-164329 : 00
0x09 : 160823-164329 : 0a 00 14 00 00 00 f4 01
0x0e : 160823-164329 : 01 00 ff ff
0x12 : 160823-164329 : c3 c8 fd 30
0x15 : 160823-164329 : 98 00
0x17 : 160823-164329 : 1f 8e 8e 13 86 dd a0 d8 52 66
0x1d : 160823-164329 : 4c cc ca 13 fc fa f7 a6 51 50 c6 85 ee 61 85 47 7f 3d d6 6b
0x1f : 160823-164329 : 2e a1 bc 3e b3 87 94 e9 68 6f ff b7
0x2f : 160823-164329 : 00
0x33 : 160823-164329 : aa bb
0x35 : 160823-164329 : f5 00 00 00 00 00 00 10 61 00 00 00 00 00 00 00
0x38 : 160823-164329 : 64 10 32 2e 36 2e 32
0x3c : 160823-164329 : aa bb cc dd ee ff 99 88 77 66 55 44 33 22 11 10
0x3e : 160823-164329 : aa bb cc
0x41 : 160823-164329 : 81 8d 23 00

Hmm, look pretty wild. Looking at the data you have no idea what they might encode.

Let’s try something different and dump all values every 10 minutes.

We see a lot of values that do not change at all like this:

0x38 : 160823-073844 : 64 10 32 2e 36 2e 32
0x38 : 160823-074852 : 64 10 32 2e 36 2e 32
0x38 : 160823-075856 : 64 10 32 2e 36 2e 32
0x38 : 160823-080859 : 64 10 32 2e 36 2e 32
0x38 : 160823-081909 : 64 10 32 2e 36 2e 32
0x38 : 160823-082914 : 64 10 32 2e 36 2e 32
0x38 : 160823-083918 : 64 10 32 2e 36 2e 32
0x38 : 160823-084922 : 64 10 32 2e 36 2e 32
0x38 : 160823-085925 : 64 10 32 2e 36 2e 32
0x38 : 160823-090931 : 64 10 32 2e 36 2e 32
0x38 : 160823-091936 : 64 10 32 2e 36 2e 32
0x38 : 160823-092940 : 64 10 32 2e 36 2e 32
0x38 : 160823-093945 : 64 10 32 2e 36 2e 32
0x38 : 160823-094951 : 64 10 32 2e 36 2e 32
0x38 : 160823-100006 : 64 10 32 2e 36 2e 32
0x38 : 160823-101010 : 64 10 32 2e 36 2e 32
0x38 : 160823-102014 : 64 10 32 2e 36 2e 32
0x38 : 160823-103018 : 64 10 32 2e 36 2e 32
0x38 : 160823-104022 : 64 10 32 2e 36 2e 32

Others seem to change randomly like this

0x41 : 160823-073844 : d4 0d 23 00
0x41 : 160823-074852 : 31 10 23 00
0x41 : 160823-075856 : 8e 12 23 00
0x41 : 160823-080859 : f0 14 23 00
0x41 : 160823-081909 : 4d 17 23 00
0x41 : 160823-082914 : aa 19 23 00
0x41 : 160823-083918 : 02 1c 23 00
0x41 : 160823-084922 : 5f 1e 23 00
0x41 : 160823-085925 : bc 20 23 00
0x41 : 160823-090931 : 19 23 23 00
0x41 : 160823-091936 : 76 25 23 00
0x41 : 160823-092940 : d3 27 23 00
0x41 : 160823-093945 : 30 2a 23 00
0x41 : 160823-094951 : 97 2c 23 00
0x41 : 160823-100006 : f4 2e 23 00
0x41 : 160823-101010 : 51 31 23 00
0x41 : 160823-102014 : ae 33 23 00
0x41 : 160823-103018 : 06 36 23 00
0x41 : 160823-104022 : 63 38 23 00
0x41 : 160823-105025 : c0 3a 23 00

But then there is one characteristic with very controlled changes:

0x35 : 160823-073844 : f2 00 00 68 00 00 00 10 66 00 00 00 00 00 00 00
0x35 : 160823-074852 : f2 00 00 54 00 00 00 10 66 00 00 00 00 00 00 00
0x35 : 160823-075856 : f1 00 00 54 00 00 00 10 66 00 00 00 00 00 00 00
0x35 : 160823-080859 : f2 00 00 54 00 00 00 10 66 00 00 00 00 00 00 00
0x35 : 160823-081909 : f2 00 00 68 00 00 00 10 66 00 00 00 00 00 00 00
0x35 : 160823-082914 : f2 00 00 54 00 00 00 10 66 00 00 00 00 00 00 00
0x35 : 160823-083918 : f2 00 00 68 00 00 00 10 65 00 00 00 00 00 00 00
0x35 : 160823-084922 : f2 00 00 68 00 00 00 10 66 00 00 00 00 00 00 00
0x35 : 160823-085925 : f2 00 00 57 00 00 00 10 66 00 00 00 00 00 00 00
0x35 : 160823-090931 : f2 00 00 57 00 00 00 10 65 00 00 00 00 00 00 00
0x35 : 160823-091936 : f2 00 00 57 00 00 00 10 65 00 00 00 00 00 00 00
0x35 : 160823-092940 : f2 00 00 57 00 00 00 10 65 00 00 00 00 00 00 00
0x35 : 160823-093945 : f2 00 00 57 00 00 00 10 65 00 00 00 00 00 00 00
0x35 : 160823-094951 : f2 00 00 6b 00 00 00 10 65 00 00 00 00 00 00 00
0x35 : 160823-100006 : f2 00 00 6b 00 00 00 10 63 00 00 00 00 00 00 00
0x35 : 160823-101010 : f2 00 00 6b 00 00 00 10 63 00 00 00 00 00 00 00
0x35 : 160823-102014 : f2 00 00 79 00 00 00 10 65 00 00 00 00 00 00 00
0x35 : 160823-103018 : f2 00 00 79 00 00 00 10 63 00 00 00 00 00 00 00
0x35 : 160823-104022 : f2 00 00 6b 00 00 00 10 65 00 00 00 00 00 00 00
0x35 : 160823-105025 : f2 00 00 79 00 00 00 10 65 00 00 00 00 00 00 00

Seems like there are 4 bytes of values and a lot of zeros. Could these 4 byte be the sensor values? They are!
Now let’s check the values in the Mi app on the iPhone:

  • 24.2 degree celcius
  • 121 Lux
  • 16% moisture
  • 101 us/cm fertility (whatever this means)

Except for the temperature these values map nicely to the data:

  • 16 = 0x10 – byte 8
  • 121 = 0x79 – byte 4
  • 101 = 0x65 – byte 9

The only thing that isn’t yet clear is the encoding of the temperature.

Also the sensor should be able to measure up to 10000 lux. This won’t work with a single byte. So, let’s put another plant it into the bright sunlight:

25 01 00 f7 26 00 00 28 0e 01 00 00 00 00 00 00
  • Fertility is now 270, which is bytes 9 and 10 (MSB byte 9, LSB byte 10)
  • Sunlight is 9975 lux which is stored in bytes 4 and 4 (MSB byte 4, LSB byte 5)
  • And now it is also getting clear how the temperature is encoded. It is in 0.1 degree Celcius steps in bytes 1 and 2, which gives us 29.3 degree celcius in this example (which fits the data displayed by the app).

Das richtige BLE Modul für dein Projekt

Bluetooth Low Energy wird immer populärer. BLE verbraucht nicht nur weniger Strom als “klassische” Bluetooth-Schnittstellen, sondern bietet sogar noch eine höhere Sendereichweite. Das wird durch eine reduzierte Datenrate erreicht. Damit eignet sich BLE nicht für grosse Datenübertragungen. Gerade für Sensoranwendungen ist das aber normalerweise kein Problem.

Leider haben viele Prototyping-Plattformen wie Arduino und ESP8266 noch kein BLE integriert. Man muss also Zusatzmodule nutzen, von denen es sehr viele verschiedene gibt.

Allerdings sollte man vor der Bestellung das Datenblatt des Moduls genau lesen.

hm13

Ich erhielt heute dieses HM13-Modul, welches recht günstig ist. Geplant war damit einen Bluetooth-Pflanzensensor anzufragen. Als ich aber das Datenblatt richtig las, stellte ich fest, dass dieses Modul nur für eine serielle Datenverbindung über Bluetooth gedacht ist. Man kann es nicht als “allgemeines” Bluetooth-Interface nutzen.

Daher hier nochmal der Tip: Datenblätter vor der Bestellung lesen! Ansonsten kauft man evtl. Sachen, die man nicht brauchen kann.

Bluetooth-Probleme mit dem Raspberry Pi 3

Der “Bluetooth tracker” in Home Assistant ist eine spannende Funktion. Sie erlaubt es, festzustellen, ob Personen an einem bestimmten Ort sind (z.B. zu Hause). Dafür kann z.B. das Mobiltelefon genutzt werden.  Der Raspberry Pi 3 kommt mit einem integrierten Bluetooth chip. Das heisst, um die Tracking-Funktion auf Bluetooth-Basis zu nutzen, ist keine zusätzliche Hardware erforderlich. Ziemlich cool! Allerdings hatte ich ziemlich grosse Problem, diese Funktion zum Laufen zu bringen.Bluetooth war auf dem Raspberry Pi  aktiviert, dennoch hat Home Assistant keine Bluetooth-Geräte gefunden. Wo liegt das Problem?

Fangen wir mal an, auf der Kommandozeile nach Bluetooth-Geräten zu suchen:

[email protected]:~# hcitool scan
Scanning ...

Nichts! Dabei sind diverse Bluetooth-Geräte aktiv. Versuchen wir es als nächstes mit “blutoothctl”:

[email protected]:~# sudo bluetoothctl
[NEW] Controller B8:27:EB:76:70:7C raspberrypi [default]
[bluetooth]# devices
[bluetooth]# scan on
Discovery started
[CHG] Controller B8:27:EB:76:70:7C Discovering: yes
[NEW] Device 04:69:F8:xx:xx:xx 04-69-F8-xx-xx-xx
[NEW] Device 88:C6:26:xx:xx:xx 88-C6-26-xx-xx-xx
[bluetooth]# quit
[DEL] Controller B8:27:EB:76:70:7C raspberrypi [default]

Da sind die ganzen Geräte. Wieso findet hcitool die nicht? Gibt es evtl. Probleme mit einem Serverprozess?

[email protected]:~# systemctl status bluetooth
● bluetooth.service - Bluetooth service
   Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled)
   Active: active (running) since Mon 2016-06-13 15:53:36 UTC; 1h 54min ago
     Docs: man:bluetoothd(8)
 Main PID: 729 (bluetoothd)
   Status: "Running"
   CGroup: /system.slice/bluetooth.service
           └─729 /usr/lib/bluetooth/bluetoothd

Jun 13 15:53:36 raspberrypi systemd[1]: Starting Bluetooth service...
Jun 13 15:53:36 raspberrypi bluetoothd[729]: Bluetooth daemon 5.23
Jun 13 15:53:36 raspberrypi systemd[1]: Started Bluetooth service.
Jun 13 15:53:36 raspberrypi bluetoothd[729]: Starting SDP server
Jun 13 15:53:36 raspberrypi bluetoothd[729]: Bluetooth management interface 1.10 initialized
Jun 13 15:53:36 raspberrypi bluetoothd[729]: Sap driver initialization failed.
Jun 13 15:53:36 raspberrypi bluetoothd[729]: sap-server: Operation not permitted (1)

Das sieht etwas seltsam aus. Der Serverprozess läuft zwar, aber es gibt dort eine seltsame Fehlermeldung. Wenn man nun nach dieser sucht, findet man sehr viel Text dazu auf  raspberypi.org. Um das in den Griff zu bekommen, muss man wohl das SAP-Plugin abschalten.

Dazu bearbeitet man die Datei /etc/systemd/system/bluetooth.target.wants/bluetooth.service und fügt die Option “–noplugin=sap” an der “ExecStart”-Zeile an:

[Unit]
Description=Bluetooth service
Documentation=man:bluetoothd(8)

[Service]
Type=dbus
BusName=org.bluez
ExecStart=/usr/lib/bluetooth/bluetoothd --noplugin=sap
NotifyAccess=main
#WatchdogSec=10
#Restart=on-failure
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
LimitNPROC=1

[Install]
WantedBy=bluetooth.target
Alias=dbus-org.bluez.service

Wie sieht es jetzt aus?

[email protected]:~# systemctl status bluetooth
● bluetooth.service - Bluetooth service
   Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled)
   Active: active (running) since Mon 2016-06-13 17:56:35 UTC; 9s ago
     Docs: man:bluetoothd(8)
 Main PID: 19106 (bluetoothd)
   Status: "Running"
   CGroup: /system.slice/bluetooth.service
           └─19106 /usr/lib/bluetooth/bluetoothd --noplugin=sap

Jun 13 17:56:35 raspberrypi bluetoothd[19106]: Bluetooth daemon 5.23
Jun 13 17:56:35 raspberrypi bluetoothd[19106]: Starting SDP server
Jun 13 17:56:35 raspberrypi bluetoothd[19106]: Excluding (cli) sap
Jun 13 17:56:35 raspberrypi bluetoothd[19106]: Bluetooth management interface 1.10 initialized
Jun 13 17:56:35 raspberrypi systemd[1]: Started Bluetooth service.

Die Fehlermeldung ist weg. Allerdings zeigt

hcitool scan

weiterhin keine Bluetooth-Gerät an.

Wird fortgesetzt…