espruino
Version:
Command Line Interface and library for Communications with Espruino JavaScript Microcontrollers
234 lines (191 loc) • 6.88 kB
JavaScript
(function () {
/*
In order to allow a connection with the DBus daemon, you have to set up right permissions.
Create the file `/etc/dbus-1/system.d/node-ble.conf` with the following content (customize with userid)
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="%userid%">
<allow own="org.bluez"/>
<allow send_destination="org.bluez"/>
<allow send_interface="org.bluez.GattCharacteristic1"/>
<allow send_interface="org.bluez.GattDescriptor1"/>
<allow send_interface="org.freedesktop.DBus.ObjectManager"/>
<allow send_interface="org.freedesktop.DBus.Properties"/>
</policy>
</busconfig>
*/
if (typeof process === 'undefined') return;
if (require("os").platform() != "linux") {
console.log("serial_node_ble: Not running Linux - disabling Bluetooth DBUS support");
return;
}
const NORDIC_SERVICE = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
const NORDIC_TX = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
const NORDIC_RX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
const NORDIC_TX_MAX_LENGTH = 20;
const SCAN_STOP_TIMEOUT = 5000;
var adapter = undefined;
var errored = false;
var bluetooth;
var btDevice;
var btUARTService;
var txCharacteristic;
var rxCharacteristic;
var txInProgress = false;
var scanStopTimeout = undefined;
function init() {
Espruino.Core.Config.add("BLUETOOTH_LOW_ENERGY_DBUS", {
section: "Communications",
name: "Connect over Bluetooth Smart (BTLE) via 'node-ble'",
descriptionHTML: 'Allow connection to Espruino via BLE with the Nordic UART implementation',
type: "boolean",
defaultValue: true
});
}
async function getAdapter() {
if (adapter)
return adapter;
try {
var {createBluetooth} = require('node-ble');
var BT = createBluetooth();
bluetooth = BT.bluetooth;
process.on('exit', () => BT.destroy());
} catch (e) {
console.log("serial_node_ble: module couldn't be loaded, no node.js Bluetooth Low Energy\n", e);
errored = true;
}
adapter = await bluetooth.defaultAdapter();
if (Espruino.Config.WEB_BLUETOOTH || Espruino.Config.BLUETOOTH_LOW_ENERGY) {
// Everything has already initialised, so we must disable
// web bluetooth this way instead
console.log("serial_node_ble: Disable other bluetooth plugins as we have Node-ble instead");
Espruino.Config.WEB_BLUETOOTH = false;
Espruino.Config.BLUETOOTH_LOW_ENERGY = false;
}
return adapter;
}
async function scanDevices(adapter) {
if (scanStopTimeout === undefined) {
console.log("serial_node_ble: Start Scanning...");
await adapter.startDiscovery();
} else clearTimeout(scanStopTimeout);
scanStopTimeout = setTimeout(function() {
console.log("serial_node_ble: Stop Scanning...");
scanStopTimeout = undefined;
adapter.stopDiscovery();
}, SCAN_STOP_TIMEOUT);
devices = await adapter.devices();
reportedDevices = [];
await devices.reduce((p,address) => p.then(() =>
adapter.getDevice(address)).then((d) => {
newDevice = { path: address, type: "bluetooth" };
return d.helper.prop('UUIDs').then((uuids) => {
d.getAlias().catch(() => '').then((n) => {
newDevice['description'] = n;
if (uuids.includes(NORDIC_SERVICE) || Espruino.Core.Utils.isRecognisedBluetoothDevice(n))
reportedDevices.push(newDevice);
});
});
}), Promise.resolve());
return reportedDevices;
}
var getPorts = function (callback) {
console.log("Getting ports");
if (errored || !Espruino.Config.BLUETOOTH_LOW_ENERGY_DBUS) {
console.log("serial_node_ble: getPorts - disabled");
callback([], true);
return;
}
getAdapter().then((adapter) =>
scanDevices(adapter)).then((devices) =>
callback(devices, false)
).catch((err) => {
console.error("serial_node_ble", "error on scanDevices", err)
callback([], true)
})
};
var closeSerial = function () {
if (btDevice) {
btDevice.disconnect();
}
};
var openSerial = function (serialPort, openCallback, receiveCallback, disconnectCallback) {
txInProgress = false;
console.log("BT> Connecting");
adapter.getDevice(serialPort).then((d) => {
btDevice = d;
return btDevice.connect();
}).catch(() => {
console.error("BT> ERROR Connecting");
btDevice = undefined;
return openCallback();
}).then(() => {
btDevice.on('disconnect', function() {
txCharacteristic = undefined;
rxCharacteristic = undefined;
btUARTService = undefined;
btDevice = undefined;
txInProgress = false;
disconnectCallback();
});
return btDevice.gatt();
}).then((g) =>
g.getPrimaryService(NORDIC_SERVICE)
).then((sv) => {
btUARTService = sv;
return btUARTService.getCharacteristic(NORDIC_TX);
}).then((tx) => {
txCharacteristic = tx;
return btUARTService.getCharacteristic(NORDIC_RX);
}).then((rx) => {
rxCharacteristic = rx;
console.log("BT> Connected");
rxCharacteristic.on('valuechanged', (data) =>
receiveCallback(new Uint8Array(data).buffer)
);
return rxCharacteristic.isNotifying().then((n) => {
if (n)
console.error("Another process is connected to this device, problems may occur.");
}).then(() => rxCharacteristic.startNotifications()).then(() =>
openCallback(true)
);
}).catch((e) => {
console.error("BT> ERROR getting services/characteristics");
console.error(e,e.stack);
closeSerial();
return openCallback();
});
};
/** Throttled serial write */
var writeSerial = function (data, callback) {
if (txCharacteristic === undefined) return;
if (data.length>NORDIC_TX_MAX_LENGTH) {
console.error("BT> TX length >"+NORDIC_TX_MAX_LENGTH);
return callback();
}
if (txInProgress) {
console.error("BT> already sending!");
return callback();
}
console.log("BT> send "+JSON.stringify(data));
txInProgress = true;
txCharacteristic.writeValue(Espruino.Core.Utils.stringToBuffer(data), {type:'command'}).then(() => {
txInProgress = false;
return callback();
}).catch((e) => {
console.error("BT> SEND ERROR " + e);
closeSerial();
});
};
// ----------------------------------------------------------
Espruino.Core.Serial.devices.push({
"name" : "Node-ble Bluetooth LE",
"init": init,
"getPorts": getPorts,
"open": openSerial,
"write": writeSerial,
"close": closeSerial,
"maxWriteLength" : NORDIC_TX_MAX_LENGTH,
});
})();