Read, Write, Notify
Perform GATT operations after connecting to a BLE peripheral.
GATT operations happen after a peripheral is connected and services have been discovered. Most applications use the same shape:
- Select a service UUID and characteristic UUID.
- Check the characteristic capability.
- Read, write, subscribe, or unsubscribe.
- Keep the process, activity, or coroutine alive long enough for notifications or indications to arrive.
The snippets below assume you already have a connected peripheral, a service_uuid, and a characteristic_uuid.
Read
auto value = peripheral.read(service_uuid, characteristic_uuid);
std::cout << "Read: " << value << std::endl;uint8_t* data = NULL;
size_t data_length = 0;
simpleble_peripheral_read(peripheral, service_uuid, characteristic_uuid, &data, &data_length);
for (size_t i = 0; i < data_length; i++) {
printf("%02X ", data[i]);
}
printf("\n");
simpleble_free(data);value = peripheral.read(service_uuid, characteristic_uuid)
print(f"Read: {value}")BluetoothUUID service = new BluetoothUUID(serviceUuid);
BluetoothUUID characteristic = new BluetoothUUID(characteristicUuid);
byte[] value = peripheral.read(service, characteristic);
System.out.println("Read: " + bytesToHex(value));let value = peripheral.read(&service_uuid, &characteristic_uuid).unwrap();
println!("Read: {:?}", value);val value = peripheral.read(serviceUuid, characteristicUuid)
Log.d("SimpleBLE", "Read: ${value.joinToString(" ") { "%02x".format(it) }}")Write
SimpleBLE::ByteArray payload = SimpleBLE::ByteArray::fromHex("010203");
// Acknowledged write.
peripheral.write_request(service_uuid, characteristic_uuid, payload);
// Unacknowledged write.
peripheral.write_command(service_uuid, characteristic_uuid, payload);uint8_t payload[] = {0x01, 0x02, 0x03};
simpleble_peripheral_write_request(
peripheral,
service_uuid,
characteristic_uuid,
payload,
sizeof(payload));
simpleble_peripheral_write_command(
peripheral,
service_uuid,
characteristic_uuid,
payload,
sizeof(payload));payload = bytes([0x01, 0x02, 0x03])
# Acknowledged write.
peripheral.write_request(service_uuid, characteristic_uuid, payload)
# Unacknowledged write.
peripheral.write_command(service_uuid, characteristic_uuid, payload)byte[] payload = new byte[] {0x01, 0x02, 0x03};
peripheral.writeRequest(service, characteristic, payload);
peripheral.writeCommand(service, characteristic, payload);let payload = vec![0x01, 0x02, 0x03];
peripheral.write_request(&service_uuid, &characteristic_uuid, &payload).unwrap();
peripheral.write_command(&service_uuid, &characteristic_uuid, &payload).unwrap();SimpleDroidBLE currently exposes writeRequest and writeCommand placeholders, but characteristic writes are not implemented in the alpha Android-only binding yet. Use C++ SimpleBLE through the Android backend when you need write support today, or track SimpleDroidBLE until those methods are completed.
Notify
peripheral.notify(service_uuid, characteristic_uuid, [](SimpleBLE::ByteArray payload) {
std::cout << "Notification: " << payload << std::endl;
});
std::this_thread::sleep_for(std::chrono::seconds(10));
peripheral.unsubscribe(service_uuid, characteristic_uuid);static void on_notify(simpleble_peripheral_t peripheral,
simpleble_uuid_t service,
simpleble_uuid_t characteristic,
const uint8_t* data,
size_t data_length,
void* userdata) {
for (size_t i = 0; i < data_length; i++) {
printf("%02X ", data[i]);
}
printf("\n");
}
simpleble_peripheral_notify(peripheral, service_uuid, characteristic_uuid, on_notify, NULL);
/* Keep the process alive while notifications arrive. */
simpleble_peripheral_unsubscribe(peripheral, service_uuid, characteristic_uuid);import time
peripheral.notify(
service_uuid,
characteristic_uuid,
lambda data: print(f"Notification: {data}"),
)
time.sleep(10)
peripheral.unsubscribe(service_uuid, characteristic_uuid)peripheral.notify(service, characteristic, data -> {
System.out.println("Notification: " + bytesToHex(data));
});
Thread.sleep(10_000);
peripheral.unsubscribe(service, characteristic);let mut stream = peripheral.notify(&service_uuid, &characteristic_uuid).unwrap();
tokio::spawn(async move {
while let Some(Ok(event)) = stream.next().await {
if let simplersble::ValueChangedEvent::ValueUpdated(data) = event {
println!("Notification: {:?}", data);
}
}
});
std::thread::sleep(std::time::Duration::from_secs(10));
peripheral.unsubscribe(&service_uuid, &characteristic_uuid).unwrap();val job = CoroutineScope(Dispatchers.Main).launch {
peripheral.notify(serviceUuid, characteristicUuid).collect { payload ->
Log.d("SimpleBLE", "Notification: ${payload.joinToString(" ") { "%02x".format(it) }}")
}
}
delay(10_000)
peripheral.unsubscribe(serviceUuid, characteristicUuid)
job.cancel()Common pitfalls
- Use
write_request/writeRequestwhen you need the peripheral to acknowledge the write. - Use
write_command/writeCommandonly when the characteristic supports write-without-response. - Subscribe only to characteristics that advertise notify or indicate capability.
- Keep the process, activity, or coroutine alive while notifications are active.
- Unsubscribe before disconnecting when your workflow has a clear end.
