abandonware / noble

A Node.js BLE (Bluetooth Low Energy) central module : Community maintained
https://libraries.io/npm/@abandonware%2Fnoble
MIT License
535 stars 162 forks source link

Disconnection event received immediately after Connection on Raspberry Pi 3 B+ #62

Open javiermelendezc opened 4 years ago

javiermelendezc commented 4 years ago

I'm currently trying to connect to one of my peripherals using @abandonware/noble, As soon as a connection is established, i call discoverServices() and most of the time the connection falls immediately. I use event-based transitions using Statecharts, so I'm sure there's no overlapping of commands during the connection. It's totally related to: https://github.com/noble/noble/issues/465 Where some people think it's because BLE and WiFi are on the same chipset on RPi 3B+ and RPi Zero. Although these boards work well on bluepy according to someone on the thread. So I think the problem it's related to hci. Any ideas on how to solve this?

endrelovas commented 4 years ago

I added 250 msec delay after connection and the issue has gone. Sounds silly but works.

sync() {
        let swinger = this;
        this.downloadedData = [];
        return new Promise((resolve, reject) => {
            swinger.bleDevice.once('disconnect', () => {
                setTimeout(async () => {
                    common.logger.info(`Device: disconnected. Data length: ${this.downloadedData.length}`)
                    let firstN = this.downloadedData.slice(0, 42000)
//                common.logger.info(`Device: Data: ${common.toHexString(firstN)}`)
                    let digest = crypto.createHash('sha256').update(Buffer.from(firstN)).digest('hex')
                    common.logger.info(`HASH: ${digest}`)
                    resolve(this.downloadedData);
                }, 1000)
            })

            swinger.bleDevice.once('connect', () => {
                common.logger.info(`Device: Connected`)
                swinger.downloadData().then(success => {
                }, error => {
                    swinger.disconnect().then(null);
                });
            })

            swinger.bleDevice.connect((error) => {
                if (error != null) {
                    common.logger.error(`Device: could not connect: ${error.toString()}`)
                    reject(error);
                }
            });
        })
    }

    async downloadData() {
        let swinger = this
        return new Promise((async (resolve, reject) => {
            try {
                setTimeout( async  () => {
                    swinger.characteristic = await swinger.getMyCharacteristics();
                    swinger.waitForData();
                    setTimeout(async () => {
                        await swinger.signUpForCharacteristic()
                        resolve()
                    }, 250)
                }, 250)
            } catch (e) {
                common.logger.error(`Device: Download data failed`)
                reject(e)
            }
        }));
    }
javiermelendezc commented 4 years ago

Hey @endrelovas thanks for the feedback Could you elaborate? I’m already using abandonware/noble. Same behaviour, the device disconnects as soon as a connection is established. The delay doesn’t seem to mitigate the problem everytime, it’s not a consistent solution. And seems kinda buggy When the connection is established properly everything works as expected, but sometimes it requires 10+ attempts (connects-disconnects) I haven’t seen a thread that clearly fix this problem in noble or in abandonware/noble, and it seems to have been reported multiple times.

endrelovas commented 4 years ago

Honsetly I cannot ellaborate any further as I pasted my source as well. I added 250 msec delay before every command I execute. This should not be the solution imho but it seems to work.

javiermelendezc commented 4 years ago

I've been conducting some testing, and my code seems to work the same with and without the 250ms delay. As many said on the noble thread, there's an event from the module side that isn't properly handled by noble, and causes a disconnection. It's not deterministic, so I just "wait" for the connection to be established (I've seen even 50+ retries until a connection is made) Yes @endrelovas , I agree on that, this shouldn't be considered a solution. Hope we can find a stable solution soon.

endrelovas commented 4 years ago

To restart this thread a bit: I start to suffer as you do on a Raspi 4. Random disconnections. Honestly I start to be frustrated a bit on this issue as we are at the end of a project and the test shows your experience. Have you resolved meanwhile?

endrelovas commented 4 years ago

@javiermelendezc playing a bit: no need to execute discoverServices at all. Just connect and you will get an instant disconnect. You probably have the feeling that you are getting the disconnect at that message but without calling it I end up with an immediate disconnection.

Furthermore: the peripheral won't consider it as a connection neither. It seems to be a false positive connection followed by a false negative disconnection message. Quite disturbing and the codes are in hcidump -R as well (04 05 04 00 40 00 3E) meaning that 04 05 => disconnected.

Looking into BluetoothHciSocket.cpp there is an if statement, exactly for this, under BluetoothHciSocket::kernelDisconnectWorkArounds.

else if (length == 7 && data[0] == 0x04 && data[1] == 0x05 && data[2] == 0x04 && data[3] == 0x00) {
    unsigned short handle = *((unsigned short*)(&data[4]));

    if (this->_l2sockets.count(handle) > 0) {
      close(this->_l2sockets[handle]);
      this->_l2sockets.erase(handle);
    }

I do not know what this does here but it seems that it closes the connection.

mrstegeman commented 4 years ago

This may be related: https://github.com/noble/node-bluetooth-hci-socket/pull/39

javiermelendezc commented 4 years ago

Hey @endrelovas You’re right, the connection falls immediatley, and honestly I’m not sure how to handle this. Have you found any other alternative to substitute noble. As you said it’s very frustrating. I also noted it’s a hci problem but I haven’t come as deep as you did, maybe in this way is easier for other people to help. @rzr any ideas on how to solve this or how can we help?

endrelovas commented 4 years ago

@javiermelendezc Since that I made a step forward. All I do is I wait 250 msec prior to send the next command. If connection falls I just reconnect. I made a counter up to 5 tries. Worst was 2 attempts (cycles) so far. I tried about 100 connection now. Hacky I know... :-(

endrelovas commented 4 years ago

This may be related: noble/node-bluetooth-hci-socket#39

Seems so yes!

rzr commented 4 years ago

Is this specific to any (others) adapters ?

javiermelendezc commented 4 years ago

Is this specific to any (others) adapters ?

I haven't tried any external adapters. other than my Macbook Air and the Raspberry Pi 3 B+. But it seems a regular bug on Raspberry Pi 3 specifically according to original noble issues.

endrelovas commented 4 years ago

Is this specific to any (others) adapters ?

I have no idea to be honest. This was the end of the communication in hcidump. I beleive the issue is shown on the 5th bottom line. 0405 = disconnected. Not sure where it became connected, perhaps 040E two lines above?

04 0E 04 01 0C 20 00 04 3E 21 02 01 00 01 67 30 36 CB 0D E8 15 02 01 06 11 09 50 72 69 76 61 74 44 6F 6B 74 6F 72 20 45 4B 47 B9 04 3E 1E 02 01 04 01 67 30 36 CB 0D E8 12 11 07 9E CA DC 24 0E E5 A9 E0 93 F3 A3 B5 01 00 40 6E B9 < 01 0C 20 02 00 01 04 0E 04 01 0C 20 00 < 01 0D 20 19 60 00 30 00 00 01 67 30 36 CB 0D E8 00 06 00 0C 00 00 00 C8 00 04 00 06 00 04 0F 04 00 01 0D 20 04 3E 13 01 00 40 00 00 01 67 30 36 CB 0D E8 0C 00 00 00 C8 00 00 < 01 16 20 02 40 00 04 0F 04 00 01 16 20 04 0E 0E 01 16 20 00 00 00 00 00 00 00 00 00 00 00 04 3E 0C 04 3E 40 00 3F 00 00 08 00 00 00 00 > 04 05 04 00 40 00 3E < 02 40 00 07 00 03 00 04 00 02 00 01 04 13 05 01 40 00 01 00 < 01 06 04 03 00 00 13 04 0F 04 02 01 06 04

javiermelendezc commented 4 years ago

@endrelovas have you tried with a different version of Raspbian or another Raspberry Pi model?, I don't have any other board on hand. Did you managed to get your project going with the 250ms delay approach or are you trying something new? Any ideas on how we should handle the hcidump event that you mention?

endrelovas commented 4 years ago

@javiermelendezc I tried on Raspi3 and Raspi4. AFAK they have different BT adapters.

endrelovas commented 4 years ago

@javiermelendezc

@endrelovas have you tried with a different version of Raspbian or another Raspberry Pi model?, I don't have any other board on hand.

I tried on Raspi3 and Raspi4. AFAK they have different BT adapters.

Did you managed to get your project going with the 250ms delay approach or are you trying something new?

I tried to make a recursive approach: keep connecting if 250 msec is not engough. It resolved some of my issues but after a while it broke apart.

Any ideas on how we should handle the hcidump event that you mention?

Frankly I do not have the knowledge to interpret hcidump correctly. I just try to narrow down the issue to those who understand what to do.

I try to find the purpose of BluetoothHciSocket::kernelDisconnectWorkArounds (@rzr perhaps you can explain?) If I understand I may try to modify the C++ code so the PI gets connected. Asking because the problem is not related to discoverServices for sure.

javiermelendezc commented 4 years ago

Indeed, the problem is not related to discoverServices call, and as we've seen is a false "connection-disconnection" coming from the adapter. I just changed the title to reflect that.

Frankly I do not have the knowledge to interpret hcidump correctly. I just try to narrow down the issue to those who understand what to do.

I try to find the purpose of BluetoothHciSocket::kernelDisconnectWorkArounds (@rzr perhaps you can explain?) If I understand I may try to modify the C++ code so the PI gets connected. Asking because the problem is not related to discoverServices for sure.

Great work at digging into the problem @endrelovas , Thanks!

devorrah commented 4 years ago

I am having the same exact problem. -Ubuntu 18.04 -Node v10.21.0 -"@abandonware/noble": "^1.9.2-9"

However the connections are real. I'm testing the peripheral-explorer.js example and connecting to an ESP32 that blinks slowly when a connection is made and quickly when the connection is broken. I'm trying to pair the two so that the esp32 may NOTIFY the Ubuntu/Raspberry Pi 4 when a sensor value changes.

Has a solution been found or have any of you considered switching to Bluepy? https://github.com/noble/noble/issues/465#issuecomment-252343713

javiermelendezc commented 4 years ago

Hey @devorrah , sorry for the late response.

I haven't reached a proper solution for this. I implemented a brute force approach on user side:

Looking forward to read what you did to get over this.

risnub commented 4 years ago

I would like to share similar experience. I also worked around it. If this immediate-disconnect persists, the program quits and the calling script would reset the bluetooth by doing hciconfig down and up.

I noticed something interesting. This problem happens on Raspian Buster but I have not seen this on Ubuntu Core 18.