currantlabs / ble

BSD 3-Clause "New" or "Revised" License
210 stars 177 forks source link

Got an unregistered notification || CCCD not found #73

Open DazWilkin opened 4 years ago

DazWilkin commented 4 years ago

I bought a bunch of Xiaomi Mijia BLE Thermometers (LYWSD03MMC)

I have been trying (unsuccessfully) to retrieve data from these.

I have 2 possibly related issues:

Got an unregistered notification

After Dialing the devices, I begin receiving Got an unregistered notification and I'm unable to determine how I can register a notification handler to avoid this.

There appears no way to define a (default) notification handler before|during Dial.

CCCD not found

So, I thought I could try to enumerate all the services and their characteristics and try registering handlers if I encounter characteristics that support this. But, when I find characteristics that support notifications, I receive "CCCD not found"

I removed filters attempting to find any characteristic in the device's GATT that supports both notifications and has CCCD but am unable to find any. Does this mean the device's GATT is at fault?

I'm basing my code on the repo's explorer example

What am I doing wrong?

ctx := ble.WithSigHandler(context.WithTimeout(context.Background(), 1*time.Minute))
bleClient, err := linuxDevice.Dial(ctx, ble.NewAddr(addr))
defer bleClient.CancelConnection()

services, err := bleClient.DiscoverServices(filterServices)

for _, s := range services {
    characteristics, err := bleClient.DiscoverCharacteristics(filterCharacteristics, s)

    for _, c := range characteristics {
        if (c.Property & ble.CharNotify) != 0 {
            if c.CCCD == nil {
                continue
            }
            handler := func(req []byte) {
                log.Printf("%s Service: %s Characteristic: %s (%04x) Notified: %v", addr, s.UUID, c.UUID, c.Handle, req)
            }
            if err := bleClient.Subscribe(c, false, handler); err != nil {
                log.Fatal(err)
            }
        }
        }
    }
}

NB Error handling removed for succinctness

NB I tried replicating for indication handlers without difference

Here's an example output tidied for ease of grokking:

S: 1800 C: 2a00 (0002)
S: 1800 C: 2a00 (0002) Supports Notifications
S: 1800 C: 2a00 (0002) CCCD not found
S: 1800 C: 2a01 (0004)
S: 1800 C: 2a04 (0006)
S: 1801 C: 2a05 (0009)
S: 1801 C: 2a05 (0009) Supports Indications
S: 1801 C: 2a05 (0009) CCCD not found
S: 180a C: 2a24 (000d)
S: 180a C: 2a25 (000f)
S: 180a C: 2a26 (0011)
S: 180a C: 2a27 (0013)
S: 180a C: 2a28 (0015)
S: 180a C: 2a29 (0017)
S: 180f C: 2a19 (001a)
S: 180f C: 2a19 (001a) Supports Notifications
S: 180f C: 2a19 (001a) CCCD not found
S: 000102030405060708090a0b0c0d1912 C: 000102030405060708090a0b0c0d2b12 (001e)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccb77a0a4b0c8a1a6ff2997da3a6 (0022)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccb97a0a4b0c8a1a6ff2997da3a6 (0025)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccba7a0a4b0c8a1a6ff2997da3a6 (0028)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccbb7a0a4b0c8a1a6ff2997da3a6 (002b)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccbc7a0a4b0c8a1a6ff2997da3a6 (002e)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccbc7a0a4b0c8a1a6ff2997da3a6 (002e) Supports Notifications
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccbc7a0a4b0c8a1a6ff2997da3a6 (002e) CCCD not found
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccbe7a0a4b0c8a1a6ff2997da3a6 (0032)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccc17a0a4b0c8a1a6ff2997da3a6 (0035)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccc17a0a4b0c8a1a6ff2997da3a6 (0035) Supports Notifications
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccc17a0a4b0c8a1a6ff2997da3a6 (0035) CCCD not found
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccc47a0a4b0c8a1a6ff2997da3a6 (0039)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccc87a0a4b0c8a1a6ff2997da3a6 (003c)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccd17a0a4b0c8a1a6ff2997da3a6 (003f)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccd77a0a4b0c8a1a6ff2997da3a6 (0042)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccd87a0a4b0c8a1a6ff2997da3a6 (0045)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccd97a0a4b0c8a1a6ff2997da3a6 (0048)
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccd97a0a4b0c8a1a6ff2997da3a6 (0048) Supports Notifications
S: ebe0ccb07a0a4b0c8a1a6ff2997da3a6 C: ebe0ccd97a0a4b0c8a1a6ff2997da3a6 (0048) CCCD not found
S: fe95 C: 0004 (004d)
S: fe95 C: 0010 (0050)
S: fe95 C: 0010 (0050) Supports Notifications
S: fe95 C: 0010 (0050) CCCD not found
S: fe95 C: 0017 (0054)
S: fe95 C: 0017 (0054) Supports Notifications
S: fe95 C: 0017 (0054) CCCD not found
S: fe95 C: 0018 (0058)
S: fe95 C: 0018 (0058) Supports Notifications
S: fe95 C: 0018 (0058) CCCD not found
S: fe95 C: 0019 (005c)
S: fe95 C: 0019 (005c) Supports Notifications
S: fe95 C: 0019 (005c) CCCD not found
S: 0000010000656c622e746f696d2e696d C: 0000010100656c622e746f696d2e696d (0061)
S: 0000010000656c622e746f696d2e696d C: 0000010200656c622e746f696d2e696d (0065)
S: 0000010000656c622e746f696d2e696d C: 0000010200656c622e746f696d2e696d (0065) Supports Notifications
S: 0000010000656c622e746f696d2e696d C: 0000010200656c622e746f696d2e696d (0065) CCCD not found
2020/04/15 14:13:50 Got an unregistered notification
2020/04/15 14:13:51 Got an unregistered notification
2020/04/15 14:13:52 Got an unregistered notification
2020/04/15 14:13:53 Got an unregistered notification
2020/04/15 14:13:56 Got an unregistered notification
2020/04/15 14:13:57 Got an unregistered notification
DazWilkin commented 4 years ago

OK... Some progress.

I discovered (!) that I can access CCCDs if I, at least:

_, err := bleClient.DiscoverDescriptors(nil, c)
DazWilkin commented 4 years ago

OK. I think I've finally solved it :-)

I'm still unclear why the characteristic is not enumerated and I had to hack this solution by using gatt to guess (!) the mapping from the handles to the characteristic UUIDs but...

gatttool -b ${DEVICE} --char-write-req --handle=0x0038 --value=0100 --listen
Characteristic value was written successfully
Notification handle = 0x0036 value: 24 08 2d 2e 0c 
Notification handle = 0x0036 value: 1f 08 2d 2e 0c 
Notification handle = 0x0036 value: 25 08 2d 2e 0c 

NB Write to 0x0038 but notifications from 0x0036.

If I do gatttool -b ${DEVICE} --char-desk, I get:

handle = 0x0036, uuid = ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6
...
handle = 0x0038, uuid = 00002902-0000-1000-8000-00805f9b34fb

So I hacked the write and appear to now receive notifications:

handle38 := "00002902-0000-1000-8000-00805f9b34fb"
characteristic38, err := ble.Parse(handle38)
if err != nil {
    log.Print(err)
}
if p, err := bleClient.DiscoverProfile(true); err != nil {
    if u := p.Find(ble.NewCharacteristic(characteristic38)); u != nil {
        if err := bleClient.WriteCharacteristic(u.(*ble.Characteristic), []byte{0x01, 0x00}, false); err != nil {
            log.Print(err)
        }
    }
}

I'm unsure why the notification handler is one off, but I'll take it:

(0035) Notified: [94 8 43 251 11]
(0035) Notified: [83 8 44 36 12]
(0035) Notified: [74 8 43 206 10]
(0035) Notified: [89 8 43 24 12]
(0035) Notified: [93 8 43 251 11]
(0035) Notified: [83 8 44 36 12]
(0035) Notified: [72 8 43 206 10]
st3fan commented 3 years ago

@DazWilkin unrelated to this project, but I've found the most power efficient way to get data out of those LYWSD03MMC sensors is to flash them with the custom firmware and then just listen to their advertisement broadcasts instead of connecting and querying the devices. The custom firmware puts battery, temperature and humidity in the advertisement data, which makes things much simpler for constant monitoring.

DazWilkin commented 3 years ago

@DazWilkin unrelated to this project, but I've found the most power efficient way to get data out of those LYWSD03MMC sensors is to flash them with the custom firmware and then just listen to their advertisement broadcasts instead of connecting and querying the devices. The custom firmware puts battery, temperature and humidity in the advertisement data, which makes things much simpler for constant monitoring.

Thanks @st3fan! I've been reluctant to use the bindkey approach with the original firmware which is why I was try this approach. I recently read about the custom firmware and have been meaning to try it.