Arduino 101 as Bluetooth Mouse using HID over GATT Profile

Hi guys.

I'm trying to use an Arduino 101 as a Bluetooth Mouse. I'm trying to base the reports on accelerometer movement. However, while I can connect it to my Android phone, I can't get a cursor to show up. It will show up as an input device, but no cursor. I tried a commercial Bluetooth mouse, which did cause a cursor to show up on connection. I've been bashing my head against the wall for a few days, but I can't seem to get it to work.

Maybe someone more experienced on here can show me what I've done wrong.

I'm attempting to use the HID over GATT profile.

So I have the following services and characteristics

HID Service (0x1812)

  • Protocol Model (0x2A42)
  • Boot Mouse Report (0x2A33)
  • HID Information (0x2A4A)
  • HID Report Map (0x2A4B)
  • HID Control Point (0x2A4C)
  • Report (0x2A4D)

Battery Service (0x180F)

  • Battery Level (0x2A19)

Device Information (0x180A)

  • PnP ID (0x2A50)

Thanks guys!

#include "CurieIMU.h"
#include "CurieBLE.h"
int ax, ay, az;         // accelerometer values

//Defining Bluetooth Peripheral (this board)
BLEPeripheral blePeripheral;  

//Defining HID Serivce
BLEService hidService("1812");    // declare it as an HID device
BLECharCharacteristic bootModeProtocol("2A42", BLERead | BLEWriteWithoutResponse);
BLECharacteristic bootMouseReport("2A33", BLERead | BLENotify,3);//Seting up the mouse specific characteristic
BLECharacteristic hidInformation("2A4A",BLERead, 4 );
BLECharacteristic reportMap("2A4B", BLERead,50);
BLECharCharacteristic controlPoint("2A4C",BLEWriteWithoutResponse);
BLECharacteristic mouseReport("2A4D", BLERead | BLENotify, 3); //the report value

//Defining Battery Service
BLEService batteryService("180F");
BLECharCharacteristic batteryLevel("2A19", BLERead);

//Device Information Service
BLEService deviceInformation("180A");
BLECharacteristic pnpID("2A50", BLERead, 7);

const int ledPin = 13;      // activity LED pin

int calibrateOffsets = 1; // int to determine whether calibration takes place or not

long prevmill;
int lastx;
int lasty;
unsigned char bleValue[3] = {0x00, 0x00, 0x00};
unsigned char reportMapValue[50] = {0x05,0x01,0x09,0x02,0xA1,0x01,0x09,0x01,0xA1,0x00,0x05,0x09,0x19,0x01,0x29,0x03,0x15,0x00,0x25,0x01,0x95,0x03,0x75,0x01,0x81,0x02,0x95,0x01,0x75,0x05,0x81,0x01,0x05,0x01,0x09,0x30,0x09,0x31,0x15,0x81,0x25,0x7F,0x75,0x08,0x95,0x02,0x81,0x06,0xC0,0xC0};
const unsigned char bootMode = 0x00;
const unsigned char batteryDummy = 0x63;
unsigned char hidInfo[3] = {'0112', 0x00, 0xc0};
unsigned char pnp[4] = {0x02, 0x0000, 0x0000, 0x1234};

float prevspeedx;
float prevspeedy;
float currentspeedx;
float currentspeedy;
byte movex;
byte movey;

void setup() {
  Serial.begin(9600); // initialize Serial communication
  while (!Serial);    // wait for the serial port to open

  // initialize device
  Serial.println("Initializing IMU device...");
  CurieIMU.begin();
  CurieIMU.setAccelerometerRate(200);
  CurieIMU.setAccelerometerRange(2);

  // verify connection
  Serial.println("Testing device connections...");
  if (CurieIMU.begin()) {
    Serial.println("CurieIMU connection successful");
  } else {
    Serial.println("CurieIMU connection failed");
  }

  // use the code below to calibrate accel/gyro offset values
  if (calibrateOffsets == 1) {
    Serial.println("Internal sensor offsets BEFORE calibration...");
    Serial.print(CurieIMU.getAccelerometerOffset(X_AXIS));
    Serial.print("\t"); // -76
    Serial.print(CurieIMU.getAccelerometerOffset(Y_AXIS));
    Serial.print("\t"); // -235
    Serial.println(CurieIMU.getAccelerometerOffset(Z_AXIS));

    Serial.println("About to calibrate. Make sure your board is stable and upright");
    delay(5000);

    Serial.print("Starting Acceleration calibration and enabling offset compensation...");
    CurieIMU.autoCalibrateAccelerometerOffset(X_AXIS, 0);
    CurieIMU.autoCalibrateAccelerometerOffset(Y_AXIS, 0);
    CurieIMU.autoCalibrateAccelerometerOffset(Z_AXIS, 1);
    Serial.println(" Done");

    Serial.println("Internal sensor offsets AFTER calibration...");
    Serial.print(CurieIMU.getAccelerometerOffset(X_AXIS));
    Serial.print("\t"); // -76
    Serial.print(CurieIMU.getAccelerometerOffset(Y_AXIS));
    Serial.print("\t"); // -2359
    Serial.println(CurieIMU.getAccelerometerOffset(Z_AXIS));
  }

  // configure Arduino LED for activity indicator
  pinMode(ledPin, OUTPUT);

  // Add service name and UUID for bluetooth
  blePeripheral.setLocalName("stylus");
  blePeripheral.setAdvertisedServiceUuid(hidService.uuid());

  // Add services and characteristics
 
  //HID Service and Characteristics
  blePeripheral.addAttribute(hidService);
  Serial.println("adding HID Service");
  blePeripheral.addAttribute(bootMouseReport);
  Serial.println("bootMouseReport added");
  blePeripheral.addAttribute(bootModeProtocol);
  blePeripheral.addAttribute(hidInformation);
  blePeripheral.addAttribute(reportMap);
  blePeripheral.addAttribute(controlPoint);
  blePeripheral.addAttribute(mouseReport);

  //Battery service and characteristic
  blePeripheral.addAttribute(batteryService);
  blePeripheral.addAttribute(batteryLevel);

  //Device Information
  blePeripheral.addAttribute(deviceInformation);
  blePeripheral.addAttribute(pnpID);

  // Setting the initial value for our mouse input
  bootModeProtocol.setValue('0');
  bootMouseReport.setValue(bleValue,3);
  Serial.print("boot mouse report value:   ");
  Serial.print(bleValue[0]);
  Serial.print(bleValue[1]);
  Serial.print(bleValue[2]);
  batteryLevel.setValue(batteryDummy);
  hidInformation.setValue(hidInfo, 4);
  pnpID.setValue(pnp, 7);
  reportMap.setValue(reportMapValue,50);
  mouseReport.setValue(bleValue,3);
  blePeripheral.setAppearance(962);

  // Starting the service
  blePeripheral.begin();
  Serial.println("Bluetooth device active, waiting for connections...");
  prevmill = millis();
  prevspeedx = 0;
  prevspeedy = 0;
  currentspeedx = 0;
  currentspeedy = 0;
  lastx = 0;
  lasty = 0;

  // look for central device to connect
  BLECentral central = blePeripheral.central();

  Serial.print("Connecting to central:   ");
  // print the central's MAC address
  Serial.println(central.address());

}

void loop() {
  long currentmill = millis();
  int hertz = 200;
  if (currentmill - prevmill >= 1000 / hertz) {

    // look for central device to connect
    BLECentral central = blePeripheral.central();

    // if a central is connected then we start to do things
    if (central) {
      // read raw accel/gyro measurements from device
      CurieIMU.readAccelerometer(ax, ay, az);

      currentspeedx = prevspeedx + (54 * ax);
      currentspeedy = prevspeedy + (54 * ay);
      Serial.print(ay / 258);

      Serial.print("\t");
      Serial.println((az - 16384) / 258);

      movex = byte(floor(ay / 258));
      movey = byte(floor((az - 16384) / 258));

 
      bleValue[1] = movex;
      bleValue[2] = movey;
      bootMouseReport.setValue(bleValue,3);
      prevspeedx = currentspeedx;
      prevspeedy = currentspeedy;
      prevmill = currentmill;

    }
  }
}

Also, some links that you might find helpful:

http://www.usb.org/developers/hidpage/HID1_11.pdf

https://www.arduino.cc/en/Reference/CurieBLE
https://www.bluetooth.com/specifications/gatt

I've read or at least scanned most of this stuff, but there's likely something I've missed.

Were you able to get this to work? Running your code, my computer recognizes it as a HID mouse, but it says something is wrong with it.

Do you know of any good way of getting it to run as a HID keyboard?

Did you get it to work??