sandeepmistry / arduino-BLEPeripheral

An Arduino library for creating custom BLE peripherals with Nordic Semiconductor's nRF8001 or nR51822.
MIT License
462 stars 179 forks source link

Can only read from first remote characteristic added #99

Closed grandinquisitor closed 7 years ago

grandinquisitor commented 7 years ago

I took the remote_service example and modified it to try to also use the current time service.

I found that only one worked at a time - and it depended on the order in which you call addRemoteAttribute() on the characteristic. When you add the time characteristic first it responds but not device name, if you switch the order of the addRemoteAttribute() statements then only device name responds with a value. I looked through the code and didn't find any obvious reason why this should be. I tried adding delay()'s in various places and moving lots of other code pieces around to no effect.

In addition, time service worked if you gave it either the property BLERead or BLENotify exclusively, but would not work if I chose (BLERead | BLENotify).

I'm using a Teensy 3.2 and the Arduino IDE and the Adafruit nrf8001 board.

// Import libraries (BLEPeripheral depends on SPI)
#include <SPI.h>
#include <BLEPeripheral.h>

#include <BLEUtil.h>

// define pins (varies per shield/board)
static const uint8_t REQ_PIN = 9;
static const uint8_t RDY_PIN = 2;
static const uint8_t RST_PIN = 10;

// create peripheral instance, see pinouts above
BLEPeripheral                    blePeripheral                            = BLEPeripheral(REQ_PIN, RDY_PIN, RST_PIN);

// create remote services
BLERemoteService                 remoteGenericAttributeService            = BLERemoteService("1800");
BLERemoteService                 timeService                              = BLERemoteService("1805");

// create remote characteristics
BLERemoteCharacteristic          remoteDeviceNameCharacteristic           = BLERemoteCharacteristic("2a00", BLERead);
BLERemoteCharacteristic          remoteTimeCharacteristic                 = BLERemoteCharacteristic("2A2B", BLERead);   // Only works with BLERead or BLENotify, but not both!

void setup() {
  Serial.begin(115200);
#if defined (__AVR_ATmega32U4__) || defined(__MK20DX256__)|| defined(__MK20DX128__)
  while (!Serial);
#endif

  blePeripheral.setLocalName("remote-attributes");

  // set device name and appearance
  blePeripheral.setDeviceName("Remote Attributes");
  blePeripheral.setAppearance(0x0080);

  blePeripheral.addRemoteAttribute(timeService);
  blePeripheral.addRemoteAttribute(remoteGenericAttributeService);

  ///////////////////// IT ONLY DOES THE FIRST ONE OF THESE!!!! Change the order and only the first one will work.
  blePeripheral.addRemoteAttribute(remoteDeviceNameCharacteristic);
  blePeripheral.addRemoteAttribute(remoteTimeCharacteristic);
  /////////////////////

  // assign event handlers for connected, disconnected to peripheral
  blePeripheral.setEventHandler(BLEConnected, blePeripheralConnectHandler);
  blePeripheral.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
  blePeripheral.setEventHandler(BLERemoteServicesDiscovered, blePeripheralRemoteServicesDiscoveredHandler);

  // assign event handlers for characteristic
  remoteDeviceNameCharacteristic.setEventHandler(BLEValueUpdated, bleRemoteDeviceNameCharacteristicValueUpdatedHandle);
  remoteTimeCharacteristic.setEventHandler(BLEValueUpdated, genericUpdateHandler);

  // begin initialization

  blePeripheral.begin();

  Serial.println(F("BLE Peripheral - remote attributes"));
}

void loop() {
  blePeripheral.poll();
}

void blePeripheralConnectHandler(BLECentral& central) {
  // central connected event handler
  Serial.print(F("Connected event, central: "));
  Serial.println(central.address());
}

void blePeripheralDisconnectHandler(BLECentral& central) {
  // central disconnected event handler
  Serial.print(F("Disconnected event, central: "));
  Serial.println(central.address());
}

void blePeripheralRemoteServicesDiscoveredHandler(BLECentral& central) {
  // central remote services discovered event handler
  Serial.print(F("Remote services discovered event, central: "));
  Serial.println(central.address());

  if (remoteTimeCharacteristic.canRead()) {
    Serial.println("Can read time");
    remoteTimeCharacteristic.read();
  }

  if (remoteTimeCharacteristic.canSubscribe()) {
    Serial.println("Can subscribe to time");
    remoteTimeCharacteristic.subscribe();
  }

  if (remoteDeviceNameCharacteristic.canRead()) {
    Serial.println("Can read device name");
    remoteDeviceNameCharacteristic.read();
  }
}

void bleRemoteDeviceNameCharacteristicValueUpdatedHandle(BLECentral& central, BLERemoteCharacteristic& characteristic) {
  char remoteDeviceName[BLE_REMOTE_ATTRIBUTE_MAX_VALUE_LENGTH + 1];
  memset(remoteDeviceName, 0, sizeof(remoteDeviceName));
  memcpy(remoteDeviceName, remoteDeviceNameCharacteristic.value(), remoteDeviceNameCharacteristic.valueLength());

  Serial.print(F("Remote device name: "));
  Serial.println(remoteDeviceName);
}

void genericUpdateHandler(BLECentral& central, BLERemoteCharacteristic& characteristic) {
  Serial.print(F("Characteristic value updated: "));

  // Example for time service
  // 4E 53 50 20 69 50 68 6F 6E 65 20 36

  BLEUtil::printBuffer(characteristic.value(), characteristic.valueLength());
}
grandinquisitor commented 7 years ago

Ah, I wasn't parsing the return value for the time service. It's actually getting the device name instead.

sandeepmistry commented 7 years ago

Hi @grandinquisitor,

From what I remember, the order you add the remote attributes in very important. Please try the following:

blePeripheral.addRemoteAttribute(remoteGenericAttributeService);
blePeripheral.addRemoteAttribute(remoteDeviceNameCharacteristic);

blePeripheral.addRemoteAttribute(timeService);
blePeripheral.addRemoteAttribute(remoteTimeCharacteristic);

The remote characteristic is associated with the last remote service added.

sandeepmistry commented 7 years ago

Closing for now due to lack of activity.