Web BLE Read changing GATT values

Updated on 18 November 2022
dev board Adafruit Feather Bluefruit
chip nRF52
features BLE GATT
This tutorial is more than 1 year old. If the steps below do not work, then please check the latest versions and the documentations of the individual tools used.

Before starting

Dependancies

Ensure the following dependancies are downloaded and available:

Pre-requisites

Try these simpler or similar examples:

Buy the components

Code

Download code web-ble-gatt.ino
#include <bluefruit.h>

uint8_t uvindexvalue = 0x0;
#define UUID16_SVC_ENVIRONMENTAL_SENSING 0x181A
#define UUID16_CHR_UV_INDEX 0x2A76

BLEService environmental_sensing_service = BLEService(UUID16_SVC_ENVIRONMENTAL_SENSING);
BLECharacteristic uv_index_characteristic = BLECharacteristic(UUID16_CHR_UV_INDEX);

void setup() {
  Serial.begin(115200);
  delay(500);
  Serial.println("Start!");

  Bluefruit.begin();
  Bluefruit.setName("Palm");

  setupESService();
  startAdv();
}

void loop() {
  uvindexvalue = random(0, 11);

  Serial.print("UV Index: ");
  Serial.println(uvindexvalue);

  if (uv_index_characteristic.indicate(&uvindexvalue, sizeof(uvindexvalue))) {
    Serial.print("Updated UV Index: ");
    Serial.println(uvindexvalue);
  } else {
    Serial.println("UV Index Indicate not set");
  }

  delay(1000);
}

void startAdv(void) {
  Bluefruit.Advertising.addService(environmental_sensing_service);

  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.Advertising.addName();
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);
  Bluefruit.Advertising.setFastTimeout(30);
  Bluefruit.Advertising.start(0);
}

void setupESService(void) {
  environmental_sensing_service.begin();
  uv_index_characteristic.setProperties(CHR_PROPS_INDICATE);
  uv_index_characteristic.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
  uv_index_characteristic.setFixedLen(1);
  uv_index_characteristic.begin();
  uv_index_characteristic.write(&uvindexvalue, sizeof(uvindexvalue));
}

Makefile

BOARD?=adafruit:nrf52:feather52832
PORT?=/dev/tty.SLAB_USBtoUART

.PHONY: default lint all flash clean

default: lint all flash clean

lint:
	cpplint --extensions=ino --filter=-legal/copyright,-whitespace/line_length *.ino

all:
	arduino-cli compile --fqbn $(BOARD) ./

flash:
	adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application ./.*.hex dfu-package.zip
	adafruit-nrfutil dfu serial --package dfu-package.zip -p $(PORT) -b 115200

clean:
	rm -f .*.elf
	rm -f .*.hex
	rm -f *.zip

serve:
	echo "Open Chrome browser at http://localhost:8000/web-ble-read.html"
	python -m SimpleHTTPServer 8000

Serial console

Serial output from the firmware.

Web BLE Read changing GATT values serial console

Browser console

Open the browser developer console.

Web BLE Read changing GATT values browser

Description

Connect to a BLE device with BLE GATT Service of Environmental Sensing and BLE GATT Characteristic of UV Index and read the values from the Chrome browser console when the readings are updated.

Ensure the browser side of the code is also implemented with web-ble-gatt.html.

Download code View demo

<button id="read">Connect with BLE device</button>
<button id="start" disabled>Start</button>
<button id="stop" disabled>Stop</button>

<script>
  var deviceName = 'Palm'
  var bleService = 'environmental_sensing'
  var bleCharacteristic = 'uv_index'
  var bluetoothDeviceDetected
  var gattCharacteristic

  document.querySelector('#read').addEventListener('click', function() {
    if (isWebBluetoothEnabled()) { read() }
  })

  document.querySelector('#start').addEventListener('click', function(event) {
    if (isWebBluetoothEnabled()) { start() }
  })

  document.querySelector('#stop').addEventListener('click', function(event) {
    if (isWebBluetoothEnabled()) { stop() }
  })

  function isWebBluetoothEnabled() {
    if (!navigator.bluetooth) {
      console.log('Web Bluetooth API is not available in this browser!')
      return false
    }

    return true
  }

  function getDeviceInfo() {
    let options = {
      optionalServices: [bleService],
      filters: [
        { "name": deviceName }
      ]
    }

    console.log('Requesting any Bluetooth Device...')
    return navigator.bluetooth.requestDevice(options).then(device => {
      bluetoothDeviceDetected = device
    }).catch(error => {
      console.log('Argh! ' + error)
    })
  }

  function read() {
    return (bluetoothDeviceDetected ? Promise.resolve() : getDeviceInfo())
    .then(connectGATT)
    .then(_ => {
      console.log('Reading UV Index...')
      return gattCharacteristic.readValue()
    })
    .catch(error => {
      console.log('Waiting to start reading: ' + error)
    })
  }

  function connectGATT() {
    if (bluetoothDeviceDetected.gatt.connected && gattCharacteristic) {
      return Promise.resolve()
    }

    return bluetoothDeviceDetected.gatt.connect()
    .then(server => {
      console.log('Getting GATT Service...')
      return server.getPrimaryService(bleService)
    })
    .then(service => {
      console.log('Getting GATT Characteristic...')
      return service.getCharacteristic(bleCharacteristic)
    })
    .then(characteristic => {
      gattCharacteristic = characteristic
      gattCharacteristic.addEventListener('characteristicvaluechanged',
          handleChangedValue)
      document.querySelector('#start').disabled = false
      document.querySelector('#stop').disabled = true
    })
  }

  function handleChangedValue(event) {
    let value = event.target.value.getUint8(0)
    var now = new Date()
    console.log('> ' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + ' UV Index is ' + value)
  }

  function start() {
    gattCharacteristic.startNotifications()
    .then(_ => {
      console.log('Start reading...')
      document.querySelector('#start').disabled = true
      document.querySelector('#stop').disabled = false
    })
    .catch(error => {
      console.log('[ERROR] Start: ' + error)
    })
  }

  function stop() {
    gattCharacteristic.stopNotifications()
    .then(_ => {
      console.log('Stop reading...')
      document.querySelector('#start').disabled = false
      document.querySelector('#stop').disabled = true
    })
    .catch(error => {
      console.log('[ERROR] Stop: ' + error)
    })
  }
</script>

References