Perda de dados após conectar o módulo bluetooth

Sep 09 2020

Objetivo

Estou tentando retornar dados do dispositivo BlueTooth depois de conectado porque para usar a função de leitura e gravação, preciso de alguns dados. Dados de exemplo name, overflowServiceUUIDs, solicitedServiceUUIDs, mtu, rssi...e muitos outros. Porque se quero ler ou escrever preciso de alguns atributos. Estou usando a biblioteca react-native-ble-plx.

O que está acontecendo?

Depois que o dispositivo foi conectado perdi alguns valores.

Importante

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.

As funções de sequência

toggleScanDevices ()

Colocará todos os dispositivos no bleDevicesestado.

  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 (nome)

  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 (nome)

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

Dentro da connectDevicefunção tenho um let deviceque recebo o valor sobre o findDeviceWhereNameContains, se logar essa variável devicerecebo muitos dados muito importantes, mas ainda não estou conectado. Então quando eu verificar sobre o if (!isConnected)aqui vou conectar, e depois disso, do avesso se ao logar devicenovamente perdi alguns valores.

O log antes de conectar

O log após conectar

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"

Respostas

1 diedu Sep 28 2020 at 12:42

Verificando a lib que você está usando, ele cria outro objeto de dispositivo a partir da resposta que obtém ao chamar o módulo nativo, pode ser o caso de esse novo objeto vir com valores nulos nesses campos e substituir os valores que você precisa. Como está exatamente como funciona agora, você pode copiar esses valores para outro objeto antes que eles sejam apagados

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);
  };

Mas você deve ter cuidado para que eles não sejam substituídos novamente.

Se você acha que isso é um comportamento falho de react-native-ble-plxvocê poderia abrir um PR com as alterações no Deviceconstrutor para evitar que isso aconteça.

Já que você está usando o texto datilografado, você terá algumas reclamações sobre tipificações, você pode criar um tipo a partir do tipo de react-native-ble-plxdispositivo e escolher apenas os atributos de parte dele para omitir métodos e evitar armazenar objetos complexos em seu estado.

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

type DeviceState = Pick<
    Device,
    | 'id'
    | 'name'
    | 'rssi'
    | 'mtu'
    | 'manufacturerData'
    | 'serviceData'
    | 'serviceUUIDs'
    | 'localName'
    | 'txPowerLevel'
    | 'solicitedServiceUUIDs'
    | 'isConnectable'
    | 'overflowServiceUUIDs'
  >