12

Objective

I am trying to return data from the BlueTooth device after connected because to use the read and write function, need some data. Example data name, overflowServiceUUIDs, solicitedServiceUUIDs, mtu, rssi... and many others. Because if I want to read or write I need some attributes. I am using the library react-native-ble-plx.

What is happening?

After the device connected I lost some values.

Important

type DeviceState = {
  connected: boolean;
  services: Service[];
  device: Device | null;
  characteristics: Record<string, Characteristic[]>;
};

const INITIAL_DEVICE_STATE = {
  connected: false,
  services: [],
  device: null,
  characteristics: {},
};
const [adapterState, setAdapterState] = useState(false);
const [bleDevices, setBleDevices] = useState<Device[]>([]);
const [isScanning, setIsScanning] = useState(false);
const [connectedDevice, setConnectedDevice] = useState<DeviceState>(
    INITIAL_DEVICE_STATE,
);

# The state isScaning is used to be if we are scanning devices.
# The connectedDevice state will be the connected device.

The sequence functions

toggleScanDevices()

Will push all devices to the bleDevices state.

  const toggleScanDevices = () => {
    setIsScanning(true);
    setBleDevices([]);

    bleManager.startDeviceScan(null, {}, (bleError, device) => {
      if (device && _.findIndex(bleDevices, { id: device.id }) < 0) {
        bleDevices.push(device);
        setBleDevices(bleDevices);
      }
    });

    setTimeout(() => {
      setIsScanning(false);
      bleManager.stopDeviceScan();
    }, 5000);
  };

toggleConnectDevice(device.name)

  const toggleConnectDevice = (name: string) => async () => {
    if (!connectedDevice.device) {
      await connectDevice(name);
    } else {
      const { device } = connectedDevice;

      if (!device) return;

      await device.cancelConnection();

      if (!(await device.isConnected())) {
        setConnectedDevice(INITIAL_DEVICE_STATE);
      }
    }
  };

connectDevice(name)

  const connectDevice = async (name: string) => {
    let device = findDeviceWhereNameContains(name);

    if (device === null) {
      setConnectedDevice(INITIAL_DEVICE_STATE);
      return false;
    }

    let isConnected = await device.isConnected();

    if (!isConnected) {
      /* Testar aqui */
      device = await bleManager.connectToDevice(device.id);
      isConnected = await device.isConnected();
    }
    device = await device.discoverAllServicesAndCharacteristics();

    device.onDisconnected((error, device) => {
      setConnectedDevice(INITIAL_DEVICE_STATE);
    });

    const services = await device.services();
    const characteristics: Record<string, Characteristic[]> = {};
    const descriptors = {};

    _.forEach(services, async service => {
      const deviceCharacteristics = await device?.characteristicsForService(
        service.uuid,
      );
      characteristics[service.uuid] = deviceCharacteristics || [];
    });

    setConnectedDevice(state => ({
      ...state,
      services,
      characteristics,
      device,
    }));

    const newDevice = { ...connectedDevice, device };
    setConnectedDevice(newDevice);
    console.log('não atualizado', connectedDevice);
    console.log('novo valor', newDevice);
  };

findDeviceWhereNameContains(name)

  const findDeviceWhereNameContains = (name: string) => {
    const device = bleDevices.find(item => String(item.name).includes(name));
    if (device !== undefined) {
      return device;
    }
    return null;
  };

Inside the connectDevice function I have a let device that receive the value about the findDeviceWhereNameContains, if I log this variable device I receive many data very important, but I'm not connected yet. So when I verify about the if (!isConnected) here I will connect, and after this, inside out this if when I log the device again I lost some values.

The log before connect

The data before the connection

The log after connect

Device {overflowServiceUUIDs: null, solicitedServiceUUIDs: null, localName: null, isConnectable: null, txPowerLevel: null, …}
overflowServiceUUIDs: null
solicitedServiceUUIDs: null
localName: null
isConnectable: null
txPowerLevel: null
serviceUUIDs: null
serviceData: null
mtu: null
name: "MLT-BT05"
manufacturerData: null
rssi: null
id: "88:25:83:F0:30:BC"
diedu
  • 19,277
  • 4
  • 32
  • 49
Vagner Wentz
  • 391
  • 1
  • 7
  • 29
  • 2
    Well, I think if you re-write the above code using `async-await` the problem will be solved. As a general notice don't mix the sync and async. – Eldar Sep 11 '20 at 11:17
  • @Eldar I tried this but did not work. – Vagner Wentz Sep 15 '20 at 13:44
  • @VagnerWentz, if you are using an older version of android, there is a [known bug where *The service discovery is resolved with an empty array of services without any error*](https://github.com/Polidea/react-native-ble-plx/wiki/Device-Service-Discovery)(third Note) – Elias Schablowski Sep 16 '20 at 21:21
  • @EliasSchablowski Hi Elias, how are you? I saw about the cellphone that I am using, the Android Version is 6.0.1, do you think that it can be a problem? – Vagner Wentz Sep 17 '20 at 02:24
  • @EliasSchablowski I tried using version 10 and did not work too – Vagner Wentz Sep 17 '20 at 02:33
  • @VagnerWentz, I just saw the problem (though never encountered it myself) and thought it could apply. Realistically there are only two other possibilities, that either the device is advertising a service it does't have (or at least says it doesn't) or that there is a bug in *react-native-ble-plx*, and you may want to open an issue there as the code seems to be correct (though it could use a little cleanup). – Elias Schablowski Sep 17 '20 at 06:03
  • @EliasSchablowski Hi Elias, do you know if there is some possibility to know if the cellphone is paired with the module Bluetooth? Because we have two states, the first is connected and the second is paired. – Vagner Wentz Sep 17 '20 at 14:18
  • 1
    I think you shouldn't mix `.then()` with `async` and `await` syntax, try to convert those to `await` so you have a better perspective with a clearer code – diedu Sep 28 '20 at 05:15
  • if at all you want to continue using promises, you should rewite it to device.connect().then(d => d.discoverAllServicesAndCharacteristics) .then(d => {console.log(d.id)}) – Amit Oct 01 '20 at 02:07
  • @SenguptaAmit I refactored all of my code I will edit – Vagner Wentz Oct 01 '20 at 13:04

1 Answers1

2

Checking the lib you're using, it creates another device object from the reponse it gets when calling the native module, it could be the case this new object comes with null values on those fields and replaces the values you need. Because it's just as it works by now, you could copy those values to another object before they get erased

import pick from 'lodash/pick';

const connectDevice = async (name: string) => {
    let device = findDeviceWhereNameContains(name);
    // according to the screenshot, the object still have the
    // information you want at this point
    
    // taking advantage taht you have lodash already you can use pick
    const valuesYouNeed = pick(device, ['serviceUUIDs', ...]);

    ...

    // here you merge them back with other device's attr that are present at this point
    const newDevice = { ...pick(device, ['connected', ...]), ...valuesYouNeed };
    setConnectedDevice(newDevice);
    console.log('não atualizado', connectedDevice);
    console.log('novo valor', newDevice);
  };

But you have wary they don't get replaced again.

If you think this is a faulty behaviour of react-native-ble-plx you could open a PR with the changes in the Device constructor to avoid this happening.

Since you're using typescript, you'll have some complaints about typings, you could create a type from react-native-ble-plx device type and pick only the attributes part of it to omit methods and avoid storing complex object in your state.

import { Device } from 'react-native-ble-plx`;

type DeviceState = Pick<
    Device,
    | 'id'
    | 'name'
    | 'rssi'
    | 'mtu'
    | 'manufacturerData'
    | 'serviceData'
    | 'serviceUUIDs'
    | 'localName'
    | 'txPowerLevel'
    | 'solicitedServiceUUIDs'
    | 'isConnectable'
    | 'overflowServiceUUIDs'
  >
diedu
  • 19,277
  • 4
  • 32
  • 49
  • Hey did you edit your comment about the new edition that I did? – Vagner Wentz Oct 04 '20 at 14:57
  • @VagnerWentz what I edited was my answer, previously I just refactored your code with `await`s, now I'm proposing to store the values before they get lost – diedu Oct 04 '20 at 18:50
  • Ok I will test and if is right I will give to you the bounty – Vagner Wentz Oct 04 '20 at 19:11
  • Where do you think that I need to put at my code your example? – Vagner Wentz Oct 04 '20 at 19:27
  • One doubt, at the part that you say `create another devices` what did you mean? – Vagner Wentz Oct 04 '20 at 20:11
  • @VagnerWentz I was talking about the code on the library, if you check the link you'll see what I meant, they instantiate a new device object `new Device(nativeDevice, this)` with the response from the native module, which probably have null values in the fields you need – diedu Oct 04 '20 at 20:37
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/222498/discussion-between-vagner-wentz-and-diedu). – Vagner Wentz Oct 04 '20 at 21:38