Causes the imp to begin scanning for GAP advertisements
Device
Only available on the imp004m and imp006 (impOS 42)
Name | Type | Description |
---|---|---|
callback | Function |
A function to process any detected advertising data
|
Nothing
This method initiates scanning and registers a function that will be called when a compatible imp detects an advertisement signal.
The callback function has a single parameter of its own, adverts, into which an array of detected advertisements is passed. Each advert is a table with the following keys:
Key | Data Type | Description |
---|---|---|
type | Integer | The type of advertisement detected (see below) |
rssi | Integer | The signal strength (-127 to 20) of the received data in dBm |
address | String | The peer’s address as a 12-character hexadecimal string |
addresstype | Integer | 0 if the address is public, 1 if the address is random |
data | Blob | The raw data received. Up to 31 bytes in length |
time | Integer | The time of receipt by impOS, as an integer millisecond time against the same clock as hardware.millis() |
rule | Integer | The index (integer, zero-based) of the filter that allowed this advert. This is intended to both assist debugging of rules, and to allow Squirrel to fast-path adverts which match a certain rule (see bluetooth.setscanfilter() examples). This will be zero if the default accept-all filter is in use. |
impOS™ will add as many advertisements to the adverts array as it can but will not add latency to achieve this.
impOS doesn’t interpret or validate the raw advertisement data (provided as data in the advert table) in any way other than to cap the length at the specified 31 bytes. Specifically, data is not guaranteed to contain syntactically valid advertising or scan data. Your application code should therefore take great care when parsing data.
Typically, a user will want to process advertising and scan response packets together; your application is responsible for mapping advertisement to scan response.
Bluetooth Constant | Value | Description |
---|---|---|
ADV_IND | 0 | Connectable undirected advertisement |
ADV_DIRECT_IND | 1 | Connectable directed advertisement |
ADV_SCAN_IND | 2 | Scannable undirected advertisement |
ADV_NONCONN_IND | 3 | Non-connectable undirected advertisement |
SCAN_RSP | 4 | Scan response |
Note These constants are not defined in Squirrel — please use the integer value in comparisons.
Passive iBeacon Scanning
Scan for iBeacons and save each in a table as they are found.
bt <- null;
// Beacons
beacons <- [];
function makePrettyUUID(uuid) {
return uuid.slice(0,9) + "-" + uuid.slice(8,12) + "-" + uuid.slice(12,16) + "-" + uuid.slice(16);
}
function reportBeacon(beacon) {
server.log("Beacon added: " + makePrettyUUID(beacon.uuid) + " (" + beacon.majorString + "/" + beacon.minorString + ")");
}
function addBeacon(beacon) {
beacons.append(beacon);
reportBeacon(beacon);
}
try {
// Instantiate BT on imp006 breakout
bt = hardware.bluetooth.open();
} catch (err) {
server.error(err);
return;
}
// Set scan parameters for passive scanning
bt.setscanparams(false, 100, 100);
// Filter out advertisements whose 'data' contains the iBeacon preamble bytes
// and whose type is ADV_NONCONN_IND
bt.setscanfilter([{ "type" : 3, "data" : "\x02\x01\x06\x1A\xFF\x4C\x00\x02\x15" }]);
server.log("Scanning...");
bt.startscan(function(adverts) {
foreach (advert in adverts) {
// Convert payload to hex string
local payload = "";
for (local i = 0 ; i < advert.data.len() ; i++) {
payload += format("%02x", advert.data[i]);
}
// This is a beacon so record it
local beacon = {};
beacon.uuid <- payload.slice(18, 50);
beacon.majorString <- payload.slice(50, 54);
beacon.minorString <- payload.slice(54, 58);
if (beacons.len() > 0) {
local got = false;
foreach (aBeacon in beacons) {
if (aBeacon.uuid == beacon.uuid &&
aBeacon.majorValue == beacon.majorValue &&
aBeacon.minorValue == beacon.minorValue) {
got = true;
break;
}
}
if (!got) addBeacon(beacon);
} else {
addBeacon(beacon);
}
}
});
Active iBeacon Scanning
The following code updates the code shown above to support active scanning for beacon scan response packets. See bluetooth.startadvertise() for code creating and issuing the scan response.
bt <- null;
// Beacons
beacons <- [];
appBeaconUUID <- "9277830ab2eb490fa1dd7fe38c492ede";
function makePrettyUUID(uuid) {
return uuid.slice(0,9) + "-" + uuid.slice(8,12) + "-" + uuid.slice(12,16) + "-" + uuid.slice(16);
}
function reportBeacon(beacon) {
server.log("Beacon added: " + makePrettyUUID(beacon.uuid) + " (" + beacon.majorString + "/" + beacon.minorString + ")");
}
function addBeacon(beacon) {
// Check for OUR beacons -- add extra fields for them
if (beacon.uuid == appBeaconUUID) {
beacon.appID <- 0x00FF;
beacon.devID <- "";
}
beacons.append(beacon);
reportBeacon(beacon);
}
try {
// Instantiate BT on imp006 breakout
bt = hardware.bluetooth.open();
} catch (err) {
server.error(err);
return;
}
// Set scan parameters for active scanning (required for scan response detection)
bt.setscanparams(true, 100, 100);
// Filter out advertisements whose 'data' contains the iBeacon preamble bytes
bt.setscanfilter([{ "data" : "\x02\x01\x06" }]);
server.log("Scanning...");
bt.startscan(function(adverts) {
foreach (advert in adverts) {
if (advert.type == 4) {
local appID = (advert.data[6] << 8) + advert.data[5];
local got = false;
foreach (aBeacon in beacons) {
if ("appID" in aBeacon) {
if (aBeacon.appID == appID && aBeacon.devID.len() == 0) {
aBeacon.devID = advert.data.tostring().slice(9);
server.log("Response received (device ID: " + aBeacon.devID + ")");
break;
}
}
}
return;
}
// Convert payload to hex string
local payload = "";
for (local i = 0 ; i < advert.data.len() ; i++) {
payload += format("%02x", advert.data[i]);
}
// This is a beacon so record it
local beacon = {};
beacon.uuid <- payload.slice(18, 50);
beacon.majorString <- payload.slice(50, 54);
beacon.minorString <- payload.slice(54, 58);
if (beacons.len() > 0) {
local got = false;
foreach (aBeacon in beacons) {
if (aBeacon.uuid == beacon.uuid &&
aBeacon.majorValue == beacon.majorValue &&
aBeacon.minorValue == beacon.minorValue) {
got = true;
break;
}
}
if (!got) addBeacon(beacon);
} else {
addBeacon(beacon);
}
}
});