Bluetooth Low Energy (BLE) høres kanskje ut som noe klurete lavnivå-greier for dingser. Jeg synes absolutt ikke det. BLE har en ganske enkel og elegant måte å implementertere data-endepunkter på, og har inspirert meg i tanker rundt superenkelt API design generelt.

Dette utarbeida og utbredte API-designet finnes på milliarder av ting, og du bruker det sannsynligvis daglig enten du er klar over det eller ikke.

TLDR;

BLE sin standard for tjenester er API i 2 lag. GATT heter dette.

  • Characteristic. Det er et helt konkret datapunkt som i sin enkelhet kan ta en verdi (write), returnere en verdi (read) eller gi deg en verdi asynkront (notify). F.eks Temperatur i Celcius kan være en READ, og gjerne NOTIFY i tillegg.
  • Service er gruppering av flere karakteristikker. Hvis du i tillegg har verdien luftfuktighet hører eksempelvis intuitivt sammen med temperatur, så det vil være naturlig at de ligger i samme service. Et fornuftig navn på den tjenesten kunne vært noe sånt som ‘Environmental Sensing’.

Dette er kjernen i API-designet i BLE. Services som holder på karakteristikker altså.

Eksempel på et komplett API

UUID-er er helt sentrale. Service og Characteristics er UUID-er.

La oss finne på to UUID-er og lage dette helt nye API-et som ingen noensinne har sett før:

  • Service e7b45330-61f5-403a-aa70-cf4293b019d8
  • Characteristic 953a2e20-5f52-4395-9b3d-102415feeab7, har støtte for READ og vi returnerer alltid 42.

Nå kan en BLE klient koble seg til, og spørre etter verdien i 953a2e20-5f52-4395-9b3d-102415feeab7 (under e7b45330-61f5-403a-aa70-cf4293b019d8) og jammen få 42 rett i fleisen.

UUID-er… Æsj!

Ja, UUID-er er kontrakten mellom maskiner i BLE. Og det funker helt fint.

Service og Characteristics

Slapp av, det finnes Descriptorer du kan henge på for å gi mennesker en liten tekst for hver characteristic.

Descriptor

BLE? ‘There is an App for that’

Ja virkelig. Hvis du vil titte på API-er til BLE-tingene rundt deg her du sitter så anbefaler jeg å bruke nRF Connect fra Nordic Semiconductor. Det er fra den jeg har screenshotta over her, og det er en generisk Bluetooth Low energy App. Installer den med en gang! Og hvis du har litt gadgets rundt deg så kan du skule rundt på Services og Characteristics.

Hva er meningen med livet?

Et voldsomt spørsmål, med et kjent svar

Descriptor

Standardiserte UUID-er

I stedet for å spinne opp UUID-er så kan du kan følge standard UUID-er definert i GATT sine definisjoner, f.eks Audio Stream Control, Batteristatus, Fitness, Insulinmålinger 🥁 ooooog 🥁 Environmental Sensing (0x181A). Environmental Sensing var altså ikke noe vi trakk ut av flosshatten oppe i TLDR-seksjonen, men en tjeneste som ligger godt forankret i GATT. Når du følger standard UUID-er så har du integrert mot klienter som finnes der ute allerede. Som har et forhold til disse UUID-ene - og det får du gratis.

Dette er kjernen av hvorfor tingene dine kjenner igjen de andre tingene dine, uten “drivere” eller spesial-apps men bare Bluetooth Low Energy.

Du kan bruke standard UUID-er, OG kjøre dine egne i tillegg hvis du ikke finner alt du trenger i standardene. Dette er nok ganske vanlig for dingser som skal gå utover å f.eks være et generisk bluetooth tastatur. De pleier å ha en slags Companion App du kan laste ned om du vil.

Vil avslutte med en kjekk detalj; UUID-er som er innlemmet standarden består kun av 16 bits (f.eks 0x181A), som unektelig er mer behagelig for øyet enn 128 bits e7b45330-61f5-403a-aa70-cf4293b019d8

La oss skitne til hendene

Vi bruker

Descriptor

Vi definerer UUID-er på toppen av main.cpp

#include <Arduino.h>
#include <ArduinoBLE.h>

BLEService myService("e7b45330-61f5-403a-aa70-cf4293b019d8");
BLEStringCharacteristic myCharacteristic("953a2e20-5f52-4395-9b3d-102415feeab7", BLERead | BLEBroadcast, 2);
BLEDescriptor characterLabellDescriptor("2901", "Meningen med livet er...");

Setup og Loop

Som kjent, dersom du har vært borti Arduino, så kjøres setup kun 1 gang ved boot, og loop repeteres resten av levetiden.

void setup() {
  Serial.begin(9600); // boilerplate
  if (!BLE.begin()) {
    Serial.println("Fikk ikke startet Bluetooth Low Energy :'( ");
    while (1);
  }
  
  BLE.setLocalName("Hoppsann"); // Navnet du ser når du scanner
  
  BLE.setAdvertisedService(myService); // Vi snakker om Advertisement i neste seksjon

  myService.addCharacteristic(myCharacteristic);

  myCharacteristic.addDescriptor(characterLabellDescriptor);
  
  myCharacteristic.setValue("42");

  BLE.addService(myService);
  BLE.advertise();
}

void loop() {
    BLE.poll();
}

Hva er advertise?

Dette er viktig i Bluetooth Low Energy. Det er strømkrevende å skrike rundt seg på radio. For å spare batteri så slår du gjerne av Advertise etter at en klient er tilkoblet.

Legg til handler av connect og disconnect i setup

  BLE.setEventHandler(BLEConnected, connected);
  BLE.setEventHandler(BLEDisconnected, disconnected);

Og handlerene

void connected(BLEDevice central) {
  BLE.stopAdvertise();
}

void disconnected(BLEDevice central) {
  BLE.advertise();
}

Det var det

Koden finner du i et platformio-prosjekt på github.