-
• #2
Is the bind really creating a connection or just take note of the device id in a registration and then taking into account only advertisements of the registered device id? That is the way to get away from having to connect and still get data, assuming the data is in the advertisement.
TPMS has version bluetooth 4 and 5. Your's look like they are 4. But that's just from the outside look.
Interesting pub at https://www.amarinfotech.com/differences-comparisons-bluetooth-5-vs-4-2.html (print attached as .pdf)
2 Attachments
-
• #3
Hey, @allObjects thanks for your response.
Since the devices were quite cheap and on the market for a while I guess it's all old-fashioned BLE 4.
My guess was also that a connection actually might have been avoided by the creators since it means complexity and drawing more battery power.
But my question is this:
Does the quoted advertisement data contain everything the device has to tell us?
Where is the device name that my LightBlue BLE sniffer app shows?
How can I obtain any data from the advertised service and its characteristics when I cannot connect to the device?PS: I just found the same item on aliexpress and they explicitly say it's BLE 4 - look: https://www.aliexpress.com/item/33041131063.html
1 Attachment
-
• #4
Update:
Doing some more research I found this:
https://raspberrypi.stackexchange.com/questions/76959/how-to-decode-tpms-sensor-data-through-rpi3-bluetoothMaybe it is the same protocol, maybe similar.
At least the temperature reading would make sense in my case - not sure with the pressure yet.So it most probably is about the
manufacturerData
and no connection is required.
That would be quite simple to do, I'll just get my Pixl.js ready to take over from there... ;-) -
• #5
OK. Another update.
Some more investigation and some coding later I have a working solution now:const ttl = 30; var idlog = {}; function i2h(i) { return ('0'+i.toString(16)).slice(-2); } function a2h(a, i, l) { var res = ''; for(var j=i; j<i+l; j++) { res += i2h(a[j]); } return res; } function dec32(a, i) { var res = Uint32Array(1); res[0] = (a[i+3] << 24) | (a[i+2] << 16) | (a[i+1] << 8) | a[i]; return res[0]; } function decodeData(device) { var d = device.manufacturerData; var id = a2h(d, 0, 6); var data = { battery: d[14], flat: d[15], temp: dec32(d, 10) / 100, pressure: dec32(d, 6) / 100000 }; var t = getTime(); if( idlog[id] == undefined || idlog[id].last + ttl < t) { idlog[id] = { last: t, data: data }; console.log(id, data); } } function onInit() { NRF.setScan(function(device){ decodeData(device); }, { filters:[{ services: ['fbb0'] }] }); }
This is the output from a few sensors (*ba52 sits on a "real" tire, the others are just blown into to wake them up):
82eaca30bc95 { "battery": 85, "flat": 0, "temp": 23.11, "pressure": 0.07926 } 82eaca30bc95 { "battery": 85, "flat": 1, "temp": 23.55, "pressure": 0 } 80eaca10ba52 { "battery": 85, "flat": 0, "temp": 18.67, "pressure": 8.36479 } 80eaca10ba52 { "battery": 85, "flat": 0, "temp": 18.67, "pressure": 8.36479 } 80eaca10ba52 { "battery": 85, "flat": 0, "temp": 18.67, "pressure": 8.36479 } 82eaca30bc95 { "battery": 85, "flat": 0, "temp": 23.7, "pressure": 0.07018 } 81eaca207bdd { "battery": 75, "flat": 0, "temp": 23.73, "pressure": 0.053 } 80eaca10ba52 { "battery": 85, "flat": 0, "temp": 18.67, "pressure": 8.36479 } 82eaca30bc95 { "battery": 85, "flat": 1, "temp": 24.85, "pressure": 0 } 81eaca207bdd { "battery": 75, "flat": 1, "temp": 24.15, "pressure": 0 }
Now everything needs to be wrapped up, put into a working solution including low pressure / battery indication (sound, LED), missing signal detection, pairing etc...
-
• #6
@ChristianW, thanks, you just made my day! - you really push it to the limit with:
Now everything needs to be wrapped up, put into a working solution including low pressure / battery indication (sound, LED), missing signal detection, pairing etc...
-
• #7
@allObjects what do you mean by push it to the limit?
It is not ready yet, just the core piece.
But at least I know it is possible... -
• #8
That's great! Sorry I was a bit late to this, but yes, it makes sense that the relevant data would be in manufacturerData. Those look amazingly neat - I'd love to see something working on a Pixl.js or Bangle.js.
The code you have there looks spot on - all I'd say is you can always use
DataView
to decode the data without needingdec32
...d = new DataView(device.manufacturerData); d.getUint8(14) //d.getUint32(10) or d.getUint32(10,1) if byte order is different // you can also use getInt32 for signed data (eg temperature?)
For anyone else interested in this you seem to be able to just search for "v11 tpms" and find them on eBay around £30.
If you're doing this for a bike you could maybe even detect which sensor is which using signal strength (I guess the rear wheel would be lower strength than the front :).
-
• #11
Nice - thanks!
Disclaimer: I have played around with Espruino some while ago and my JS and BLE knowledge may have gotten a bit rusty - so sorry for any stupid questions in advance.
I got a Chinese TPMS (tire pressure monitoring system) consisting of 4 coincell powered BLE enabled oversized valve caps and an iOS / Android App.
The sensors support pressures over 10 Bar, but the App doesn't allow warning thresholds beyond 6,4 and since I want to monitor my bike tires I am stuck here.
Espruino to the rescue! (so I thought) - so I started hacking.
I can see the Sensors appearing in the iOS LightBlue app from time to time. Name "TPMSn-xxxxx" with n being 1..4 and xxxxxx being the hex suffix of the device ID.
Each device reports 1 service, but any connection attempt times out.
So I got out my old Nordic development board with the Espruino port loaded and finally used this:
to log this:
From time to time devices appear. But this is it.
When I actually try to connect a device using this code:
I guess the device avoids connections to save power.
Or at least makes connections as brief as possible.
So my questions:
2 Attachments