From 035017617e8afe2150022108490cfd1edcd7baa4 Mon Sep 17 00:00:00 2001 From: Nathan Kellenicki Date: Thu, 19 Dec 2019 15:44:35 -0800 Subject: [PATCH] Bug fixes, hubs now wait for hub properties before being connected --- examples/multiple_motors.js | 37 +++++++++++++++++ src/devices/hubled.ts | 2 +- src/hubs/basehub.ts | 28 +++++++++---- src/hubs/lpf2hub.ts | 82 +++++++++++++++---------------------- 4 files changed, 93 insertions(+), 56 deletions(-) create mode 100644 examples/multiple_motors.js diff --git a/examples/multiple_motors.js b/examples/multiple_motors.js new file mode 100644 index 0000000..a466273 --- /dev/null +++ b/examples/multiple_motors.js @@ -0,0 +1,37 @@ +/* + * + * This demonstrates connecting multiple hubs to your laptop. Once connected, all the hubs LED lights will cycle through the same colors simultaneously. + * + */ + +const PoweredUP = require(".."); + +const poweredUP = new PoweredUP.PoweredUP(); +poweredUP.scan(); // Start scanning for hubs + +console.log("Looking for Hubs..."); + +poweredUP.on("discover", async (hub) => { // Wait to discover hubs + + await hub.connect(); // Connect to hub + console.log(`Connected to ${hub.name}!`); + + const motorA = await hub.waitForDeviceAtPort("A"); + console.log("Got motor A"); + const motorB = await hub.waitForDeviceAtPort("B"); + console.log("Got motor B"); + const motorC = await hub.waitForDeviceAtPort("C"); + console.log("Got motor C"); + const motorD = await hub.waitForDeviceAtPort("D"); + console.log("Got motor D"); + + motorA.setPower(30); + motorB.setPower(30); + motorC.setPower(30); + motorD.setPower(30); + + hub.on("disconnect", () => { + console.log("Hub disconnected"); + }); + +}); \ No newline at end of file diff --git a/src/devices/hubled.ts b/src/devices/hubled.ts index ed33903..6aee4e3 100644 --- a/src/devices/hubled.ts +++ b/src/devices/hubled.ts @@ -41,7 +41,7 @@ export class HubLED extends Device { public setRGB (red: number, green: number, blue: number) { return new Promise((resolve, reject) => { this.subscribe(HubLED.Mode.RGB); - this.writeDirect(0x00, Buffer.from([red, green, blue])); + this.writeDirect(0x01, Buffer.from([red, green, blue])); return resolve(); }); } diff --git a/src/hubs/basehub.ts b/src/hubs/basehub.ts index 8c8d08d..dfa7895 100644 --- a/src/hubs/basehub.ts +++ b/src/hubs/basehub.ts @@ -54,10 +54,11 @@ export class BaseHub extends EventEmitter { private _type: Consts.HubType; private _portMap: {[portName: string]: number} = {}; - private _attachCallbacks: ((device: Device) => void)[] = []; + private _attachCallbacks: ((device: Device) => boolean)[] = []; constructor (device: IBLEAbstraction, portMap: {[portName: string]: number} = {}, type: Consts.HubType = Consts.HubType.UNKNOWN) { super(); + this.setMaxListeners(20); // Technic Medium Hub has 9 built in devices + 4 external ports. Node.js throws a warning after 11 attached event listeners. this._type = type; this._bleDevice = device; this._portMap = portMap; @@ -183,7 +184,7 @@ export class BaseHub extends EventEmitter { public getDeviceAtPort (portName: string) { const portId = this._portMap[portName]; - if (portId) { + if (portId !== undefined) { return this._attachedDevices[portId]; } else { throw new Error(`Port ${portName} does not exist on this hub type`); @@ -199,7 +200,10 @@ export class BaseHub extends EventEmitter { } this._attachCallbacks.push((device) => { if (device.portName === portName) { - return resolve(device); + resolve(device); + return true; + } else { + return false; } }); }); @@ -224,7 +228,10 @@ export class BaseHub extends EventEmitter { } this._attachCallbacks.push((device) => { if (device.type === deviceType) { - return resolve(device); + resolve(device); + return true; + } else { + return false; } }) }); @@ -289,9 +296,15 @@ export class BaseHub extends EventEmitter { * @param {Device} device */ this.emit("attach", device); - this._attachCallbacks.forEach((callback) => { - callback(device); - }); + debug(`Attached device type ${device.type} (${Consts.DeviceTypeNames[device.type]}) on port ${device.portName} (${device.portId})`); + + let i = this._attachCallbacks.length; + while (i--) { + const callback = this._attachCallbacks[i]; + if (callback(device)) { + this._attachCallbacks.splice(i, 1); + } + } } @@ -303,6 +316,7 @@ export class BaseHub extends EventEmitter { * @param {Device} device */ this.emit("detach", device); + debug(`Detached device type ${device.type} (${Consts.DeviceTypeNames[device.type]}) on port ${device.portName} (${device.portId})`); } diff --git a/src/hubs/lpf2hub.ts b/src/hubs/lpf2hub.ts index 0ece694..703f22e 100644 --- a/src/hubs/lpf2hub.ts +++ b/src/hubs/lpf2hub.ts @@ -17,20 +17,24 @@ export class LPF2Hub extends BaseHub { private _messageBuffer: Buffer = Buffer.alloc(0); + private _propertyRequestCallbacks: {[property: number]: ((data: Buffer) => void)} = {}; + public connect () { return new Promise(async (resolve, reject) => { + debug("LPF2Hub connecting"); await super.connect(); await this._bleDevice.discoverCharacteristicsForService(Consts.BLEService.LPF2_HUB); this._bleDevice.subscribeToCharacteristic(Consts.BLECharacteristic.LPF2_ALL, this._parseMessage.bind(this)); - await this.sleep(100); - this.send(Buffer.from([0x01, 0x02, 0x02]), Consts.BLECharacteristic.LPF2_ALL); // Activate button reports - this.send(Buffer.from([0x01, 0x03, 0x05]), Consts.BLECharacteristic.LPF2_ALL); // Request firmware version - this.send(Buffer.from([0x01, 0x04, 0x05]), Consts.BLECharacteristic.LPF2_ALL); // Request hardware version - this.send(Buffer.from([0x01, 0x05, 0x02]), Consts.BLECharacteristic.LPF2_ALL); // Activate RSSI updates - this.send(Buffer.from([0x01, 0x06, 0x02]), Consts.BLECharacteristic.LPF2_ALL); // Activate battery level reports - this.send(Buffer.from([0x01, 0x0d, 0x05]), Consts.BLECharacteristic.LPF2_ALL); // Request primary MAC address + await this.sleep(500); + this._requestHubPropertyReports(0x02); // Activate button reports + await this._requestHubPropertyValue(0x03); // Request firmware version + await this._requestHubPropertyValue(0x04); // Request hardware version + this._requestHubPropertyReports(0x05); // Activate RSSI updates + this._requestHubPropertyReports(0x06); // Activate battery level reports + await this._requestHubPropertyValue(0x0d); // Request primary MAC address this.emit("connect"); + debug("LPF2Hub connected"); resolve(); }); } @@ -131,7 +135,12 @@ export class LPF2Hub extends BaseHub { switch (message[2]) { case 0x01: { - this._parseDeviceInfo(message); + const property = message[3]; + const callback = this._propertyRequestCallbacks[property]; + if (callback) { + callback(message); + } + delete this._propertyRequestCallbacks[property]; break; } case 0x04: { @@ -164,7 +173,23 @@ export class LPF2Hub extends BaseHub { } - private _parseDeviceInfo (message: Buffer) { + private _requestHubPropertyValue (property: number) { + return new Promise((resolve) => { + this._propertyRequestCallbacks[property] = (message) => { + this._parseHubPropertyResponse(message); + return resolve(); + }; + this.send(Buffer.from([0x01, property, 0x05]), Consts.BLECharacteristic.LPF2_ALL); + }); + } + + + private _requestHubPropertyReports (property: number) { + this.send(Buffer.from([0x01, property, 0x02]), Consts.BLECharacteristic.LPF2_ALL); + } + + + private _parseHubPropertyResponse (message: Buffer) { // Button press reports if (message[3] === 0x02) { @@ -356,45 +381,6 @@ export class LPF2Hub extends BaseHub { device.receive(message); } - // if ((data[3] === 0x3d && this.type === Consts.HubType.CONTROL_PLUS_HUB)) { // Control+ CPU Temperature - // /** - // * Emits when a change is detected on a temperature sensor. Measured in degrees centigrade. - // * @event LPF2Hub#temp - // * @param {string} port For Control+ Hubs, port will be "CPU" as the sensor reports CPU temperature. - // * @param {number} temp - // */ - // this.emit("temp", "CPU", ((data.readInt16LE(4) / 900) * 90).toFixed(2)); - // return; - // } - - // const port = this._getPortForPortNumber(data[3]); - - // if (!port) { - // return; - // } - - // if (port && port.connected) { - // switch (port.type) { - // case Consts.DeviceType.DUPLO_TRAIN_BASE_COLOR: { - // if (data[4] <= 10) { - // this.emit("color", port.id, data[4]); - // } - // break; - // } - // case Consts.DeviceType.DUPLO_TRAIN_BASE_SPEEDOMETER: { - // /** - // * Emits on a speed change. - // * @event LPF2Hub#speed - // * @param {string} port - // * @param {number} speed - // */ - // const speed = data.readInt16LE(4); - // this.emit("speed", port.id, speed); - // break; - // } - // } - // } - }