Skip to main content Link Menu Expand (external link) Document Search Copy Copied

(Nano 33 BLE Sense) BLE Communication

2023 11.04

reference


  1. 1. What is Bluetooth® Low Energy?
  2. 2. How Does Bluetooth® Low Energy Work?
  3. 3. Services and Characteristics
  4. 4. Using Bluetooth® Low Energy and Arduino
    1. Programming the Central Device
    2. Programming the Peripheral Device

1. What is Bluetooth® Low Energy?

  • Classic Bluetooth®: Includes three working modes- BR, EDR, and HS(AMP)
    • (ex) Audio applications(wireless headphones)
  • Bluetooth® Low Energy: Designed to reduce the power consumption by reducing the amount of time that the Bluetooth radio is on.
    • (ex) Power constrained applications, such as wearables and IoT devices
  • Classic Bluetooth® and Bluetooth® Low Energy have different physical layer modulation and demodulation methods, hence, they are not compatible

2. How Does Bluetooth® Low Energy Work?

  • Central Role (= Servers): Performs a scan and listen for broadcasting information. The central device attempts to connect with peripheral device as soon as it picks up the advertising information.
  • Peripheral Role (= Clients): When Bluetooth® connection is established, it will advertise or broadcast information to near devices.
  • Once a connection is established, the central device will interact with the available information that the peripheral device has. This information exchange is called services.

3. Services and Characteristics

  • Service: A group of capabilities.
    • (ex) A smartwatch can measure your heart rate, track physical activity and track your sleep patterns. These three capabilities, would exist in a service called health service.
  • UUID: A unique identification code given to every service. 16-bit or 32-bit long for official Bluetooth® services while non-official Bluetooth® services are 128-bit long.
  • Characteristics: Each characteristic represents a unique capability of the central device. The peripheral device can
    • 1) write information to,
    • 2) request information from,
    • 3) and subscribe to updates from these characteristics.
      Any characteristic, like the services, have a 16 bit long or 128 bit long UUID.

4. Using Bluetooth® Low Energy and Arduino

  • Central: Nano 33 BLE Sense (to use the embedded gesture sensor)
  • Peripheral: Nano 33 BLE
  • Service: gestureService
  • Characteristic: gesture_type
  • Summary: If the central device detects a gesture with its gesture sensor, it will write the type of the gesture detected in the gesture_type characteristic of the gestureService. Then, based on the value stored in the gesture_type characteristic, the built-in RGB LED of the peripheral device will turn on a specific color.

Programming the Central Device

/*
  BLE_Central_Device.ino
  https://docs.arduino.cc/tutorials/nano-33-ble-sense/ble-device-to-device

  This program uses the ArduinoBLE library to set-up an Arduino Nano 33 BLE Sense
  as a central device and looks for a specified service and characteristic in a
  peripheral device. If the specified service and characteristic is found in a
  peripheral device, the last detected value of the on-board gesture sensor of
  the Nano 33 BLE Sense, the APDS9960, is written in the specified characteristic.

  The circuit:
  - Arduino Nano 33 BLE Sense.

  This example code is in the public domain.
*/

#include <ArduinoBLE.h>
#include <Arduino_APDS9960.h>

// Unique UUID code for service
const char *deviceServiceUuid = "19b10000-e8f2-537e-4f6c-d104768a1214";
// Unique UUID code for characteristic
const char *deviceServiceCharacteristicUuid = "19b10001-e8f2-537e-4f6c-d104768a1214";

int gesture = -1;
int oldGestureValue = -1;

void setup()
{
  Serial.begin(9600);
  while (!Serial)
    ;

  // Initialize the APDS9960 sensor for gesture recognition
  if (!APDS.begin())
  {
    Serial.println("* Error initializing APDS9960 sensor!");
  }

  APDS.setGestureSensitivity(80);

  // Initialize BLE
  if (!BLE.begin())
  {
    Serial.println("* Starting Bluetooth® Low Energy module failed!");
    while (1)
      ;
  }

  /*
  // Set the name of the central device,
  // and start advertising to make itself
  // discoverable to other peripheral devices
  */
  BLE.setLocalName("Nano 33 BLE (Central)");
  BLE.advertise();

  Serial.println("Arduino Nano 33 BLE Sense (Central Device)");
  Serial.println(" ");
}

void loop()
{
  connectToPeripheral();
}

void connectToPeripheral()
{
  BLEDevice peripheral;

  Serial.println("- Discovering peripheral device...");

  // Scan for a peripheral with a specific service UUID
  // find available device and store in 'peripheral'
  do
  {
    BLE.scanForUuid(deviceServiceUuid);
    peripheral = BLE.available();
  } while (!peripheral);

  // Peripheral Info
  if (peripheral)
  {
    Serial.println("* Peripheral device found!");
    Serial.print("* Device MAC address: ");
    Serial.println(peripheral.address());
    Serial.print("* Device name: ");
    Serial.println(peripheral.localName());
    Serial.print("* Advertised service UUID: ");
    // This should be same as central service uuid
    Serial.println(peripheral.advertisedServiceUuid());
    Serial.println(" ");
    BLE.stopScan(); // stop scan when found

    // Control the peripheral once found
    controlPeripheral(peripheral);
  }
}

void controlPeripheral(BLEDevice peripheral)
{
  // Check if peripheral is connected
  Serial.println("- Connecting to peripheral device...");
  if (peripheral.connect())
  {
    Serial.println("* Connected to peripheral device!");
    Serial.println(" ");
  }
  else
  {
    Serial.println("* Connection to peripheral device failed!");
    Serial.println(" ");
    return;
  }

  // Check for peripheral's services and characteristics.
  Serial.println("- Discovering peripheral device attributes...");
  if (peripheral.discoverAttributes())
  {
    Serial.println("* Peripheral device attributes discovered!");
    Serial.println(" ");
  }
  else
  {
    Serial.println("* Peripheral device attributes discovery failed!");
    Serial.println(" ");
    peripheral.disconnect();
    return;
  }

  // Store peripheral's characteristic container to central's characteristic
  BLECharacteristic gestureCharacteristic = peripheral.characteristic(deviceServiceCharacteristicUuid);

  // Check if peripheral device initialized/writable characteristic
  if (!gestureCharacteristic)
  {
    Serial.println("* Peripheral device does not have gesture_type characteristic!");
    peripheral.disconnect();
    return;
  }
  else if (!gestureCharacteristic.canWrite())
  {
    Serial.println("* Peripheral does not have a writable gesture_type characteristic!");
    peripheral.disconnect();
    return;
  }

  // Write value to gesture_type characteristic
  while (peripheral.connected())
  {
    gesture = gestureDetectection();
    // Check if gesture changed from before
    if (oldGestureValue != gesture)
    {
      oldGestureValue = gesture;
      Serial.print("* Writing value to gesture_type characteristic: ");
      Serial.println(gesture);
      gestureCharacteristic.writeValue((byte)gesture);
      Serial.println("* Writing value to gesture_type characteristic done!");
      Serial.println(" ");
    }
  }
  Serial.println("- Peripheral device disconnected!");
}

// Gesture detection function
int gestureDetectection()
{
  if (APDS.gestureAvailable())
  {
    gesture = APDS.readGesture();

    switch (gesture)
    {
    case GESTURE_UP:
      Serial.println("- UP gesture detected");
      break;
    case GESTURE_DOWN:
      Serial.println("- DOWN gesture detected");
      break;
    case GESTURE_LEFT:
      Serial.println("- LEFT gesture detected");
      break;
    case GESTURE_RIGHT:
      Serial.println("- RIGHT gesture detected");
      break;
    default:
      Serial.println("- No gesture detected");
      break;
    }
  }
  return gesture;
}

Programming the Peripheral Device

/*
  BLE_Peripheral.ino

  This program uses the ArduinoBLE library to set-up an Arduino Nano 33 BLE
  as a peripheral device and specifies a service and a characteristic. Depending
  of the value of the specified characteristic, an on-board LED gets on.

  The circuit:
  - Arduino Nano 33 BLE.

  This example code is in the public domain.
*/

#include <ArduinoBLE.h>

enum
{
  GESTURE_NONE = -1,
  GESTURE_UP = 0,
  GESTURE_DOWN = 1,
  GESTURE_LEFT = 2,
  GESTURE_RIGHT = 3
};

// Unique UUID code for service
const char *deviceServiceUuid = "19b10000-e8f2-537e-4f6c-d104768a1214";
// Unique UUID code for characteristic
const char *deviceServiceCharacteristicUuid = "19b10001-e8f2-537e-4f6c-d104768a1214";

int gesture = -1;

// Define a BLE service and characteristic !! (done in peripheral)
BLEService gestureService(deviceServiceUuid);
BLEByteCharacteristic gestureCharacteristic(deviceServiceCharacteristicUuid, BLERead | BLEWrite);

void setup()
{
  Serial.begin(9600);
  while (!Serial)
    ;
  // Initialize LEDs and set their initial states
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  pinMode(LEDB, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDB, HIGH);
  digitalWrite(LED_BUILTIN, LOW);

  // Initialize the BLE module
  if (!BLE.begin())
  {
    Serial.println("- Starting Bluetooth® Low Energy module failed!");
    while (1)
      ;
  }

  /*
  // Set the name of the peripheral device,
  // and define the services and characteristics
  */
  BLE.setLocalName("Arduino Nano 33 BLE (Peripheral)");
  // the service that the peripheral will advertise
  // used to specify which service you want to actively advertise to other devices.
  BLE.setAdvertisedService(gestureService);
  // adds a characteristic to 'gestureService'
  gestureService.addCharacteristic(gestureCharacteristic);
  // adds 'gestureService' to the list of services provided by the peripheral, necessary to expose the service to central devices
  // registers the service with the BLE stack, making it available for central devices to discover when they connect to the peripheral.
  BLE.addService(gestureService);
  // initial value of 'gestureCharacteristic' set to -1
  gestureCharacteristic.writeValue(-1);
  // This function call starts the advertising process
  BLE.advertise();

  Serial.println("Nano 33 BLE (Peripheral Device)");
  Serial.println(" ");
}

void loop()
{
  // Checks for a central device's connection
  BLEDevice central = BLE.central();
  Serial.println("- Discovering central device...");
  delay(500);

  if (central)
  {
    Serial.println("* Connected to central device!");
    Serial.print("* Device MAC address: ");
    Serial.println(central.address());
    Serial.println(" ");

    while (central.connected())
    {
      // Check if characteristic was written to by central
      if (gestureCharacteristic.written())
      {
        // Update the LEDs based on the received gesture value
        gesture = gestureCharacteristic.value();
        writeGesture(gesture);
      }
    }

    Serial.println("* Disconnected to central device!");
  }
}

// Handle changes in the characteristic's value and update LEDs
void writeGesture(int gesture)
{
  Serial.println("- Characteristic <gesture_type> has changed!");

  switch (gesture)
  {
  case GESTURE_UP:
    Serial.println("* Actual value: UP (red LED on)");
    Serial.println(" ");
    digitalWrite(LEDR, LOW);
    digitalWrite(LEDG, HIGH);
    digitalWrite(LEDB, HIGH);
    digitalWrite(LED_BUILTIN, LOW);
    break;
  case GESTURE_DOWN:
    Serial.println("* Actual value: DOWN (green LED on)");
    Serial.println(" ");
    digitalWrite(LEDR, HIGH);
    digitalWrite(LEDG, LOW);
    digitalWrite(LEDB, HIGH);
    digitalWrite(LED_BUILTIN, LOW);
    break;
  case GESTURE_LEFT:
    Serial.println("* Actual value: LEFT (blue LED on)");
    Serial.println(" ");
    digitalWrite(LEDR, HIGH);
    digitalWrite(LEDG, HIGH);
    digitalWrite(LEDB, LOW);
    digitalWrite(LED_BUILTIN, LOW);
    break;
  case GESTURE_RIGHT:
    Serial.println("* Actual value: RIGHT (built-in LED on)");
    Serial.println(" ");
    digitalWrite(LEDR, HIGH);
    digitalWrite(LEDG, HIGH);
    digitalWrite(LEDB, HIGH);
    digitalWrite(LED_BUILTIN, HIGH);
    break;
  default:
    digitalWrite(LEDR, HIGH);
    digitalWrite(LEDG, HIGH);
    digitalWrite(LEDB, HIGH);
    digitalWrite(LED_BUILTIN, LOW);
    break;
  }
}