From 83b3eabf1d64273159bbc8e694a5c9cfd2534e08 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Thu, 16 Aug 2018 15:38:03 -0400 Subject: [PATCH] Regulate micro:bit send timing (#1471) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * display blocks yield for 50ms * WIP on microbit command send timing * WIP on microbit command send timing * Cleanup and JSDOC * Send “withResponse” param to Link * Remove caching, add busy timeout --- src/extensions/scratch3_microbit/index.js | 66 ++++++++++++++++++++--- src/io/bleSession.js | 6 ++- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/extensions/scratch3_microbit/index.js b/src/extensions/scratch3_microbit/index.js index bf0a0931..6659e741 100644 --- a/src/extensions/scratch3_microbit/index.js +++ b/src/extensions/scratch3_microbit/index.js @@ -27,6 +27,12 @@ const BLECommand = { const BLETimeout = 4500; // TODO: might need tweaking based on how long the device takes to start sending data +/** + * A time interval to wait (in milliseconds) while a block that sends a BLE message is running. + * @type {number} + */ +const BLESendInterval = 100; + /** * Enum for micro:bit protocol. * https://github.com/LLK/scratch-microbit-firmware/blob/master/protocol.md @@ -108,6 +114,19 @@ class MicroBit { * @private */ this._timeoutID = null; + + /** + * A flag that is true while we are busy sending data to the BLE session. + * @type {boolean} + * @private + */ + this._busy = false; + + /** + * ID for a timeout which is used to clear the busy flag if it has been + * true for a long time. + */ + this._busyTimeoutID = null; } // TODO: keep here? @@ -252,21 +271,40 @@ class MicroBit { } /** - * Write a message to the device BLE session. + * Send a message to the device BLE session. * @param {number} command - the BLE command hex. - * @param {Uint8Array} message - the message to write. - * @return {Promise} - a Promise that resolves when writing to device. + * @param {Uint8Array} message - the message to write * @private */ _writeSessionData (command, message) { if (!this.getPeripheralIsConnected()) return; + if (this._busy) return; + + // Set a busy flag so that while we are sending a message and waiting for + // the response, additional messages are ignored. + this._busy = true; + + // Set a timeout after which to reset the busy flag. This is used in case + // a BLE message was sent for which we never received a response, because + // e.g. the device was turned off after the message was sent. We reset + // the busy flag after a while so that it is possible to try again later. + this._busyTimeoutID = window.setTimeout(() => { + this._busy = false; + }, 5000); + const output = new Uint8Array(message.length + 1); output[0] = command; // attach command to beginning of message for (let i = 0; i < message.length; i++) { output[i + 1] = message[i]; } const data = Base64Util.uint8ArrayToBase64(output); - return this._ble.write(BLEUUID.service, BLEUUID.txChar, data, 'base64'); + + this._ble.write(BLEUUID.service, BLEUUID.txChar, data, 'base64', true).then( + () => { + this._busy = false; + window.clearTimeout(this._busyTimeoutID); + } + ); } } @@ -750,7 +788,11 @@ class Scratch3MicroBitBlocks { this._device.displayMatrix(this._device.ledMatrixState); } - return Promise.resolve(); + return new Promise(resolve => { + setTimeout(() => { + resolve(); + }, BLESendInterval); + }); } /** @@ -762,7 +804,12 @@ class Scratch3MicroBitBlocks { displayText (args) { const text = String(args.TEXT).substring(0, 19); if (text.length > 0) this._device.displayText(text); - return Promise.resolve(); + + return new Promise(resolve => { + setTimeout(() => { + resolve(); + }, BLESendInterval); + }); } /** @@ -774,7 +821,12 @@ class Scratch3MicroBitBlocks { this._device.ledMatrixState[i] = 0; } this._device.displayMatrix(this._device.ledMatrixState); - return Promise.resolve(); + + return new Promise(resolve => { + setTimeout(() => { + resolve(); + }, BLESendInterval); + }); } /** diff --git a/src/io/bleSession.js b/src/io/bleSession.js index 95c88793..b543fdce 100644 --- a/src/io/bleSession.js +++ b/src/io/bleSession.js @@ -133,13 +133,17 @@ class BLESession extends JSONRPCWebSocket { * @param {number} characteristicId - the ble characteristic to write. * @param {string} message - the message to send. * @param {string} encoding - the message encoding type. + * @param {boolean} withResponse - if true, resolve after peripheral's response. * @return {Promise} - a promise from the remote send request. */ - write (serviceId, characteristicId, message, encoding = null) { + write (serviceId, characteristicId, message, encoding = null, withResponse = null) { const params = {serviceId, characteristicId, message}; if (encoding) { params.encoding = encoding; } + if (withResponse) { + params.withResponse = withResponse; + } return this.sendRemoteRequest('write', params) .catch(e => { this._sendError(e);