Bluetooth
Communicating with BLE peripherals using the Web Bluetooth API
nx.js implements the Web Bluetooth API
for communicating with Bluetooth Low Energy (BLE) peripherals as a GATT
client. It is exposed via the global
navigator.bluetooth
object, and is backed by the Switch's application-facing btm.u + bt
services.
This means the same Web Bluetooth code that runs in a Chrome web app can run on the Switch.
Implemented Interfaces
| Interface | Description |
|---|---|
Bluetooth (navigator.bluetooth) | Entry point: availability + device requests |
BluetoothDevice | A discovered/known device |
BluetoothRemoteGATTServer | The connection to a device's GATT server |
BluetoothRemoteGATTService | A GATT service |
BluetoothRemoteGATTCharacteristic | A characteristic (read/write/notify) |
BluetoothUUID | UUID helpers |
Finding a device
On the Switch, the application BLE scanner is filter-based: devices are
discovered by an advertised 128-bit service UUID. Pass the service UUIDs you
care about in the filters of
requestDevice():
const device = await navigator.bluetooth.requestDevice({
filters: [
// Nordic UART service (common on Arduino/ESP32/Adafruit boards)
{ services: ['6e400001-b5a3-f393-e0a9-e50e24dcca9e'] },
],
});
console.log(`Found: ${device.name ?? '(unnamed)'} [${device.id}]`);Note
Consumer devices that advertise only manufacturer data (e.g. AirPods) cannot be discovered by scanning. See Connecting by address below.
Connecting by address
nx.js adds a deviceId extension to requestDevice() so you can connect to a
device by its known Bluetooth address — bypassing the scan entirely. This is
useful for devices that advertise no usable service UUID:
const device = await navigator.bluetooth.requestDevice({
deviceId: 'aa:bb:cc:dd:ee:ff',
optionalServices: ['6e400001-b5a3-f393-e0a9-e50e24dcca9e'],
});Tip
Use a phone app like nRF Connect to discover a device's address and the service UUIDs it advertises.
Reading and writing
Connect to the GATT server, look up a service and characteristic, then read or write its value:
const server = await device.gatt.connect();
const service = await server.getPrimaryService(
'6e400001-b5a3-f393-e0a9-e50e24dcca9e',
);
const characteristic = await service.getCharacteristic(
'6e400002-b5a3-f393-e0a9-e50e24dcca9e',
);
// Read
const value = await characteristic.readValue(); // DataView
// Write
await characteristic.writeValueWithResponse(new Uint8Array([0x01, 0x02]));Notifications
Subscribe to characteristic notifications (the CCCD write is handled
automatically) and listen for characteristicvaluechanged events:
await characteristic.startNotifications();
characteristic.addEventListener('characteristicvaluechanged', (event) => {
const value = event.target.value; // DataView
console.log('notified:', new Uint8Array(value.buffer));
});Disconnection
Listen for the gattserverdisconnected event to know when the device drops:
device.addEventListener('gattserverdisconnected', () => {
console.log('Device disconnected.');
});Other features
- Browser-style ATT MTU negotiation
- Writes with and without response
- GATT service/characteristic discovery
navigator.bluetooth.getAvailability()
See the apps/bluetooth
example for a complete GATT explorer that dumps a device's full GATT table.