Nano 33 Sense: temp, humid, pressure via bluetooth gives wildly incorrect values

I am creating a sketch to read values from the Nano 33 Sense for temperature, humidity, and atmospheric pressure. I have Bluetooth set up as well. Upon Bluetooth connection, I read the values and update the Bluetooth characteristics for pressure(2A6D), temperature(2A6E), and humidity(2A6F). I send the same values to the serial output for debugging purposes.

I have no trouble reading the values from the sensors and the output to serial is accurate. The trouble I’m having lies with the Bluetooth values. They swing wildly with outrageous values that almost look like an uninitialized value. For example, 232 degrees C, with 161% humidity.

My best guess is that I’m doing something wrong when updating the characteristic values. I’ve had success with the Bluetooth Hello World sketch. Also, if I hard code writeValue(0) for everything in my sketch, it will show zeros for all characteristics. But I cannot figure out why using values from the sensors is so inaccurate.

I am using Nordic’s nRF on Android to connect and read the values. My code is below. Any assistance would be appreciated.

#include <ArduinoBLE.h>
#include <Arduino_HTS221.h>
#include <Arduino_LPS22HB.h>
 
// Declare the variables for reading barometric pressure, temperature,
// and humidity, respectively.
float p;
float t;
float h;

// Declare Bluetooth service names, and characteristics. Pressure,
// temperature, and humidity are all standard GATT services.
// https://specificationrefs.bluetooth.com/assigned-values/16-bit%20UUID%20Numbers%20Document.pdf
BLEService pressureService("2A6D");
BLEFloatCharacteristic pressureCharacteristic("2A6D", BLERead);
BLEService temperatureService("2A6E");
BLEFloatCharacteristic temperatureCharacteristic("2A6E", BLERead);
BLEService humidityService("2A6F");
BLEFloatCharacteristic humidityCharacteristic("2A6F", BLERead);

// Error pulse of death. Loops forever. Only used if there are problems.
void error_pulse() {
  while (1) {
    for (int i=0; i<3; i++) {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(166);
      digitalWrite(LED_BUILTIN, LOW);
      delay(167);
    }
    delay(1001);
  }  
}

void setup() {
  // Illuminate LED for 5sec to indicate startup. 
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(5000);

  // Initialize serial for debug output.
  Serial.begin(9600);

  // Initialize atmospheric sensors.
  Serial.println("Initializing humidity and temperature sensor.");
  if (!HTS.begin()) {
    Serial.println("Failed.");
    error_pulse();
  }
  Serial.println("Initializing barometric pressure sensor.");
  if (!BARO.begin()) {
    Serial.println("Failed.");
    error_pulse();
  }

  // Initialize Bluetooth communication.
  Serial.println("Initializing Bluetooth communication.");
  if (!BLE.begin()) {
    Serial.println("Failed.");
    error_pulse();
  }

  // Add Bluetooth services for barometric pressure, temperature, and humidity.
  // Give them floating point characteristics, and advertise them.
  Serial.println("Setting up Bluetooth services for pressure, temperature, and humidity.");
  BLE.setLocalName("Weather");
  BLE.setAdvertisedService(pressureService);
  pressureService.addCharacteristic(pressureCharacteristic);
  pressureCharacteristic.writeValue(0.0);
  BLE.addService(pressureService);
  BLE.setAdvertisedService(temperatureService);
  temperatureService.addCharacteristic(temperatureCharacteristic);
  temperatureCharacteristic.writeValue(0.0);
  BLE.addService(temperatureService);
  BLE.setAdvertisedService(humidityService);
  humidityService.addCharacteristic(humidityCharacteristic);
  humidityCharacteristic.writeValue(0.0);
  BLE.addService(humidityService);
  Serial.println("Advertising services.");
  BLE.advertise();
  digitalWrite(LED_BUILTIN, LOW);
}

void loop() {

  // Wait for a connection from a central.
  BLEDevice central = BLE.central();
  if (central) {
   Serial.print("Incoming connection from: ");
    Serial.println(central.address());
    digitalWrite(LED_BUILTIN, HIGH);

    while (central.connected()) {
      // Get readings from sensors and update the charcteristic values.
      p = BARO.readPressure(KILOPASCAL) * 1000;
      t = HTS.readTemperature(CELSIUS);
      h = HTS.readHumidity();

      Serial.print("Pressure: ");
      Serial.println(p);
      Serial.print("Temperature: ");
      Serial.println(t);
      Serial.print("Humidity: ");
      Serial.println(h);

      pressureCharacteristic.writeValue(p);
      temperatureCharacteristic.writeValue(t);
      humidityCharacteristic.writeValue(h);

      delay(1000);
    }

    Serial.println("Connection terminated.");
    digitalWrite(LED_BUILTIN, LOW);
  }
}

After finding a helpful post from @rsanalo, I was able to determine it's a problem of variable typing. I assumed (incorrectly) that by using BLEFloatCharacteristic, I was telling Bluetooth to use float values for my pressure, temperature, and humidity characteristics. Bad assumption on my part.

Looking at the code in rsanalo's post I was able to determine the proper typing for the characteristics. I now have an accurate reading for temperature and I'm working on the conversions for humidity and pressure.

I will post the corrected code as a follow-up in case anyone else stumbles on this same problem.

I managed to find the authority for all of the GATT characteristics and their data types. It’s a bit of a round about way of getting there:

For example: https://www.bluetooth.com/xml-viewer/?src=https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.pressure.xml

The interesting thing to me was all the numeric values were integers, but some have implied decimal places, so multiplication by a factor of 10 or 100 is needed for one or two decimal places, respectively. Some are also in different units. For example, Bluetooth wants pressure expressed in Pascals. The Arduino sensor gives kiloPascals. Bluetooth temperature is Celsius only, no Fahrenheit. (Though, admittedly, 80% of the world probably doesn’t care.)

With this new information, I revised my code and made a few other improvements. Everything works as expected now.

#include <ArduinoBLE.h>
#include <Arduino_HTS221.h>
#include <Arduino_LPS22HB.h>
 
// Declare the variables for reading barometric pressure, temperature, and humidity, respectively.
float p;
float t;
float h;

// Declare Bluetooth service name, and characteristics. All are standard GATT services.
BLEService environmentalSensingService("181A");
// https://specificationrefs.bluetooth.com/assigned-values/16-bit%20UUID%20Numbers%20Document.pdf
BLEUnsignedIntCharacteristic pressureCharacteristic("2A6D", BLERead); // 32-bit unsigned in Pascals, 1 decimal place.
// https://www.bluetooth.com/xml-viewer/?src=https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.pressure.xml
BLEShortCharacteristic temperatureCharacteristic("2A6E", BLERead); // 16-bit signed, 2 decimal places.
// https://www.bluetooth.com/xml-viewer/?src=https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.temperature.xml
BLEUnsignedIntCharacteristic humidityCharacteristic("2A6F", BLERead); // 16-bit unsigned, 2 decimal places.
// https://www.bluetooth.com/xml-viewer/?src=https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.humidity.xml

// Error pulse of death. Loops forever. Only used if there are problems.
void error_pulse() {
  while (1) {
    for (int i=0; i<3; i++) {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(166);
      digitalWrite(LED_BUILTIN, LOW);
      delay(167);
    }
    delay(1001);
  }  
}

void setup() {
  // Illuminate LED for 5sec to indicate startup. 
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(5000);

  // Initialize serial for debug output.
  Serial.begin(9600);

  // Initialize atmospheric sensors.
  Serial.println("Initializing humidity and temperature sensor.");
  if (!HTS.begin()) {
    Serial.println("Failed.");
    error_pulse();
  }
  Serial.println("Initializing barometric pressure sensor.");
  if (!BARO.begin()) {
    Serial.println("Failed.");
    error_pulse();
  }

  // Initialize Bluetooth communication.
  Serial.println("Initializing Bluetooth communication.");
  if (!BLE.begin()) {
    Serial.println("Failed.");
    error_pulse();
  }

  // Set up Bluetooth Environmental Sensing service.
  Serial.println("Setting up service with characteristics for pressure, temperature, and humidity.");
  BLE.setLocalName("Nano33BLE");
  BLE.setAdvertisedService(environmentalSensingService);

  // Add characteristics for barometric pressure, temperature, and humidity.
  environmentalSensingService.addCharacteristic(pressureCharacteristic);
  environmentalSensingService.addCharacteristic(temperatureCharacteristic);
  environmentalSensingService.addCharacteristic(humidityCharacteristic);

  // Make the service available.
  BLE.addService(environmentalSensingService);
  BLE.setConnectable(true);
  Serial.println("Advertising services.");
  BLE.advertise();

  // Turn off LED to indicate startup is complete.
  digitalWrite(LED_BUILTIN, LOW);
}

void loop() {

  // Wait for a connection from a central.
  BLEDevice central = BLE.central();

  // When a connection is made, activate LED and write address to serial for debug.
  if (central) {
    digitalWrite(LED_BUILTIN, HIGH);
    Serial.print("Incoming connection from: ");
    Serial.println(central.address());

    while (central.connected()) {
      // Get readings from sensors and update the charcteristic values.
      p = BARO.readPressure(KILOPASCAL);
      t = HTS.readTemperature(CELSIUS) - 4; // Subtract 4 deg C to compensate for heat generated by Nano 33 board.
      h = HTS.readHumidity();

      // Write values to serial port for debug.
      Serial.print("Pressure: ");
      Serial.print(p);
      Serial.println("kPa");
      Serial.print("Temperature: ");
      Serial.print(t);
      Serial.println("°C");
      Serial.print("Humidity: ");
      Serial.print(h);
      Serial.println("%");

      // Update Bluetooth characteristics with new values.
      pressureCharacteristic.writeValue((uint32_t) round(p * 10000)); // Convert kPa to Pa and shift for one decimal place.
      temperatureCharacteristic.writeValue((int16_t) round(t * 100)); // Shift for two decimal places.
      humidityCharacteristic.writeValue((uint16_t) round(h * 100)); // Shift for two decimal places.

      // Delay between updates. (Don't make too long of connections start to timeout.)
      delay(1000);
    }

    // Turn off LED when connection is dropped. 
    digitalWrite(LED_BUILTIN, LOW);
    Serial.println("Connection terminated.");
  }
}