Connecting to a device using the chrome.bluetooth API

Kev K picture Kev K · Feb 21, 2014 · Viewed 15.5k times · Source

I have been trying to create a Chrome App that uses the chrome.bluetooth API to connect to and communicate with the Texas Instruments CC2541 SensorTag device.

The code here detects the SensorTag and gets the device information, but the 'getProfiles' and 'getServices' methods called on the device both return empty, and the 'connect' method gives the error 'Profile not found: invalid uuid'.

I have tried multiple variations of UUIDs taken from the example SensorTag Android app (as can be seen in the code), but all give the same 'invalid uuid' error.

Even if you can't fix this particular problem it would be good to hear from anyone who has had any joy using the chrome.bluetooth API at all. My experience so far is that it is too much of a moving target to really use (yes, I do know it is 'Dev' only...), but I'd really like to get it working if possible.

Thanks for looking - any help or ideas much appreciated!

EDIT : Additional platform information
I first tried running this on Windows 7 with a CSR 4.0 Bluetooth dongle but this turns out to be completely futile: with the generic Windows 7 BT Driver Chrome can see the adapter and detect BT devices, but the driver doesn't support Low Energy so can't detect the device I want it to. Using the CSR Driver, which does support LE and which I can use in Windows "Bluetooth Devices" to connect to LE devices, Chrome.bluetooth cannot detect the Bluetooth adapter at all.

So now I'm working with an Acer C720 Chromebook, which looks like it should work, but I just get the "Invalid UUID" message whatever I try.

(Although the Chrome OS and Win/Mac/Linux 'Dev' versions of Chrome are out of step with updates - Chrome OS was behind the others for a while but has now caught up - so for a time required different format 'manifest.json' files to launch the app on the different platforms.)

main.js

// I have tried multiple variations of known UUIDS for device... all give "Profile not found: invalid uuid" 
var profiles = [
             // UUID_IRT_SERV from example Android app
                {uuid : 'f000aa00-0451-4000-b000-000000000000', name : 'SensorTag1-lc'},
                {uuid : 'F000AA00-0451-4000-B000-000000000000', name : 'SensorTag1-uc'},
                {uuid : 'f000aa00', name : 'SensorTag1-shortlc'},
                {uuid : 'F000AA00', name : 'SensorTag1-shortuc'},
             // UUID_IRT_DATA from example Android app
                {uuid : 'f000aa01-0451-4000-b000-000000000000', name : 'SensorTag2-lc'},
                {uuid : 'F000AA01-0451-4000-B000-000000000000', name : 'SensorTag2-uc'},
                {uuid : 'f000aa01', name : 'SensorTag2-shortlc'},
                {uuid : 'F000AA01', name : 'SensorTag2-shortuc'},
             // UUID_IRT_CONF from example Android app
                {uuid : 'f000aa02-0451-4000-b000-000000000000', name : 'SensorTag3-lc'},
                {uuid : 'F000AA02-0451-4000-B000-000000000000', name : 'SensorTag3-uc'},
                {uuid : 'f000aa02', name : 'SensorTag3-shortlc'},
                {uuid : 'F000AA02', name : 'SensorTag3-shortuc'},
             // UUID_IRT_PERI from example Android app
                {uuid : 'f000aa03-0451-4000-b000-000000000000', name : 'SensorTag4-lc'},
                {uuid : 'F000AA03-0451-4000-B000-000000000000', name : 'SensorTag4-uc'},
                {uuid : 'f000aa03', name : 'SensorTag4-shortlc'},
                {uuid : 'F000AA03', name : 'SensorTag4-shortuc'},
             // UUID_KEY_SERV from example Android app
                {uuid : '0000ffe0-0000-1000-8000-00805f9b34fb', name : 'SensorTag5-lc'},
                {uuid : '0000FFE0-0000-1000-8000-00805F9B34FB', name : 'SensorTag5-uc'},
                {uuid : '0000ffe0', name : 'SensorTag5-shortlc'},
                {uuid : '0000FFE0', name : 'SensorTag5-shortuc'},
             // UUID_KEY_DATA from example Android app
                {uuid : '0000ffe1-0000-1000-8000-00805f9b34fb', name : 'SensorTag6-lc'},
                {uuid : '0000FFE1-0000-1000-8000-00805F9B34FB', name : 'SensorTag6-uc'},
                {uuid : '0000ffe1', name : 'SensorTag6-shortlc'},
                {uuid : '0000FFE1', name : 'SensorTag6-shortuc'},
];

// Listener to deal with initial connection
chrome.bluetooth.onConnection.addListener(onConnected);

// onAdapterStateChanged callback - for debug only
chrome.bluetooth.onAdapterStateChanged.addListener(function(newStatus) {
    log('onAdapterStateChanged: ' + JSON.stringify(arguments));
});

// Logs debug messages to app window
function log(msg) {
  var msg_str = (typeof(msg) == 'object') ? JSON.stringify(msg) : msg;
  console.log(msg_str);
  var l = document.getElementById('log');
  if (l) {
    l.innerText += msg_str + '\n';
  }
}

// Function that is called on connection to device
var onConnected = function(socket) {
    log("Success - Connected to SensorTag!");
    log("Socket: " + JSON.stringify(socket));
}

function recordDevice(device) {
    log("Found BT Device: " + JSON.stringify(device));
    if (device.name == "SensorTag") {
        log("Got SensorTag...");
        // Stop discovery and then connect to SensorTag
        chrome.bluetooth.stopDiscovery(connectToSensorTag(device));
    }
}

function connectToSensorTag(device) {
    log("Getting profiles of SensorTag...");
    chrome.bluetooth.getProfiles({device: device}, function(profiles) { 
        log('Got profiles: ' + JSON.stringify(profiles));
    });
    chrome.bluetooth.getServices({deviceAddress: device.address}, function(services) { 
        log('Got services: ' + JSON.stringify(services));
    });
    for (var i = 0; i < profiles.length; i++) {
        chrome.bluetooth.connect({ device: device, profile: profiles[i] }, function() {
            log('Connect called: ' + JSON.stringify(arguments));
            if (chrome.runtime.lastError) {
                  log("Connection error: " + chrome.runtime.lastError.message);
            }
        });
    }
}

function findDevices() {
    log("Finding devices...");
    chrome.bluetooth.startDiscovery({deviceCallback: recordDevice});
}

// App execution begins here.
// Add all profiles to try connection later
for (var i = 0; i < profiles.length; i++) {
    log("Adding profile: " + profiles[i]);
    chrome.bluetooth.addProfile(profiles[i], function() {
        log("SensorTag profile added."));
    });
}

chrome.bluetooth.getAdapterState( function(result) {
      if (result.powered == false || result.available == false ) {
        log("Error: No bluetooth adapter available.");
      } else {
        log("Bluetooth adapter enabled.");
        findDevices();
      }   
});

index.html

<html>
<head></head>
<body>
    <div id="log"></div>
</body>
<script src="main.js"></script>
</html>

manifest.json

{
  "manifest_version": 2,
  "name": "Connect to SensorTag",
  "description": "Connects to TI SensorTag",
  "version": "1.0",
  "minimum_chrome_version": "30",
  "app": {
    "background": {
      "scripts": ["background.js"]
    }
  },
  "bluetooth": {}
}

background.js

chrome.app.runtime.onLaunched.addListener(function() {
  chrome.app.window.create('index.html', {
    id: "window1",
    bounds: {
      width: 640,
      height: 480
    }
  });
});

Answer

Jesse S picture Jesse S · Feb 25, 2014

I ran into this same problem. After a fair bit of digging I was able to pin down two issues with the pattern I was using.

The first issue is that it turns out, from what I can tell, an empty "bluetooth" object in the manifest is not actually valid despite the fact that there are examples around that use it and chrome does not complain:

"bluetooth": {}

Rather your bluetooth manifest entry should look something like this:

"bluetooth":{
"profiles":
    [
        "00001101-0000-1000-8000-00805f9b34fb" // array of uuids
    ]
},

The documentation for this, such that it is, can be found in this chrome commit from two weeks ago: https://codereview.chromium.org/145663004/patch/760001/770016

The other idiosyncrasy of this API that I discovered while tracking this issue down is that it is not only the connect call that can leave errors in "chrome.runtime.lastError", any of these calls may leave messages behind for you. If you check lastError after your addProfile call, you will likely see chrome complain about permissions: "Permission to add profile denied."

Going back and reading the chrome.bluetooth api documentation it actually says this in the description near the top (although I missed it just like you did)

Use the chrome.bluetooth API to connect to a Bluetooth device. All functions report failures via chrome.runtime.lastError.

(emphasis added) http://developer.chrome.com/apps/bluetooth#type-Profile

Hope this helps.