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

Event Handling Mix-up in ArduinoBLE with Multiple Peripherals #379

Open
nikolaytsigvintsev opened this issue Jan 15, 2025 · 0 comments
Open

Comments

@nikolaytsigvintsev
Copy link

nikolaytsigvintsev commented Jan 15, 2025

While developing our software which needs to receive input from several Bluetooth devices, we have run into a strange problem.
Any help would be very appreciated.

Problem Description:
An ESP32-S3-DevKitC-1-N32R8V connects to two peripheral devices (A and B). After detecting a device, its peripheral object is stored in a global std::unordered_map. Once both devices are detected, the std::unordered_map contains two elements, each representing an instance of the PeripheralDevice class.

The PeripheralDevice constructor performs the following actions:

  1. Stores the passed peripheral object from BLE.available().
  2. Connects to the device.
  3. Connects to a service and subscribes to a characteristic.
  4. Sets up an event handler of type BLEUpdated using the setEventHandler method.

Upon successful connection, event handlers for each characteristic are set. The BLE devices send data arrays of 60 bytes approximately 60 times per second.

Expected Behavior:

  • The event handler Handler_A in instance A should receive data only from Device A.
  • The event handler Handler_B in instance B should receive data only from Device B.

Actual Problem:

When changes occurs on Device A (e.g., a button press), the Handler_B of instance B also sometimes receives events and data originating from Device A, in an undefined order. This remains consistent with different numbers of devices (three or more) as well.

Additional Observations:

The issue persists even if polling the devices directly instead of subscribing to BLE notifications. When using direct functions like valueUpdated() and canRead() to read data in each instance (A and B), reading data via Instance B still appears to receive data from Device B and vice versa. Each instance uses its own reading functions, which are expected to operate independently.


Code Overview:

Connection Description:

Two identical peripheral devices (A and B) connect to the Arduino board, which retrieves data from them.

After scanning, peripherals are stored in:

std::unordered_map<std::string, PeripheralDevice *> devices;

loop() {
  BLEDevice.poll();
  
  BLEDevice peripheral = BLE.available();
  if (peripheral) {
    String deviceName = peripheral.localName();
    
    if (strstr(deviceName.c_str(), "Controller") != NULL) {
      PeripheralDevice *device = new PeripheralDevice(peripheral);
      devices[peripheral.address().c_str()] = device;
    }
  }
}

PeripheralDevice Constructor:

  1. Store peripheral in the instance:

    PeripheralDevice::PeripheralDevice(BLEDevice &_peripheral) {
        peripheral = _peripheral;
    }
  2. Connect to the device:

    peripheral.connect();
  3. Discover and connect to a service:

    if (peripheral.discoverService(UUID_CUSTOM_SERVICE) && 
        (service = peripheral.service(UUID_CUSTOM_SERVICE))) {
        ...
    }
  4. Connect to a characteristic:

    serviceNotify = service.characteristic(UUID_CUSTOM_SERVICE_NOTIFY);
  5. Subscribe to the characteristic:

    if (!serviceNotify.canSubscribe() && !serviceNotify.subscribe()) {
        return;
    }
  6. Set up an event handler:

    serviceNotify.setEventHandler(BLEUpdated, [](BLEDevice bleDevice, BLECharacteristic characteristic) {
        uint8_t value[60] = {};
        
        if (characteristic.readValue(value, sizeof(value)) == sizeof(value)) {
            if (value[58] != 0x40) {
                Log.notice("%s: %x <-> ", bleDevice.address().c_str(), value[58]);
                for (size_t i = 0; i < 60; ++i) {
                    char byteBuffer[3];
                    sprintf(byteBuffer, "%02X", value[i]);
                    Log.notice("%s ", byteBuffer);
                }
                Log.noticeln("");
            }
        }
    });

Steps to Reproduce:

  1. Connect the Arduino board to two BLE devices with identical configurations (in our case, two Samsung GearVR Controllers).
  2. Use the PeripheralDevice class to manage each connection and subscribe to their characteristics.
  3. Trigger an event (e.g., button press) on Device A.
  4. Observe the event handler of Device B receiving data intended for Device A.

Environment:

  • Hardware: ESP32-S3-DevKitC-1-N32R8V board which embeds a WROOM-2 BLE connectivity module
  • Library: ArduinoBLE
  • Peripheral Devices: Two identical BLE devices (Samsung GearVR Controller)
  • Data Rate: 60 bytes/frame, ~60 frames/second
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant