From 3fc7888aab8280ff48aaf8c1c5c24411ded21450 Mon Sep 17 00:00:00 2001 From: "Matthias L. Jugel" Date: Tue, 19 Sep 2017 23:54:46 +0200 Subject: [PATCH] update bluetooth for eddystone --- .../_locales/bluetooth-jsdoc-strings.json | 18 +++++ .../bluetooth/_locales/bluetooth-strings.json | 5 ++ libs/bluetooth/bluetooth.cpp | 69 ++++++++++++++++++- libs/bluetooth/bluetooth.ts | 19 ++++- libs/bluetooth/pxt.json | 66 +++++++++++++++++- libs/bluetooth/shims.d.ts | 46 ++++++++++++- sim/state/misc.ts | 13 +++- 7 files changed, 228 insertions(+), 8 deletions(-) diff --git a/libs/bluetooth/_locales/bluetooth-jsdoc-strings.json b/libs/bluetooth/_locales/bluetooth-jsdoc-strings.json index 91ab16da..861244b8 100644 --- a/libs/bluetooth/_locales/bluetooth-jsdoc-strings.json +++ b/libs/bluetooth/_locales/bluetooth-jsdoc-strings.json @@ -1,9 +1,26 @@ { "bluetooth": "Support for additional Bluetooth services.\n\nSupport for additional Bluetooth services.", + "bluetooth.advertiseUid": "Advertise an Eddystone UID", + "bluetooth.advertiseUidBuffer": "Advertise an Eddystone UID", + "bluetooth.advertiseUidBuffer|param|connectable": "true to keep bluetooth connectable for other services, false otherwise.", + "bluetooth.advertiseUidBuffer|param|nsAndInstance": "16 bytes buffer of namespace (bytes 0-9) and instance (bytes 10-15)", + "bluetooth.advertiseUidBuffer|param|power": "power level between 0 and 7, eg: 7", + "bluetooth.advertiseUid|param|connectable": "true to keep bluetooth connectable for other services, false otherwise.", + "bluetooth.advertiseUid|param|instance": "4 last bytes of the instance uid", + "bluetooth.advertiseUid|param|ns": "4 last bytes of the namespace uid", + "bluetooth.advertiseUid|param|power": "power level between 0 and 7, eg: 7", + "bluetooth.advertiseUrl": "Advertise an Eddystone URL", + "bluetooth.advertiseUrl|param|connectable": "true to keep bluetooth connectable for other services, false otherwise.", + "bluetooth.advertiseUrl|param|power": "power level between 0 and 7, eg: 7", + "bluetooth.advertiseUrl|param|url": "the url to transmit. Must be no longer than the supported eddystone url length, eg: \"https://makecode.com\"", "bluetooth.onBluetoothConnected": "Register code to run when the micro:bit is connected to over Bluetooth", "bluetooth.onBluetoothConnected|param|body": "Code to run when a Bluetooth connection is established", "bluetooth.onBluetoothDisconnected": "Register code to run when a bluetooth connection to the micro:bit is lost", "bluetooth.onBluetoothDisconnected|param|body": "Code to run when a Bluetooth connection is lost", + "bluetooth.onUartDataReceived": "Registers an event to be fired when one of the delimiter is matched.", + "bluetooth.onUartDataReceived|param|delimiters": "the characters to match received characters against.", + "bluetooth.setTransmitPower": "Sets the bluetooth transmit power between 0 (minimal) and 7 (maximum).", + "bluetooth.setTransmitPower|param|power": "power level between 0 (minimal) and 7 (maximum), eg: 7.", "bluetooth.startAccelerometerService": "Starts the Bluetooth accelerometer service", "bluetooth.startButtonService": "Starts the Bluetooth button service", "bluetooth.startIOPinService": "Starts the Bluetooth IO pin service.", @@ -11,6 +28,7 @@ "bluetooth.startMagnetometerService": "Starts the Bluetooth magnetometer service", "bluetooth.startTemperatureService": "Starts the Bluetooth temperature service", "bluetooth.startUartService": "Starts the Bluetooth UART service", + "bluetooth.stopAdvertising": "Stops advertising Eddystone end points", "bluetooth.uartReadUntil": "Reads from the Bluetooth UART service buffer, returning its contents when the specified delimiter character is encountered.", "bluetooth.uartWriteNumber": "Prints a numeric value to the serial", "bluetooth.uartWriteString": "Writes to the Bluetooth UART service buffer. From there the data is transmitted over Bluetooth to a connected device.", diff --git a/libs/bluetooth/_locales/bluetooth-strings.json b/libs/bluetooth/_locales/bluetooth-strings.json index ad894e0c..bf68a2dc 100644 --- a/libs/bluetooth/_locales/bluetooth-strings.json +++ b/libs/bluetooth/_locales/bluetooth-strings.json @@ -1,6 +1,10 @@ { + "bluetooth.advertiseUid|block": "bluetooth advertise UID|namespace (bytes 6-9)%ns|instance (bytes 2-6)%instance|with power %power|connectable %connectable", + "bluetooth.advertiseUrl|block": "bluetooth advertise url %url|with power %power|connectable %connectable", "bluetooth.onBluetoothConnected|block": "on bluetooth connected", "bluetooth.onBluetoothDisconnected|block": "on bluetooth disconnected", + "bluetooth.onUartDataReceived|block": "bluetooth|on data received %delimiters=serial_delimiter_conv", + "bluetooth.setTransmitPower|block": "bluetooth set transmit power %power", "bluetooth.startAccelerometerService|block": "bluetooth accelerometer service", "bluetooth.startButtonService|block": "bluetooth button service", "bluetooth.startIOPinService|block": "bluetooth io pin service", @@ -8,6 +12,7 @@ "bluetooth.startMagnetometerService|block": "bluetooth magnetometer service", "bluetooth.startTemperatureService|block": "bluetooth temperature service", "bluetooth.startUartService|block": "bluetooth uart service", + "bluetooth.stopAdvertising|block": "bluetooth stop advertising", "bluetooth.uartReadUntil|block": "bluetooth uart|read until %del=serial_delimiter_conv", "bluetooth.uartWriteNumber|block": "bluetooth uart|write number %value", "bluetooth.uartWriteString|block": "bluetooth uart|write string %data", diff --git a/libs/bluetooth/bluetooth.cpp b/libs/bluetooth/bluetooth.cpp index 007f5518..4cafcaed 100644 --- a/libs/bluetooth/bluetooth.cpp +++ b/libs/bluetooth/bluetooth.cpp @@ -7,7 +7,7 @@ using namespace pxt; /** * Support for additional Bluetooth services. */ -//% color=#0082FB weight=20 +//% color=#0082FB weight=96 icon="\uf294" namespace bluetooth { MicroBitUARTService *uart = NULL; @@ -99,6 +99,18 @@ namespace bluetooth { return uart->readUntil(ManagedString(del)).leakData(); } + /** + * Registers an event to be fired when one of the delimiter is matched. + * @param delimiters the characters to match received characters against. + */ + //% help=bluetooth/on-uart-data-received + //% weight=18 blockId=bluetooth_on_data_received block="bluetooth|on data received %delimiters=serial_delimiter_conv" + void onUartDataReceived(StringData* delimiters, Action body) { + startUartService(); + uart->eventOn(ManagedString(delimiters)); + registerWithDal(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH, body); + } + /** * Register code to run when the micro:bit is connected to over Bluetooth * @param body Code to run when a Bluetooth connection is established @@ -120,4 +132,59 @@ namespace bluetooth { void onBluetoothDisconnected(Action body) { registerWithDal(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_DISCONNECTED, body); } + + const int8_t CALIBRATED_POWERS[] = {-49, -37, -33, -28, -25, -20, -15, -10}; + /** + * Advertise an Eddystone URL + * @param url the url to transmit. Must be no longer than the supported eddystone url length, eg: "https://makecode.com" + * @param power power level between 0 and 7, eg: 7 + * @param connectable true to keep bluetooth connectable for other services, false otherwise. + */ + //% blockId=eddystone_advertise_url block="bluetooth advertise url %url|with power %power|connectable %connectable" + //% parts=bluetooth weight=11 blockGap=8 + //% help=bluetooth/advertise-url blockExternalInputs=1 + void advertiseUrl(StringData* url, int power, bool connectable) { + power = min(MICROBIT_BLE_POWER_LEVELS-1, max(0, power)); + int8_t level = CALIBRATED_POWERS[power]; + uBit.bleManager.advertiseEddystoneUrl(ManagedString(url), level, connectable); + uBit.bleManager.setTransmitPower(power); + } + + /** + * Advertise an Eddystone UID + * @param nsAndInstance 16 bytes buffer of namespace (bytes 0-9) and instance (bytes 10-15) + * @param power power level between 0 and 7, eg: 7 + * @param connectable true to keep bluetooth connectable for other services, false otherwise. + */ + //% parts=bluetooth weight=12 advanced=true + void advertiseUidBuffer(Buffer nsAndInstance, int power, bool connectable) { + ManagedBuffer buf(nsAndInstance); + if (buf.length() != 16) return; + + power = min(MICROBIT_BLE_POWER_LEVELS-1, max(0, power)); + int8_t level = CALIBRATED_POWERS[power]; + uint8_t uidNs[10]; buf.readBytes(uidNs, 0, 10); + uint8_t uidInst[6]; buf.readBytes(uidInst, 10, 6); + uBit.bleManager.advertiseEddystoneUid((const char*)uidNs, (const char*)uidInst, level, connectable); + } + + /** + * Sets the bluetooth transmit power between 0 (minimal) and 7 (maximum). + * @param power power level between 0 (minimal) and 7 (maximum), eg: 7. + */ + //% parts=bluetooth weight=5 help=bluetooth/set-transmit-power advanced=true + //% blockId=bluetooth_settransmitpower block="bluetooth set transmit power %power" + void setTransmitPower(int power) { + uBit.bleManager.setTransmitPower(min(MICROBIT_BLE_POWER_LEVELS-1, max(0, power))); + } + + /** + * Stops advertising Eddystone end points + */ + //% blockId=eddystone_stop_advertising block="bluetooth stop advertising" + //% parts=bluetooth weight=10 + //% help=bluetooth/stop-advertising advanced=true + void stopAdvertising() { + uBit.bleManager.stopAdvertising(); + } } \ No newline at end of file diff --git a/libs/bluetooth/bluetooth.ts b/libs/bluetooth/bluetooth.ts index 9ea72eed..343f24ba 100644 --- a/libs/bluetooth/bluetooth.ts +++ b/libs/bluetooth/bluetooth.ts @@ -1,7 +1,7 @@ /** * Support for additional Bluetooth services. */ -//% color=#0082FB weight=20 icon="\uf294" +//% color=#0082FB weight=96 icon="\uf294" namespace bluetooth { /** * Writes to the Bluetooth UART service buffer. From there the data is transmitted over Bluetooth to a connected device. @@ -46,4 +46,21 @@ namespace bluetooth { // dummy implementation for simulator return "???" } + + /** + * Advertise an Eddystone UID + * @param ns 4 last bytes of the namespace uid + * @param instance 4 last bytes of the instance uid + * @param power power level between 0 and 7, eg: 7 + * @param connectable true to keep bluetooth connectable for other services, false otherwise. + */ + //% blockId=eddystone_advertise_uid block="bluetooth advertise UID|namespace (bytes 6-9)%ns|instance (bytes 2-6)%instance|with power %power|connectable %connectable" + //% parts=bluetooth weight=12 blockGap=8 + //% help=bluetooth/advertise-uid blockExternalInputs=1 + export function advertiseUid(ns: number, instance: number, power: number, connectable: boolean) { + const buf = pins.createBuffer(16); + buf.setNumber(NumberFormat.Int32BE, 6, ns); + buf.setNumber(NumberFormat.Int32BE, 12, instance); + bluetooth.advertiseUidBuffer(buf, power, connectable); + } } diff --git a/libs/bluetooth/pxt.json b/libs/bluetooth/pxt.json index 75608351..f22e45b1 100644 --- a/libs/bluetooth/pxt.json +++ b/libs/bluetooth/pxt.json @@ -25,12 +25,74 @@ "advertising_timeout": 0, "tx_power": 6, "dfu_service": 1, + "event_service": 0, + "device_info_service": 1, + "security_level": "SECURITY_MODE_ENCRYPTION_NO_MITM", + "eddystone_url": 1, + "eddystone_uid": 1 + } + } + }, + "optionalConfig": { + "microbit-dal": { + "bluetooth": { + "private_addressing": 0, + "advertising_timeout": 0, + "tx_power": 6, + "dfu_service": 1, "event_service": 1, - "device_info_service": 1 + "device_info_service": 1, + "eddystone_url": 1, + "eddystone_uid": 1, + "open": 0, + "pairing_mode": 1, + "whitelist": 1, + "security_level": "SECURITY_MODE_ENCRYPTION_NO_MITM" }, "gatt_table_size": "0x700" } - } + }, + "userConfigs": [ + { + "description": "Unsecure: Anyone can connect via Bluetooth.", + "config": { + "microbit-dal": { + "bluetooth": { + "open": 1, + "pairing_mode": 0, + "whitelist": 0, + "security_level": null + } + } + } + }, + { + "description": "JustWorks pairing (default): Button press to pair.", + "config": { + "microbit-dal": { + "bluetooth": { + "open": null, + "pairing_mode": null, + "whitelist": null, + "security_level": null + } + } + } + }, + { + "description": "Passkey pairing: Button press and 6 digit key to pair.", + "config": { + "microbit-dal": { + "bluetooth": { + "open": 0, + "pairing_mode": 1, + "whitelist": 1, + "security_level": "SECURITY_MODE_ENCRYPTION_WITH_MITM" + } + } + } + } + ] }, "installedVersion": "vzlhfd" } \ No newline at end of file diff --git a/libs/bluetooth/shims.d.ts b/libs/bluetooth/shims.d.ts index 58c52fa9..5e35e3b7 100644 --- a/libs/bluetooth/shims.d.ts +++ b/libs/bluetooth/shims.d.ts @@ -4,7 +4,7 @@ /** * Support for additional Bluetooth services. */ - //% color=#0082FB weight=20 + //% color=#0082FB weight=96 icon="\uf294" declare namespace bluetooth { /** @@ -63,6 +63,14 @@ declare namespace bluetooth { //% parts="bluetooth" advanced=true shim=bluetooth::startUartService function startUartService(): void; + /** + * Registers an event to be fired when one of the delimiter is matched. + * @param delimiters the characters to match received characters against. + */ + //% help=bluetooth/on-uart-data-received + //% weight=18 blockId=bluetooth_on_data_received block="bluetooth|on data received %delimiters=serial_delimiter_conv" shim=bluetooth::onUartDataReceived + function onUartDataReceived(delimiters: string, body: () => void): void; + /** * Register code to run when the micro:bit is connected to over Bluetooth * @param body Code to run when a Bluetooth connection is established @@ -80,6 +88,42 @@ declare namespace bluetooth { //% blockId=bluetooth_on_disconnected block="on bluetooth disconnected" //% parts="bluetooth" shim=bluetooth::onBluetoothDisconnected function onBluetoothDisconnected(body: () => void): void; + + /** + * Advertise an Eddystone URL + * @param url the url to transmit. Must be no longer than the supported eddystone url length, eg: "https://makecode.com" + * @param power power level between 0 and 7, eg: 7 + * @param connectable true to keep bluetooth connectable for other services, false otherwise. + */ + //% blockId=eddystone_advertise_url block="bluetooth advertise url %url|with power %power|connectable %connectable" + //% parts=bluetooth weight=11 blockGap=8 + //% help=bluetooth/advertise-url blockExternalInputs=1 shim=bluetooth::advertiseUrl + function advertiseUrl(url: string, power: number, connectable: boolean): void; + + /** + * Advertise an Eddystone UID + * @param nsAndInstance 16 bytes buffer of namespace (bytes 0-9) and instance (bytes 10-15) + * @param power power level between 0 and 7, eg: 7 + * @param connectable true to keep bluetooth connectable for other services, false otherwise. + */ + //% parts=bluetooth weight=12 advanced=true shim=bluetooth::advertiseUidBuffer + function advertiseUidBuffer(nsAndInstance: Buffer, power: number, connectable: boolean): void; + + /** + * Sets the bluetooth transmit power between 0 (minimal) and 7 (maximum). + * @param power power level between 0 (minimal) and 7 (maximum), eg: 7. + */ + //% parts=bluetooth weight=5 help=bluetooth/set-transmit-power advanced=true + //% blockId=bluetooth_settransmitpower block="bluetooth set transmit power %power" shim=bluetooth::setTransmitPower + function setTransmitPower(power: number): void; + + /** + * Stops advertising Eddystone end points + */ + //% blockId=eddystone_stop_advertising block="bluetooth stop advertising" + //% parts=bluetooth weight=10 + //% help=bluetooth/stop-advertising advanced=true shim=bluetooth::stopAdvertising + function stopAdvertising(): void; } // Auto-generated. Do not edit. Really. diff --git a/sim/state/misc.ts b/sim/state/misc.ts index 5130801b..c09bdfd3 100644 --- a/sim/state/misc.ts +++ b/sim/state/misc.ts @@ -169,11 +169,14 @@ namespace pxsim.bluetooth { // TODO } export function uartWrite(s : string): void { - // TODO + serial.writeString(s) } export function uartReadUntil(del: string): string { - // TODO - return "" + return serial.readUntil(del); + } + export function onDataReceived(delimiters: string, handler: RefAction) { + let b = board(); + b.bus.listen(DAL.MICROBIT_ID_BLE_UART, DAL.MICROBIT_UART_S_EVT_DELIM_MATCH, handler); } export function onBluetoothConnected(a : RefAction) { // TODO @@ -181,5 +184,9 @@ namespace pxsim.bluetooth { export function onBluetoothDisconnected(a : RefAction) { // TODO } + export function advertiseUrl(url: string, power: number, connectable: boolean) { } + export function advertiseUidBuffer(nsAndInstance: Buffer, power: number, connectable: boolean) { } + export function stopAdvertising() { } + export function setTransmitPower(power: number) {} }