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

Peripheral doesn't notify central #180

Open weeteniz opened 6 years ago

weeteniz commented 6 years ago

Hi

I'm trying to develop my own window covering BLE peripheral/accessory for HomeKit using Homebridge.

So far I have:

Set up Homebridge on Raspberry Pi 2 with a bluetooth dongle. Homebridge loads fine and finds the BLEPeripheral (in this case RedBear Blend Micro with Nordic nRF8001) Here's the config.json

{
      "bridge": {
        "name": "Raspberry Pi 2”,
        "username": “00:13:EF:80:00:12”,
        "port": 51826,
        "pin": "031-45-154"
      },
      "description": "Raspberry Pi 2 Homebridge-Bluetooth",

      "platforms": [
        {
          "platform": "Bluetooth",
          "accessories": [
            {
              "name": “Smart Shades“,
              "name_note": "Name of the accessory as shown in the Home app on iOS.",

              "address": “D0:5F:2E:E2:11:22”,
              "address_note": "Bluetooth address of the accessory. Non-matching devices are ignored.",

              "services": [
                {
                  "name": “Smart Blinds”,
                  "name_note": "Name of the service as shown in the Home app on iOS.",

                  "type": “WindowCovering”,
                  "type_note1": "Type of the service - i.e. Lightbulb, Switch, Lock, HumiditySensor, ...",
                  "type_note2": "Must match this list - https://github.com/KhaosT/HAP-NodeJS/blob/master/lib/gen/HomeKitTypes.js",
                  "type_note3": "Service.Lightbulb has a mandatory On characteristic and Brightness, Hue and Saturation are optional.",

                  "UUID": "A7B10010-EEEE-5377-FF6C-D104768A1214",
                  "UUID_note": "Bluetooth UUID of the service. Capitalization and dashes doesn't matter.",

                  "characteristics": [
                    {
                      "type": “CurrentPosition”,
                      "type_note1": "Type of the characteristic - i.e. On, Brightness, CurrentHumidity, CurrentTemperature, ...",
                      "type_note2": "Must match this list - https://github.com/KhaosT/HAP-NodeJS/blob/master/lib/gen/HomeKitTypes.js",
                      "type_note3": "Characteristic.On is a BOOL value and expects READ, WRITE and NOTIFY permissions.",

                      "UUID": "A7B10011-EEEE-5377-FF6C-D104768A1214",
                      "UUID_note": "Bluetooth UUID of the characteristic. Capitalization and dashes doesn't matter."
                    },

                    {
                      "type": ”TargetPosition”,
                      "type_note1": "Type of the characteristic - i.e. On, Brightness, CurrentHumidity, CurrentTemperature, ...",
                      "type_note2": "Must match this list - https://github.com/KhaosT/HAP-NodeJS/blob/master/lib/gen/HomeKitTypes.js",
                      "type_note3": "Characteristic.Brightness is an INT value and expects READ, WRITE and NOTIFY permissions.",

                      "UUID": "A7B10012-EEEE-5377-FF6C-D104768A1214",
                      "UUID_note": "Bluetooth UUID of the characteristic. Capitalization and dashes doesn't matter."
                    }
                  ],
                  "characteristics_note1": "List of Bluetooth characteristics that will be exposed to HomeKit.",
                  "characteristics_note2": "Characteristics with non-matching UUIDs are ignored."
                }

              ],
              "services_note1": "List of Bluetooth services that will be exposed to HomeKit.",
              "services_note2": "Services with non-matching UUIDs are ignored."
            }
          ]
        }
      ]
    }

This is what happens when I launch Homebridge and start interacting with my iPhone.

screen shot 2017-09-05 at 22 55 43

I developed the code with two characteristics TargetPosition and CurrentPosition.

#include <SPI.h>
#include <BLEPeripheral.h>

const int pinLED = 13;

BLEPeripheral ble = BLEPeripheral(6,7,4);
BLEService informationService("180A");
BLECharacteristic modelCharacteristic("2A24", BLERead, "101");
BLECharacteristic manufacturerCharacteristic("2A29", BLERead, "Arduino");
BLECharacteristic serialNumberCharacteristic("2A25", BLERead, "2.71828");

BLEService windowCoveringService("A7B10010-EEEE-5377-FF6C-D104768A1214");
BLEUnsignedCharCharacteristic currentPositionCharacteristic("A7B10011-EEEE-5377-FF6C-D104768A1214", BLERead | BLENotify);
BLEUnsignedCharCharacteristic targetPositionCharacteristic("A7B10012-EEEE-5377-FF6C-D104768A1214", BLEWrite | BLERead | BLENotify);
//BLEUnsignedIntCharacteristic positionStateCharacteristic("A7B10013-EEEE-5377-FF6C-D104768A1214", BLERead | BLENotify);

void setup() {

  Serial.begin(115200);
  pinMode(pinLED, OUTPUT);
  digitalWrite(pinLED, true);

  ble.setLocalName("Smart Blinds");
  ble.setAdvertisedServiceUuid(windowCoveringService.uuid());
  ble.addAttribute(informationService);
  ble.addAttribute(modelCharacteristic);
  ble.addAttribute(manufacturerCharacteristic);
  ble.addAttribute(serialNumberCharacteristic);

  ble.addAttribute(windowCoveringService);
  ble.addAttribute(currentPositionCharacteristic);
  ble.addAttribute(targetPositionCharacteristic);
  //ble.addAttribute(positionStateCharacteristic);

  currentPositionCharacteristic.setValue(100);
  //positionStateCharacteristic.setValueLE(positionState);

  ble.setEventHandler(BLEConnected, centralConnect);
  ble.setEventHandler(BLEDisconnected, centralDisconnect);
  targetPositionCharacteristic.setEventHandler(BLEWritten, characteristicWrite);
  currentPositionCharacteristic.setEventHandler(BLESubscribed, characteristicSubscribe);

  delay(5000);

  ble.begin();
  Serial.println("Bluetooth on");
}

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

void centralConnect(BLECentral& central) {
  Serial.print("Central connected | ");
  Serial.println(central.address());
}

void centralDisconnect(BLECentral& central) {
  Serial.print("Central disconnected | ");
  Serial.println(central.address());
}

void characteristicWrite(BLECentral& central, BLECharacteristic& characteristic) {
  Serial.print("Characteristic written | ");
  Serial.println(characteristic.uuid());
  unsigned char targetPosition = (unsigned char) targetPositionCharacteristic.value();
  Serial.print("Target position set: ");
  Serial.println(targetPosition);
  setBlinds(targetPosition);

}

void characteristicSubscribe(BLECentral& central, BLECharacteristic& characteristic) {

    Serial.println("Characteristic event, subscribed");

    //Central subscribes just fine

}

void setBlinds(unsigned char targetPosition) {

  //This is where the code to drive the motor will go

  currentPositionCharacteristic.setValue(0);

  //When the current position value is set, no notification is sent to the central

  Serial.print("Current position: ");
  Serial.println(currentPositionCharacteristic.value());
  digitalWrite(pinLED, false);

}

When I attempt to open/close the blinds using iOS, the value is written from the central (Homebridge) to the peripheral (RedBear Blend Micro) TargetPosition characteristic as per the first screenshot. Interestingly, TargetPosition characteristic also sends the notification back to the central.

My issue is that whatever I tried, the CurrentPosition characteristic just refuses to send the notification back to the central when its value changes to let the central know that the operation has finished. The central is subscribed to the characteristic because serial prints out some text when the event handler is triggered at the point when the central subscribes.

As the central doesn't get an updated value for the CurrentPosition characteristic, presumably HomeKit has no idea where the blinds are so it simply displays a spinner next to the icon which just doesn't stop spinning (infinite loop).

smart blinds

Where am I going wrong?

Any help at all would be appreciated!

weeteniz commented 6 years ago

OK, I've done a bit more digging and experimenting and I think the issue might be either my controller (Blend Micro) or the BLEPeripheral library.

I also posted my original post on Homebridge-Bluetooth repo here.

I got a response from the developer of the repo saying my code looked fine but got a suggestion to simply put the characteristic.setValue methods inside the loop() method as the developer has done with several of his code examples that work.

I've done that but again, had no luck. The Homebridge-Bluetooth developer said it was very strange that my rewritten code doesn't work.

Since then I decided to rewrite my code to capture the results of characteristic.setValue() and characteristic.canNotify() methods. The relevant bit of the code now looks like this:

void loop() {
  ble.poll();

  if (targetUpdate != false) {

    if (positionStateCharacteristic.canNotify() != 0) { //code enters the block which means canNotify returns 'true'
        unsigned char state = 2;
        bool stateSet = positionStateCharacteristic.setValue(state); //capture success/failure of setValue() notification
        Serial.print("Current state set? ");
        Serial.println(stateSet); //serial returns 0 which means setValue notification fails
        Serial.println(" to: ");
        Serial.println(positionStateCharacteristic.value());
    };

    unsigned char newTarget = targetPositionCharacteristic.value();
    bool notifiable = currentPositionCharacteristic.canNotify(); //returns 'true'
    Serial.print("Can notify? ");
    Serial.println(notifiable);

    bool changed = currentPositionCharacteristic.setValue(newTarget);
    Serial.print("Current position changed? ");
    Serial.println(changed); //serial returns 0 which means setValue notification fails
    Serial.print("Current position set to: ");
    Serial.println(currentPositionCharacteristic.value());

    targetUpdate = false;

  }

}

This has made absolutely no difference to how my application functions, however I got some interesting results from the two newly implemented methods.

The result of characteristic.canNotify() comes back as 1 (true). Crucially, the result of characteristic.setValue() comes back as 0 (failure) meaning that the characteristic could not notify the central.

I also found the issue Can not notify data to central #4 in this repo where someone else using Blend Micro mentioned that central could not be notified.

That issue resulted in 226c9d3 commit which fixed it. Could I be facing something similar here?

weeteniz commented 6 years ago

The developer of the Homebridge-Bluetooth repo (mentioned above) has tried my code from the second post on Arduino 101 using CurieBLE library and it worked flawlessly.

It does appear that there’s an issue in how Red Bear Blend Micro communicates with BLEPeripheral library to send notifications.

More details about the results with CurieBLE are posted here https://github.com/vojtamolda/homebridge-bluetooth/issues/13