CurieBLE: Notify does not work

Hi all,

why do the notifications not work? If I tap on the PCB the callback get’s called and the value is changed but I don’t receive any notification. What is the problem here?

Any help is appreciated.

Best regards
Stevil

#include "CurieIMU.h"
#include "CurieTimerOne.h"
#include <CurieBLE.h>

BLEPeripheral blePeripheral;  // BLE Peripheral Device (the board you're programming)
BLEService temperatureService("19B10000-E8F2-537E-4F6C-D104768A1218"); // BLE Temperature Service

// BLE Temperature Characteristic - custom 128-bit UUID, readable by central
BLEFloatCharacteristic temperatureCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1218", BLERead );
BLEShortCharacteristic fanCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1219", BLERead | BLENotify );

const bool ON = TRUE;
const bool OFF = FALSE;
const int LED_PIN = 13;
const int Timer_5s = 5000000;   // 5 seconds in mirco second unit.

void timedDisplayIsr()   // callback function when interrupt is asserted
{
  Serial.println("Display off");
  CurieTimerOne.kill();
  fanCharacteristic.setValue(0);
}

void setup(void) {
  
  Serial.begin(9600);
  
  // Initialise the IMU
  CurieIMU.begin();
  CurieIMU.attachInterrupt(eventCallback);

  /* Enable Shock Detection */
  CurieIMU.setDetectionThreshold(CURIE_IMU_SHOCK, 1500); // 1.5g = 1500 mg
  CurieIMU.setDetectionDuration(CURIE_IMU_SHOCK, 50);   // 50ms
  CurieIMU.interrupts(CURIE_IMU_SHOCK);

  // set advertised local name and service UUID:
  blePeripheral.setDeviceName("BabyClownies");
  blePeripheral.setLocalName("Aqua");
  blePeripheral.setAdvertisedServiceUuid(temperatureService.uuid());

  // add service and characteristic:
  blePeripheral.addAttribute(temperatureService);
  blePeripheral.addAttribute(temperatureCharacteristic);
  blePeripheral.addAttribute(fanCharacteristic);

  // set the initial value for the characeristic:
  temperatureCharacteristic.setValue(26.37);
  fanCharacteristic.setValue(0);

  // begin advertising BLE service:
  blePeripheral.begin();

  Serial.println("BLE BabyClownies Temperature Peripheral");

  pinMode(LED_PIN, OUTPUT);
}

void loop(void) {

  
  // listen for BLE peripherals to connect:
  BLECentral central = blePeripheral.central();

  // if a central is connected to peripheral:
//  if (central) {
//    Serial.print("Connected to central: ");
//    // print the central's MAC address:
//    Serial.println(central.address());
//    digitalWrite(LED_PIN, HIGH);
//  }
  
  
}

static void eventCallback()
{
  if (CurieIMU.getInterruptStatus(CURIE_IMU_SHOCK))
  {
    if( (CurieIMU.shockDetected(Z_AXIS, NEGATIVE)) || (CurieIMU.shockDetected(Z_AXIS, POSITIVE)) )
      {
        Serial.println("Shock detected on Z-axis");
        Serial.println("Display on");
      }
  }
}

Can't help with your code, but free pfodDesignerV2 android app will allow you to interactively create an Android menu that can show the temp and fan values and update them. No Android programming required.

The pfodDesignerV2 will generate the code needed for Arduino101 (and other BLE devices) to connect to (paid) pfodApp app.

If you look at the generated code you may get some clues for your own code.

See Using the Arduino 101 board for an example tutorial

Here is my thought:

In the loop portion of your code you are continually making a connection to the central. All of the code I have written occurs in the context of a BLE connection having been established, which is the point of the code you commented out in the loop.

Why don't you make your BLE connection in the setup function and just do nothing in the loop() function?

Perhaps the loop() function constantly trying to make a connection to central is screwing up the setValue() transmission in your isr?

govkn: Here is my thought:

In the loop portion of your code you are continually making a connection to the central. All of the code I have written occurs in the context of a BLE connection having been established, which is the point of the code you commented out in the loop.

Why don't you make your BLE connection in the setup function and just do nothing in the loop() function?

Perhaps the loop() function constantly trying to make a connection to central is screwing up the setValue() transmission in your isr?

Hi,

but as far as I know thats just how it is done in the examples. Take a look at the heart rate measurement sketch for example. They also have that BLE central connection piece in their loop....

Yes, I used the Heart Rate Example as the basis of all my BLE sketches so far. I am very familiar with it.

When I look at you code, your loop() function continually is attempting to make a connection, over and over again.

The Heart sketch does not do this:

// check the heart rate measurement every 200ms // as long as the central is still connected: while (central.connected()) { long currentMillis = millis(); // if 200ms have passed, check the heart rate measurement: if (currentMillis - previousMillis >= 200) { previousMillis = currentMillis; updateHeartRate(); } }

As long as there is a BLE connection, this while loop will continue to execute, they are not continually re-connecting as your code does.

Hope this makes my thoughts more clear. I don't know where the error is in your code, just wanted to point out this and you can try it if you like.

Hi,

I understood what you said and changed the code. Still no difference.
There should be two moments of notification: One when a shock is detected and the second one when it is reset after 5s (when the timer callback executes).

One thing: I used a BLEShortCharacteristic and in the HRM example they used a BLECharacteristic. Could this be the important point? I’m lost on that as I cannot find proper documentation on all that stuff…

Please see the following code:

#include "CurieIMU.h"
#include "CurieTimerOne.h"
#include <CurieBLE.h>

BLEPeripheral blePeripheral;  // BLE Peripheral Device (the board you're programming)
BLEService temperatureService("19B10000-E8F2-537E-4F6C-D104768A1218"); // BLE Temperature Service

// BLE Temperature Characteristic - custom 128-bit UUID, readable by central
BLEFloatCharacteristic temperatureCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1218", BLERead );
BLEShortCharacteristic fanCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1219", BLERead | BLENotify );

const bool ON = TRUE;
const bool OFF = FALSE;
const int LED_PIN = 13;
const int Timer_5s = 5000000;   // 5 seconds in mirco second unit.

void timedDisplayIsr()   // callback function when interrupt is asserted
{
  Serial.println("Display off");
  CurieTimerOne.kill();
  fanCharacteristic.setValue(0);
}

void setup(void) {
  
  Serial.begin(9600);
  
  // Initialise the IMU
  CurieIMU.begin();
  CurieIMU.attachInterrupt(eventCallback);

  /* Enable Shock Detection */
  CurieIMU.setDetectionThreshold(CURIE_IMU_SHOCK, 1500); // 1.5g = 1500 mg
  CurieIMU.setDetectionDuration(CURIE_IMU_SHOCK, 50);   // 50ms
  CurieIMU.interrupts(CURIE_IMU_SHOCK);

  // set advertised local name and service UUID:
  blePeripheral.setDeviceName("BabyClownies");
  blePeripheral.setLocalName("Aqua");
  blePeripheral.setAdvertisedServiceUuid(temperatureService.uuid());

  // add service and characteristic:
  blePeripheral.addAttribute(temperatureService);
  blePeripheral.addAttribute(temperatureCharacteristic);
  blePeripheral.addAttribute(fanCharacteristic);

  // set the initial value for the characeristic:
  temperatureCharacteristic.setValue(26.37);
  fanCharacteristic.setValue(0);

  // begin advertising BLE service:
  blePeripheral.begin();

  Serial.println("BLE BabyClownies Temperature Peripheral");

  pinMode(LED_PIN, OUTPUT);
}

void loop(void) {

  
  // listen for BLE peripherals to connect:
  BLECentral central = blePeripheral.central();


  while (central.connected()) {
    }
  
}

static void eventCallback()
{
  if (CurieIMU.getInterruptStatus(CURIE_IMU_SHOCK))
  {
    if( (CurieIMU.shockDetected(Z_AXIS, NEGATIVE)) || (CurieIMU.shockDetected(Z_AXIS, POSITIVE)) )
      {
        Serial.println("Shock detected on Z-axis");
        Serial.println("Display on");
        CurieTimerOne.start(Timer_5s, &timedDisplayIsr);  // set timer and callback
          Serial.println("Timer started");
          fanCharacteristic.setValue(1);
      }
  }
}

What software are you using to receive BLE transmissions? I often use nRF master control for debugging purposes, however, it sometimes is a bit "sticky" requiring me to disconnect and reconnect whenever I make changes otherwise it will not work.

I have not used the type specific characteristic constructors. All BLE data is sent as bytes, see https://www.hackster.io/gov/imu-to-you-ae53e1?ref=part&ref_id=15659&offset=0 for a tutorial on this, also the tutorial at https://www.arduino.cc/en/Reference/CurieBLE implies a solution whereby strings are sent as bytes, you could try this.

I am contemplating using an interrupt mechanism to update a characteristic in a current project this weekend, so I will let you know how it goes!

Thanks for your fast answer. I use the nRF Master Control Panel and also LightBlue. Both on my iPhone. Both work well with the HRM example and I am not new to BLE - I'm only new to the Arduino 101.

Yesterday I figured out that obviously it is not possible to set a new value and receive notifications inside a ISR (if you can call them like that).

I tried to update the value inside the IMU-callback function - no notification, even though the value was updated.

Inside the timerOne callback the behaviour was identical: Value gets writtn but no notification received.

When you're working with flags and set the new value inside the loop-function everything works as expected and I received notifications.

Is that written anywhere in the documentation?

Hope it'll help some of you guys out there.

Regards Stephan

I'm reading the source code on github corelibs-arduino101/libraries/CurieBLE/src/BLECharacteristic.cpp for the method setValue().

(Maybe that is newer than what is on your Arduino101 i.e. maybe you need to "update the firmware", which is misleading terminology because that seems to mean: update the Arduino libraries.)

In that version of the libraries source code, setValue() takes two parameters and returns a bool indicating success. Your code only passes one parameter and doesn't check the return value. Which leads me to believe that your "firmware" and the examples are outdated. Otherwise, I don't think your code would compile. Maybe the latest version has fixed your problem?

Another thought: the Curie software architecture is complex, with the Arduino libraries in a layer on top of some unspecified RTOS (real time operating system) running on the ARC ISA cpu and communicating to a x86 ISA cpu which is doing the low-level Bluetooth. The "ISR callback" is probably not called while the interrupt is being handled but is triggered soon thereafter by an event (or semaphore?). But maybe it IS called while the interrupt is being handled and should be kept short and shouldn't call certain system calls? Its an interesting question and a lot to think about.