Hi,
as it comes to the end of the Year, the usual new year resolutions come up. And of course, to get some indicators if your new fitness routines that usually lasts only the first couple of weeks work, we need data. I’ve got a withings scale connected to Homeassistant, the Withings integration works fine so far. But a better indicator is a body tape measure. There are some with Bluetooth out there. I had one from Bagel Labs, not sold anymore, which did ok. An okayish app which sent the data to some unknown servers and eventually to apple health. With some stunts it was possbile to push them from there to homeassistant.
Wouldn’t it be way cooler if that thingy just pushes the bluetooth data to homeassistant without any cloud?
So I tried a bit around with that tape measure, unfortunately it seems to need some init commands/handshake after a bt connect judging some tests with nRF connect or a wireshark sniffer.
So I noticed a very cheap one on Amazon (The “Renpho RF-BMF01”). Perhaps this works a bit better. Spoiler: it did
While the App is quite ok and allows you to work without any stupid cloud login ( just click the very small text “guest mode” ) and just sends the data to apple health in your phone ( don’t know how it works on android), I still want to have the data in Homeassistant.
Short check with nRF connect or LightBlue shows that this tape measure is happy if you just connect to it and it shows two characteristics, one which allows notification. Promising.
So the Idea was to use an ESPhome ESP32 board running anyway to use as a bt gateway to this tape measure and use esphome’s ble_client
sensor which allows to get data from non-supported devices.
First step is to get the mac adress of the tape measure. You can use esphome for this
Just add:
esp32_ble_tracker:
text_sensor:
- platform: ble_scanner
name: "BLE Devices Scanner"
Most articles forget about the text sensor, without it you won’t see anything.
Now turn on the tape measure and check the esphome logs of that board. You might see a lot of ble devices out there, but one identifies with ES_TAPE. - thats the tape measure. Copy the Mac address. For what i have seen this mac is static and doesn’t change over time.
Next thing is ( you can remove the above code now as it will spam your logs) to connect to that device.
ble_client:
- mac_address: YA:DA:YA:DA:12:34
id: es_tape
Replace the mac address with the one you got from the last step.
Now we need to subscribe to the Service. This consists of a Service UID and a Characteristics UID.
I got the IDs from the BLE sniffer app, but they should be always the same, so just use the ones below.
sensor:
- platform: ble_client
type: characteristic
ble_client_id: es_tape
id: es_tape_measurement
name: "es tape measurement"
service_uuid: '0783B03E-8535-B5A0-7140-A304D2495CB7'
characteristic_uuid: '0783B03E-8535-B5A0-7140-A304D2495CB8'
notify: true
This Sensor refers to the ble_client and pulls data every time the tape measure notifies the client that there is new data.
From the Sniffer app I got some weird responses. The Tape sends data when you slide the tape - so it seems you get some live data. Usually you are supposed to press a “check” button to finally send the correct measure to the app.
I pulled the tape to some different lengts, last one was with that check/ok button pressed
#0cm 0x2A30303030303B30303030303B30303030504D0A
#5.1cm 0x2A30303531303B30303030303B30303030504D0A
#12.4cm0x2A30313234303B30303030303B30303030504D0A
#115.1 0x2A31313531303B30303030303B30303030504D0A
#ok btn0x2A30303635303B30303030303B30303030534D0A
If you look closely you will notice something. The measurements are somewhere in bytes 1-4. Shifted by 30… wait… lets try some decoding. Ascii cannot be, perhaps utf8?
*00650;00000;0000SM
Okay… that was 6.5cm so it seems 65mm leaving one 0 for very small diet progess ( but the tape only can do 1mm resolution). Second block is unknown, but the last two letters are interesting.
The other measurements had PM
standing there, so I guess the P stands for Permanent ( live update ) and the “S” for send, so thats when the user presses the ok button. I didn’t test it but I would bet some money that the M flips to I if you switch the tape measure from metric to imperial
Next step: how do I get this mess in the right order? Lambda to the rescue:
lambda: |-
if( x[17] & 0x01 ){
uint16_t tape_measurement = 1000*(x[1] & 0x0f )+100*(x[2] & 0x0f )+10*(x[3] & 0x0f)+ (x[4] & 0x0f) ;
return (float)tape_measurement/10;
}
else{
return {};
}
x is an array, containing all the returned bytes. so with x[0] you get the first byte, with x[1] the second one etc…
The first if condition is just to check for a bit that is only present in the “s” letter, not in the “p” letter.
Otherwise homeassistant will get flooded with data when you unwind the tape measure and fiddle around. It still returns zero, we’ll address that in a second.
Next line is just some calculation. This possibly can be done more elegant with bit shifting, but this gets the job done, too. I just mask every 16bit byte by half to get the real number ( so a 31
from above gets 01
.) Then i just multiply the all the digits with the correct decimal multiplier and add them together. Since we’re getting mm’s and I want to have a cm measurement I just divide it by 10 while returnung the value.
Tine for a quick test
Next step: I want to have some feedback when the measuement is sent to homeassistant. The esp board is somewhere behind the washing machine. So i just connected a small speaker and used the rtttl
integration to play some tones when the tape successfully connected and a measurement was sent.
So here’s the full code:
#speaker for notifications beeps
output:
- platform: ledc
pin: GPIO23
id: rtttl_out
#funny notification beeps. Google rtttl :)
rtttl:
output: rtttl_out
id: my_rtttl
#ble client. Change the mac to the one of your tape measure
ble_client:
- mac_address: E8:CB:ED:DA:DA:DA
id: es_tape
on_connect:
then:
- rtttl.play: 'siren:d=8,o=5,b=185:4d#6,a#,2d#6'
#tape measure sensor
sensor:
- platform: ble_client
type: characteristic
ble_client_id: es_tape
id: es_tape_measurement
name: "es tape measurement"
service_uuid: '0783B03E-8535-B5A0-7140-A304D2495CB7'
characteristic_uuid: '0783B03E-8535-B5A0-7140-A304D2495CB8'
notify: true
filters:
- filter_out: NAN
- filter_out: 0.0
- debounce: 1s
lambda: |-
if( x[17] & 0x01 ){
uint16_t tape_measurement = 1000*(x[1] & 0x0f )+100*(x[2] & 0x0f )+10*(x[3] & 0x0f)+ (x[4] & 0x0f) ;
return (float)tape_measurement/10;
}
else{
return {};
}
icon: 'mdi:tape-measure'
unit_of_measurement: 'cm'
accuracy_decimals: 2
internal: false
on_value:
then:
- rtttl.play: 'siren:d=8,o=5,b=185:4d#6,f#6,2d#6'
Some notes:
The filters in the sensor care about:
- not sending out undef messages ( happens when the tape is disconnected/disconnecting)
- not sending out zero messages ( see text above)
- remove chattiness, while pressing the send button the tape sends the message like 10 times or so…
The speaker is connected to GPIO23 and GND ( see the ledc output) and has a 220ohm resistor as it was an 8 ohm speaker overloading the gpio otherwise)
Just added a small housing for the final result:
Finally, after adding the esp board to homeassistant using the esphome integration and publishing a first measurement…
Yes, its a 3 digit number. I really have to do some more sports in 2024.
Was a funny project which I thougth is worth sharing
There are of course some improvements that could be made.
For example, currently I can only measure the waist size (Taillienumfang ). But as I didn’t measure anything else with the old tape i’m happy with that. You could probably add some buttons or a small UI with esphome to define the measurement type.