Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stuck on retrieveServices (not connected after connection?) #831

Open
Silvesterrr opened this issue Feb 10, 2022 · 30 comments
Open

Stuck on retrieveServices (not connected after connection?) #831

Silvesterrr opened this issue Feb 10, 2022 · 30 comments

Comments

@Silvesterrr
Copy link

Describtion
Hi, I've got this annoying bug it appears inconsistently every few times. It's really similar to this Issue [#402]
(#402)
It looks something like this:

  • Connect do device
  • Retrieve services -> and at this point the code hangs up
    I never found out this bug after first run of my app (first connect). Only at second, etc. connection.

I can't find any mistakes because one time it works like a charm and other time it has a problem.
I found out that disabling and enabling Bluetooth on the phone and retrying connecting works
(But I can't make my users to disable and enable Bluetooth. Or can I ;) ha-ha).

My code
I've tried writing my code in 2 ways both have this bug.

async function internalConnect() {
    await BleManager.connect(item.id);
    console.log('after conn');
    await BleManager.retrieveServices(item.id);
    console.log('after retrive');  // <---if the bug appears I never get to see this log
    await BleManager.write(
      item.id,
      global.GATTS_SECURITY_UUID,
      global.GATTC_ACCESS_CODE_UUID,
      stringToIntArray(code),
    );
    console.log('after write');
    await BleManager.read(
      item.id,
      global.GATTS_SECURITY_UUID,
      global.GATTC_ACCESS_CODE_UUID,
    )
      .then(readData => {
        console.log('access code response: ' + readData);
       //My other stuff here
      })
      .catch(error => {
        // Failure code
        console.log(error);
      });
  }

And this way:

BleManager.connect(item.id)
      .then(() => {
        console.log('connected to device');
        setTimeout(() => {
          BleManager.isPeripheralConnected(item.id, []).then(isConnected => {
            //do not remove
            if (isConnected) {
              console.log('Peripheral is connected!');
            } else {
              console.log('Peripheral is NOT connected!');
              Connect(); //<--- run the same function
              return;
            }
          });
        }, 900);
        setTimeout(() => {
          console.log('timeout');

          BleManager.retrieveServices(item.id)
            .then(PeripheralInfo => {
              console.log(PeripheralInfo);

              BleManager.write(
                item.id,
                global.GATTS_SECURITY_UUID,
                global.GATTC_ACCESS_CODE_UUID,
                stringToIntArray(code),
              )
                .then(() => {
                  console.log('written');
                  BleManager.read(
                    item.id,
                    global.GATTS_SECURITY_UUID,
                    global.GATTC_ACCESS_CODE_UUID,
                  )
                    .then(readData => {
                      console.log('access code response: ' + readData);
                      //My stuff here
                    })
                    .catch(error => {
                      handleConnectionErrors(error);
                      return;
                    });
                })
                .catch(error => {
                  handleConnectionErrors(error);
                  return;
                });
            })
            .catch(error => {
              handleConnectionErrors(error);
              return;
            });
        }, 900);
      })
      .catch(error => {
        handleConnectionErrors(error);
        return;
      });

In this way when the bug appears after running isPeripheralConnected I get Peripheral is NOT connected! So I've tried to resolve this by running this function again, but it just loops in this place and doesn't work.

How to Reproduce
Accually I don't know. It appears so random, I can't control it. I only know that it appears at second or next connections.

Smartphone:

  • Device: xiaomi redmi note 7
  • OS: android 10 QKQ1.190910.002
  • react-native-cli: 2.0.1
  • react-native: 0.66.4
  • ble-manager: 8.0.1s

Btw, I really like this library. It's really intuitive :)

@liuliu66666
Copy link

same issue

@michael-jogo
Copy link

@Silvesterrr any update on this?

@Silvesterrr
Copy link
Author

@Silvesterrr any update on this?

Nope, am focused on something different...
But I'll be back onto it soon.
If I figure out anything new I'll post it.

@manxhong
Copy link

manxhong commented Jun 3, 2022

@Silvesterrr I had the same hanging issue with Android only, iOS works perfectly fine to me. There is a workaround I did that solves the issue. Try to createBond before the connect :

if (Platform.OS === 'android') {
   await BleManager.createBond(item.id);
  // you may want to add some delay after create bond
   await BleManager.connect(item.id);
}

Hope this helps.

@sarevok89
Copy link

Is there any update on this issue? I'm currently struggling with the same issue.

@sundayhd
Copy link

sundayhd commented Nov 19, 2022

Same problem on xiaomi pad 5 😒

I tried so many things like clear bluetooth cache / binding etc.. I wasted few hours on this.

As workaround im wating at least 1 sec and if there is no connection (not getting "retrieveServices") I trigger disconnect and then reconnect again every time.
something like this (look at "handleHangOutBug" function):

const connectToDevice = async (id) => {
    try {
      await BleManager.connect(id);
    }
    catch(error) {
      handleError(error);
      return connectToDevice(id);
    }
    Log("Weight", "setDevice", "Connected to:", id);
    clearTimeouts();
    setConnectionStatus(1);
    currentDeviceId.current = id;
    retrieveServices(id);
    handleHangOutBug(id);
  }

  const handleHangOutBug = (id) => {
    hangOutTimeout.current = setTimeout(() => {
      if (connectionStatus.current === 1) {
        hangOutretries.current = hangOutretries.current + 1;
        isConnectingProcess.current = true;
        disconnect().then(() => {
          connectToDevice(id);
        });
      }
    }, (1000 + (hangOutretries.current * 250)));
  }

Please let me know if you have better solutions, thanks 😀

@sundayhd
Copy link

sundayhd commented Nov 22, 2022

Toady I played with "bluetooth MAP version & AVRCP version" inside developer options.
When I changed map version to 1.3 its looks like that problem go away, so give it a shot 😃

@Silvesterrr
Copy link
Author

Toady I played with "bluetooth MAP version & AVRCP version" inside developer options. When I changed map version to 1.3 its looks like that problem go away, so give it a shot 😃

yea, fun fact but I cannot force user to change that. Anyone tried using "react-native-ble-plx"?
Does this library has this issue?

@sundayhd
Copy link

Toady I played with "bluetooth MAP version & AVRCP version" inside developer options. When I changed map version to 1.3 its looks like that problem go away, so give it a shot 😃

yea, fun fact but I cannot force user to change that. Anyone tried using "react-native-ble-plx"?
Does this library has this issue?

I already tried ble-plx, same issue 😟

@RuchiraSwarnapriya
Copy link

Hi @Silvesterrr, I'm also facing similar kind of issue,
In very first time BleManager.connect return this error "Connection error" but if I try again it connected without any issue

Did you found a solution for this issue ?

@Silvesterrr
Copy link
Author

Did you found a solution for this issue ?
Sorry I did not. I kinda hacked the way throught by disabling bluetooth and enabling it when the problem occures with other library. But I hope this issue will be resolved because it's so frustrating.

@JoshuaGuzman02
Copy link

Mismo problema, alguna solución?

@sfsnaruto
Copy link

Same issue

@KonstantinKostianytsia
Copy link

The same issue

@maeda-kazuya
Copy link

Same here, any solutions for this?

@sfsnaruto
Copy link

@maeda-kazuya , @KonstantinKostianytsia try to run the scan method first and then call the connect method.

@maeda-kazuya
Copy link

Thanks, I just came up with some workaround.
I added retry process as follows and it works for now.
(Still wondering the root cause of failure..)

    let isConnected = false;

    while (!isConnected) {
      await BleManager.connect(id)
        .then(() => {
          isConnected = true;
        })
    }

@cfradella
Copy link

Also encountering this issue. It'll stall on retrieveServices until something else interrupts it.

@froggydisk
Copy link

froggydisk commented Sep 14, 2023

I don't know if this could be an answer, but let me share my code.
Before, you should see two parts of the original code.

// react-native-ble-manager > src > types.ts
export interface PeripheralInfo extends Peripheral {
  serviceUUIDs?: string[];
  characteristics?: Characteristic[];
  services?: Service[];
}
// react-native-ble-manager > src > index.ts
retrieveServices(peripheralId: string, serviceUUIDs: string[] = []) {
    return new Promise<PeripheralInfo>((fulfill, reject) => {
      bleManager.retrieveServices(
        peripheralId,
        serviceUUIDs,
        (error: string | null, peripheral: PeripheralInfo) => {
          if (error) {
            reject(error);
          } else {
            fulfill(peripheral);
          }
        }
      );
    });
  }

When you use retrieveServices, it will return the result 'peripheral', whose elements' types are defined by Interface 'PeripheralInfo'. As you can see, the original code makes PeripheralInfo extend Peripheral, and Interface 'Peripheral' is as below.

export interface Peripheral {
  id: string;
  rssi: number;
  name?: string;
  advertising: AdvertisingData;
}

These say, the result of retrieveServices must have attributes 'id', 'rssi', and 'advertising' while others are optional.
BUT, what if the result does NOT have the attributes?
You'd better not force it to distribute the information of what it may not have.

So, here is the part of my code.

export interface PeripheralInfo { // -> this is what I fixed
  id: string; // -> this is what I added
  serviceUUIDs?: string[];
  characteristics?: Characteristic[];
  services?: Service[];
}

In my code, PeripheralInfo does not extend Peripheral anymore. Instead, the result still contains 'id', because you will offer it when you call retrieveServices function.
You can put other options if you want, like this.

rssi?: number;
name?: string;
advertising?: AdvertisingData;

Don't forget to make them optional by using '?'

@2sem
Copy link

2sem commented Sep 15, 2023

sometimes connecting is fast, but retrieveServices takes over 30 seconds.
And then disconnecting also takes much long time.

@RakeebBaig1

This comment was marked as off-topic.

@pernestrand
Copy link

My current workaround is something like this, where I basically race it against another promise. If the other promise resolves first I consider it a timeout and handle it accordingly.

const tryGetServices = await Promise.race([
	BleManager.retrieveServices(peripheral.id),
	new Promise((resolve, reject) =>
		setTimeout(() => {
			resolve('timeout')
		}, 10000)
	),
])

const peripheralData = tryGetServices as PeripheralInfo | string

if (typeof peripheralData === 'string') {
	// timeout
} else {
	// got services, proceed...
}

@Loovery
Copy link

Loovery commented Dec 24, 2023

Any update? I have same problem after upgrade version 8.x.x to 11.x.x

@Loovery
Copy link

Loovery commented Dec 26, 2023

What I found out.

  1. The problem does not occur on all devices. 100% of the problem is reproduced with older Wahoo kickr (CyclingPower Service).
  2. The device connects without problems. All data is read. The problem is exactly when retrieveServices is requested. It just hangs without any response.

The function retrieveServices, reaches the code:

if !uuids.isEmpty {
                peripheral.instance.discoverServices(uuids)
            } else {
                peripheral.instance.discoverServices(nil)
            }

Nothing further

service: 1818
characteristic: a026e005-0a7d-4ab3-97fa-f1500f9feb8b

react-native: 0.71.11
react-native-ble-manager: 11.0.7

@Loovery
Copy link

Loovery commented Dec 27, 2023

Version 11.0.8 fixed my problem

@vhakulinen
Copy link
Contributor

I just ran across this issue with a peripheral that is already "connected" (i.e. BleManager.isPeripheralConnected returns true).

It looks like that the command queue is the one that drops the ball. If the gatt is null, then it just bails out and doesn't invoke any pending callbacks with errors.

@dejawho
Copy link

dejawho commented May 13, 2024

same problem, if the device is really connect retrieveService never return

@vhakulinen
Copy link
Contributor

The Android stack (at least) requires that you call connectGatt to get the required object to do any BLE stuff. So you'll have to call connectGatt in your code at some point even if the bluetooth stack reports that the peripheral is connected. So make sure to call connect on the peripheral before trying to retrieveServices (tho' that doesn't mean that the call should just hang).

@lzagar-itm
Copy link

lzagar-itm commented May 27, 2024

Running version 11.4.0 and still having issue. It hangs on retrieveServices, I tried to refresh cache, disconnect before I connect, put timeout before calling retrieveServices but still no luck. I looked at native code and onServicesDiscovered never gets called. Strange thing is that I can connect first time to it but after that I have to remove and add device again. I got Google Pixel 8 running Android 14. If creator someone has any input why this might be happening or where to start to look keep me posted

@keyre
Copy link

keyre commented Jul 12, 2024

My current workaround is something like this, where I basically race it against another promise. If the other promise resolves first I consider it a timeout and handle it accordingly.

const tryGetServices = await Promise.race([
	BleManager.retrieveServices(peripheral.id),
	new Promise((resolve, reject) =>
		setTimeout(() => {
			resolve('timeout')
		}, 10000)
	),
])

const peripheralData = tryGetServices as PeripheralInfo | string

if (typeof peripheralData === 'string') {
	// timeout
} else {
	// got services, proceed...
}

It works on android!

My solution is:

 myRetrieveServices = async () => {
    return await Promise.race([
      BleManager.retrieveServices(this.deviceConnected.id),
      new Promise((resolve, reject) => setTimeout(reject, 3000)),
    ]);
  };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests