ESP32/Bluetooth/61. A value a phone can read
Your chip
Your language
Bluetooth · 61 of 81

A value a phone can read

A value a phone can read, and then the one extra property that stops the phone having to ask. The difference is invisible in the sketch and enormous on a battery.

/esp32/ble-read-and-notify · arduino · S3

Read is polite and expensive

PROPERTY_READ vs PROPERTY_NOTIFY
phone asks
Every poll is a request and a reply, whether or not anything moved.
How the phone gets the value
Value
21.4 °C
Radio wake-ups
0
Average current
Twelve seconds, three changes. Run it under each mode and count the arrows: the value that reaches the phone is identical, and the number of times the radio had to wake up to deliver it is not.

A read is the phone asking. It has no way to know whether anything changed, so it asks on a timer — and every one of those exchanges wakes your radio whether or not there was news.

A notify is the board telling. Same characteristic, same value, one extra property and one descriptor; the phone subscribes once and then hears from you only when something moves.

For a sensor that changes every few seconds and a phone that wants to watch it, that is the difference between a coin cell lasting a week and lasting a year.

Which to use

Read for values that are looked up rather than watched: a serial number, a firmware version, a calibration constant. There is no reason to push those.

Notify for anything that changes and anyone is watching. Fire-and-forget — if the packet is lost, the next one carries the newer value anyway, which for a temperature is exactly right.

Indicate is notify with an acknowledgement, at the cost of a round trip per message. Use it when losing one update actually matters, which for sensor data it usually does not.

Send the standard shape when there is one

The SIG has assigned UUIDs for common measurements, and using them means a generic app displays your value correctly with no configuration. Temperature is 0x2A6E: a signed 16-bit integer in hundredths of a degree, little-endian. Sending 21.4 as five ASCII characters works, and every scanner on earth will show it as five bytes of nothing.

On your S3
ChipXtensa LX7 · 2 × 240 MHz
Board settingESP32S3 Dev Module
Default I2CSDA 8 · SCL 9
Watch out forThe port vanishes after upload

The code

One service, one characteristic, both readable and notifying. The BLE2902 descriptor is what gives the phone somewhere to switch notifications on.

notify.ino
#include <BLEDevice.h>
#include <BLE2902.h>

#define SVC  "0000181a-0000-1000-8000-00805f9b34fb"   // environmental sensing
#define CHR  "00002a6e-0000-1000-8000-00805f9b34fb"   // temperature

BLECharacteristic *temp;
bool connected = false;

class Conn : public BLEServerCallbacks {
  void onConnect(BLEServer *) override    { connected = true; }
  void onDisconnect(BLEServer *s) override { connected = false; s->startAdvertising(); }
};

void setup() {
  Serial.begin(115200);
  BLEDevice::init("tinkerblock-temp");
  BLEServer *server = BLEDevice::createServer();
  server->setCallbacks(new Conn());

  BLEService *svc = server->createService(SVC);
  temp = svc->createCharacteristic(
    CHR, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
  temp->addDescriptor(new BLE2902());       // without this, no subscribe
  svc->start();
  server->getAdvertising()->start();
}

void loop() {
  static int16_t last = 0;
  int16_t c = 2140;                          // 21.40 °C, in hundredths
  if (connected && c != last) {
    temp->setValue((uint8_t *)&c, 2);
    temp->notify();                          // only when it moved
    last = c;
  }
  delay(1000);
}

Generate your own UUIDs rather than copying these. Two projects sharing a UUID is not fatal, but it makes every scanner on the bench show two devices claiming to be the same thing.

When it does not work

The phone can read but cannot subscribe

The BLE2902 descriptor is missing. Without it there is no client characteristic configuration to write to, so nRF Connect shows no subscribe arrow and notify() sends to nobody.

notify sends but the value never changes on screen

setValue must be called before notify, every time. notify() transmits whatever is currently stored — calling it after updating a local variable but not the characteristic sends the old bytes.

The board disappears after the first disconnect

Advertising stops when a client connects and does not restart on its own. Call startAdvertising() from the disconnect callback, which is what the Conn class above is for.

The number in the app is nonsense

A characteristic is bytes, not a string. A standard temperature characteristic is a signed 16-bit value in hundredths of a degree, little-endian; sending "21.4" as text is valid BLE and a generic app will show it as garbage.

Where this goes next

The other direction, and the twenty bytes that arrive when you send thirty. Nothing reports the loss.

A value a phone can write

Edit this page — content/esp32/ble-read-and-notify.mdx

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse ESP32 on the forum