FTLab Wireless Smart Geiger — Part 3

Previous posts:

We saw that the BSG-001 does not expose useful standard services and characteristics. So now we have to look at the vendor-specific characteristics:

Value handleUUIDProperties
0x000b1524read, write
0x000d1525read, notify
0x00101526read, notify

The first one (1524) is the only one writable, so I guess we can send commands there. I guess reading it returns the last command written to it.

The two others can notify, so they are probably where the answers come back. But why two of them?

Read

Let’s see what’s already there:

$ A=D4:EA:38:XX:XX:XX
$ for h in 0x000b 0x000d 0x0010; do gatttool -t random -b $A --char-read -a $h; done
Characteristic value/descriptor: a0 02 01 0a 0d 14 39 0d 00 00 00 00 00 00 00 00 00 00 00 00
Characteristic value/descriptor: a0 0b 01 00 00 00 13 00 00 00 00 c3 00 00 00 00 00 00 00 00
Characteristic value/descriptor: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# Press some buttons in the app then read again.
$ for h in 0x000b 0x000d 0x0010; do gatttool -t random -b $A --char-read -a $h; done
Characteristic value/descriptor: a0 02 01 00 0d 15 11 20 00 00 00 00 00 00 00 00 00 00 00 00
Characteristic value/descriptor: a0 0b 01 00 00 00 1f 00 00 00 00 c3 00 00 00 00 00 00 00 00
Characteristic value/descriptor: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

We can see all values are 20 bytes long, which is the maximum payload size for an ATT in BLE 4.0 (for the default ATT_MTU). Can’t really deduce much yet.

Notifications

By default, a “notify” characteristic doesn’t send anything. We have to write to the characteristic’s CCCD (Client Characteristic Configuration Descriptor, UUID 2902):

This works in an interactive connection, while the client is connected to the device; once disconnected, the notifications stop. So the commands with gatttool we used so far won’t do.

First, let’s find the CCCDs of the characteristics:

$ gatttool -t random -b $A --char-desc | grep 2902
handle = 0x000e, uuid = 00002902-0000-1000-8000-00805f9b34fb
handle = 0x0011, uuid = 00002902-0000-1000-8000-00805f9b34fb

So 0x000e for 1525 and 0x0011 for 1526.

Let’s turn both on and wait for something to happen, using gatttool in interactive mode:

$ gatttool -I -t random -b $A
[D4:EA:38:XX:XX:XX][LE]> connect
Attempting to connect to D4:EA:38:XX:XX:XX
Connection successful
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000e 0100
Characteristic value was written successfully
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x0011 0100
Characteristic value was written successfully
[D4:EA:38:XX:XX:XX][LE]>

…and nothing. Kind of expected. We should probably send some commands to the device to see if it answers.

Write

Continuing in the same session, let’s write the command we read in 1524:

[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00 00 00 00 00 00 00 00 00 00 00 00
Characteristic value was written successfully
Notification handle = 0x000d value: a0 0b 01 00 00 1c 01 05 00 05 00 c3 00 00 00 00 00 00 00 00

So we received an answer on 1525, a similar shape to the previous read but not exactly the same.

Let’s read it a few more times (showing the results only here):

Notification handle = 0x000d value: a0 0b 01 00 00 27 31 05 00 05 00 c3 00 00 00 00 00 00 00 00 
Notification handle = 0x000d value: a0 0b 01 00 00 27 32 05 00 05 00 c3 00 00 00 00 00 00 00 00 
Notification handle = 0x000d value: a0 0b 01 00 00 27 33 05 00 05 00 c3 00 00 00 00 00 00 00 00 
Notification handle = 0x000d value: a0 0b 01 00 00 27 34 05 00 05 00 c3 00 00 00 00 00 00 00 00 
Notification handle = 0x000d value: a0 0b 01 00 00 27 35 05 00 05 00 c3 00 00 00 00 00 00 00 00 
Notification handle = 0x000d value: a0 0b 01 00 00 27 36 05 00 05 00 c3 00 00 00 00 00 00 00 00 
Notification handle = 0x000d value: a0 0b 01 00 00 27 38 05 00 05 00 c3 00 00 00 00 00 00 00 00 

I waited about 1 second between each command, except the last one for which I waited about 2 seconds… It looks like we have some kind of clock in the middle here.

Now we have an answer pattern for that request. Let’s dig into the request; I guess we don’t need the full 20 bytes, let’s drop bytes until the answer gives a different pattern:

[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00 00 00 00 00 00 00 00 00 00 00 00
Notification handle = 0x000d value: a0 0b 01 00 00 31 31 14 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00 00 00 00 00 00 00 00 00 00 00
Notification handle = 0x000d value: a0 0b 01 00 00 31 33 14 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00 00 00 00 00 00 00 00 00 00
Notification handle = 0x000d value: a0 0b 01 00 00 31 35 14 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00 00 00 00 00 00 00 00 00
Notification handle = 0x000d value: a0 0b 01 00 00 31 37 14 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00 00 00 00 00 00 00 00
Notification handle = 0x000d value: a0 0b 01 00 00 31 3a 14 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00 00 00 00 00 00 00
Notification handle = 0x000d value: a0 0b 01 00 00 32 05 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00 00 00 00 00 00
Notification handle = 0x000d value: a0 0b 01 00 00 32 0b 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00 00 00 00 00
Notification handle = 0x000d value: a0 0b 01 00 00 32 0d 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00 00 00 00
Notification handle = 0x000d value: a0 0b 01 00 00 32 10 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00 00 00
Notification handle = 0x000d value: a0 0b 01 00 00 32 14 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00 00
Notification handle = 0x000d value: a0 0b 01 00 00 32 16 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d 00
Notification handle = 0x000d value: a0 0b 01 00 00 32 18 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39 0d
Notification handle = 0x000d value: a0 0b 01 00 00 32 1a 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14 39
Notification handle = 0x000d value: a0 0b 01 00 00 32 1c 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d 14
Notification handle = 0x000d value: a0 0b 01 00 00 32 1e 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a 0d
Notification handle = 0x000d value: a0 0b 01 00 00 32 20 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01 0a
Notification handle = 0x000d value: a0 0b 01 00 00 32 22 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02 01
Notification handle = 0x000d value: a0 0b 01 00 00 32 24 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0 02
Notification handle = 0x000d value: a0 0b 01 00 00 32 25 0f 00 00 00 c3 00 00 00 00 00 00 00 00
[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a0
Notification handle = 0x000d value: a0 0b 01 00 00 32 27 0f 00 00 00 c3 00 00 00 00 00 00 00 00

Two things:

So maybe let’s change that first byte:

[D4:EA:38:XX:XX:XX][LE]> char-write-req 0x000b a1
Characteristic value was written successfully
Notification handle = 0x0010 value: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Interesting, received a different response, from the 1526 characteristic notification this time. Let’s assume the first byte is the command then.

After more readings and a bit of cheating (I looked at what the iOS app said):

BytesMeaning
a0Command
0bResponse length? (11 bytes)
01State
00Days
00Hours
00Minutes
00Seconds
0f 000x000f = 0.15 µSv/h (average)
05 000x0005 = 0.05 µSv/h (1-minute)
c3 000x00c3 battery level

Notes:

Other commands?

The current assumption is that the first byte is the command. So there could be 256 possible commands.

Let’s try them! (I did it for you, so don’t do it, it’s not fully safe)

$ ( echo connect; sleep 4
    echo "char-write-req 0x000e 0100"; echo "char-write-req 0x0011 0100"; sleep 1
    for i in $(seq 0 255); do echo "char-write-req 0x000b $(printf %02x $i)"; sleep 1.5; done
    echo disconnect; echo exit ) | gatttool -I -t random -b $A | tee test.log
$ grep Notification test.log

It mostly worked, but with a couple of issues (see below). Most commands return nothing, here are the ones that do something:

CommandAnswers onAnswer
21152521 0b …, some dates and numbers
30Silently changes the device name, visible after next reboot; probably the command to set the name
31152531 0b 04 xx xx xx xx …, the 4 characters after BLE_Gamma:
40Fails, reboots the device, which re-appears as DfuTarg; it’s in firmware update mode, writing things now may damage it
a01525a0 0b …, we have covered this one already
a11526no command, no length, just data

So 1526 is only used by a1, which probably dumps the recorded measurements. I read twenty bytes of zeros after a reboot, then started a recording session and waited for the first few records to appear:

If a command requires data (like setting the name), we may have missed it, as it may not answer unless data is provided.

Anyway, guessing commands like that is tedious and can be dangerous as we just saw. Can we find a “shortcut”?

Comments Add one by emailing me.