ArduinoBLE Peripheral working, Central Question re: data type

Hi
I am using this code on a BLE Sense.


#include <ArduinoBLE.h>           // Bluetooth Library
#include <Arduino_HTS221.h>       // Pressure Sensor Library
#include <Arduino_LPS22HB.h>      // Temperature Sensor Library
#include <Arduino_LSM9DS1.h>      // Magnetometer Sensor Library

// Initalizing global variables for sensor data to pass onto BLE
String p, t, m;

// BLE Service Name
BLEService customService("180C");

// BLE Characteristics
// Syntax: BLE<DATATYPE>Characteristic <NAME>(<UUID>, <PROPERTIES>, <DATA LENGTH>)
BLEStringCharacteristic ble_pressure("2A56", BLERead | BLENotify, 13);
BLEStringCharacteristic ble_temperature("2A57", BLERead | BLENotify, 13);
BLEStringCharacteristic ble_magnetic("2A58", BLERead | BLENotify, 20);

// Function prototype
void readValues();

void setup()
{
    // Initalizing all the sensors
    HTS.begin();
    BARO.begin();
    IMU.begin();
    Serial.begin(9600);
    while (!Serial);
    if (!BLE.begin())
    {
        Serial.println("BLE failed to Initiate");
        delay(500);
        while (1);
    }

    // Setting BLE Name
    BLE.setLocalName("Arduino Environment Sensor");
    
    // Setting BLE Service Advertisment
    BLE.setAdvertisedService(customService);
    
    // Adding characteristics to BLE Service Advertisment
    customService.addCharacteristic(ble_pressure);
    customService.addCharacteristic(ble_temperature);
    customService.addCharacteristic(ble_magnetic);

    // Adding the service to the BLE stack
    BLE.addService(customService);

    // Start advertising
    BLE.advertise();
    Serial.println("Bluetooth device is now active, waiting for connections...");
}

void loop()
{
    // Variable to check if cetral device is connected
    BLEDevice central = BLE.central();
    if (central)
    {
        Serial.print("Connected to central: ");
        Serial.println(central.address());
        while (central.connected())
        {
            delay(200);
            
            // Read values from sensors
            readValues();

            // Writing sensor values to the characteristic
            ble_pressure.writeValue(p);
            ble_temperature.writeValue(t);
            ble_magnetic.writeValue(m);

            // Displaying the sensor values on the Serial Monitor
            Serial.println("Reading Sensors");
            Serial.println(p);
            Serial.println(t);
            Serial.println(m);
            Serial.println("\n");
            delay(1000);
        }
    }
    Serial.print("Disconnected from central: ");
    Serial.println(central.address());
}

void readValues()
{
    // Reading raw sensor values from three sensors
    float x, y, z;
    float pressure = BARO.readPressure();
    float temperature = HTS.readTemperature();
    if (IMU.magneticFieldAvailable()) {
      IMU.readMagneticField(x, y, z);

    // Saving sensor values into a user presentable way with units
    p = String(pressure) + " kPa";
    t = String(temperature) + " C";
    m = "X:" + String(x) + ", Y:" + String(y);
    }
}

It works well on the mobile phone app LightBlue I can connect to the peripheral, open the characteristic, switch the data to be shown in UTF-8. Making the switch leads to it being readable exactly as on the serial output.

To take this to the next level I want to move from phone to a second BLE board. In the forum I found a topic by @Klaus_K where he wrote the following code


/*
  This example creates a BLE central that scans for a peripheral with a Environmental Sensing Service (ESS).
  If that contains temperature and humidity characteristics the values are displayed.

  The circuit:
  - Arduino Nano 33 BLE or Arduino Nano 33 IoT board.

  This example code is in the public domain.
*/

#include <ArduinoBLE.h>

//----------------------------------------------------------------------------------------------------------------------
// BLE UUIDs
//----------------------------------------------------------------------------------------------------------------------

// https://www.bluetooth.com/specifications/assigned-numbers/environmental-sensing-service-characteristics/

#define BLE_UUID_ENVIRONMENTAL_SENSING_SERVICE    "181A"
#define BLE_UUID_TEMPERATURE                      "2A6E"
#define BLE_UUID_HUMIDITY                         "2A6F"

void setup()
{
  Serial.begin( 9600 );
  while ( !Serial );

  BLE.begin();

  BLE.scanForUuid( BLE_UUID_ENVIRONMENTAL_SENSING_SERVICE );
} // setup


void loop()
{
  static long previousMillis = 0;

  long interval = 5000;
  unsigned long currentMillis = millis();
  if ( currentMillis - previousMillis > interval )
  {
    previousMillis = currentMillis;

    BLEDevice peripheral = BLE.available();

    if ( peripheral )
    {
      if ( peripheral.localName() != "Arduino Nano 33 BLE" )
      {
        return;
      }

      BLE.stopScan();

      explorePeripheral( peripheral );

      BLE.scanForUuid( BLE_UUID_ENVIRONMENTAL_SENSING_SERVICE );
    }
  }
} // loop


bool explorePeripheral( BLEDevice peripheral )
{
  if ( !peripheral.connect() )
  {
    return false;
  }

  if ( !peripheral.discoverAttributes() )
  {
    peripheral.disconnect();
    return false;
  }

  BLECharacteristic temperatureCharacterisic = peripheral.characteristic( BLE_UUID_TEMPERATURE );
  if ( temperatureCharacterisic )
  {
    int16_t temperature;
    temperatureCharacterisic.readValue( temperature );
    Serial.print( "Temperature: " );
    Serial.print( temperature / 100.0 );
    Serial.println( "°C" );
  }

  BLECharacteristic humidityCharacterisic = peripheral.characteristic( BLE_UUID_HUMIDITY );
  if ( humidityCharacterisic )
  {
    uint16_t humidity;
    humidityCharacterisic.readValue( humidity );
    Serial.print( "Humidity: " );
    Serial.print( humidity / 100.0 );
    Serial.println( "%" );
  }

  peripheral.disconnect();
  return true;
} // explorePeripheral

I can adapt Klause’s code easily but I don’t understand how to work out which type of variable to use. In his central code he uses int16_t and uint16_t. In my initial code that is working on Lightblue the peripheral specifies the data as String.

Please can you help me with:

  1. What to use to satisfy the first script as it is to allow that to remain the same, how to read UTF-8 like in Lightblue. This is useful to understand because I want to know how to deal with the situation where a system is already set up and can not be changed. In this situation LightBlue proves it is readable and only requires a change to UTF-8.
  2. How it should be done and whether 1 is correct or incorrect in any way or just a preference. Update I have found that @Klaus_K mentions in another post that Temperature is specified by BLE “as UUID 2A6E Type sint16”

So maybe I now have part of my answer to question 2 but any other follow ups appreciated, especially to question 1, if it is specified as String in the first code and so easily adjusted to UTF-8 in LightBlue is it easy also to just apply the same on a central?

Thanks for your help

I found some information here

BLE format types

Based on this then perhaps the answer to the first question is to look up UTF-8 in the table and it says utf8s. If I specify that in the central code then perhaps it will output correctly as it does in LightBlue?

Yet that then raises the question of how if the first peripheral code is using String and in @Klaus_K central code it says BLE specify a different format why could that be?

BLEStringCharacteristic ble_temperature("2A57", BLERead | BLENotify, 13);
ble_temperature.writeValue(t);

The source code for the BLEStringCharacterisitic shows that .writeValue() is just sending the null terminated array of characters (aka c string).

int BLEStringCharacteristic::writeValue(const String& value)
{
  return BLECharacteristic::writeValue(value.c_str());
}

I think you should be able to read it like this

BLECharacteristic temperatureCharacterisic = peripheral.characteristic("2A57");
  if ( temperatureCharacterisic )
  {
    char temperature[13];
    temperatureCharacterisic.readValue( &temperature,13 );
    Serial.print( "Temperature: " );
    Serial.print( temperature);
  }
1 Like

Awesome
Thanks @cattledog
Useful for others hopefully too.

One thing I wondered about is timing.

Considering the intersection between the two codes, let’s say we have a system that broadcasts temperature when it hits a set point, when it reaches writeValue which variables affect the frequency at which it is printed into serial on the central device?

Long interval = 5000; (central)
and
delay(200); (peripheral)

Looks like these are the only time variables available. How when connected would you take maximum control over the frequency of transmission and printing? Or let’s say an array was used on the central how would you control the frequency new values were added ?

:face_with_spiral_eyes:

The frequency at which the peripheral reads the sensors and updates the value should be controlling. The central can not read new data faster than the peripheral makes it available to read.

There are probably callback, notification, and subscription routines available to tighten things up, but it is adding complexity and I'm not very experienced with code for centrals.

1 Like

The central devices decides how much data it wants. The central is the client e.g., you looking for news on a newspaper page. So, new values are added by the central by reading the characteristic on the peripheral.
The central can decide to subscribe to a peripheral characteristic when notifications are available. That will allow the central to read the characteristic when it was updated by the peripheral.

If you want to make it sophisticates you can add a characteristic in the peripheral that would tell the peripheral how often you want it to update the sensor data. Then the central can modify the value and with notifications the update would be automatically scheduled.

I got it already but thanks for coming back.
Your code is really lean led me to a similar strategy.
Thank you so much for your contribution in terms of creating exemplary code because the examples aren't very good.
I would love to rack your brains about mitigating heavy switching between Wi-Fi and BLE on the IoT Nano board, but maybe in another post. It has taken me weeks to work out and still doesn't work properly :joy: because of buffer / memory problems.
The board designers made a terrible decision not to make its capacity much larger.
Even not being able to run Wi-Fi and BLE simultaneously isn't a problem but it's under specified.
Bring on the Next BLE series. In design now (probably) and no larger and five times capacity releasing in late 2022/ early 2023.
Future versions of Arduino should come with a watchdog built in that leads to a report when the IDE serial leads to a hang.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.