From e75899a5a8de137f8b3fa604078de92501502ab8 Mon Sep 17 00:00:00 2001 From: Nathan Kellenicki Date: Sat, 13 Apr 2019 11:33:35 -0700 Subject: [PATCH] Increased speed of vernie --- examples/vernie_remote.js | 4 +- src/__tests__/boostmovehub.test.ts | 64 ++++++++++++++++++++++++++++++ src/__tests__/puphub.test.ts | 16 ++++---- src/__tests__/pupremote.test.ts | 35 ++++++++++++---- src/lpf2hub.ts | 3 ++ src/puphub.ts | 35 +++++++++++++++- src/testdevice.ts | 51 +++++++++++++++++------- 7 files changed, 175 insertions(+), 33 deletions(-) create mode 100644 src/__tests__/boostmovehub.test.ts diff --git a/examples/vernie_remote.js b/examples/vernie_remote.js index c621030..a9af49c 100644 --- a/examples/vernie_remote.js +++ b/examples/vernie_remote.js @@ -31,12 +31,12 @@ poweredUP.on("discover", async (hub) => { // Wait to discover Vernie and Remote switch (state) { case PoweredUP.Consts.ButtonState.UP: // If up is pressed, move the track forward { - vernie.setMotorSpeed(button === "LEFT" ? "A" : "B", 50); + vernie.setMotorSpeed(button === "LEFT" ? "A" : "B", 65); break; } case PoweredUP.Consts.ButtonState.DOWN: // If down is pressed, move the track backwards { - vernie.setMotorSpeed(button === "LEFT" ? "A" : "B", -50); + vernie.setMotorSpeed(button === "LEFT" ? "A" : "B", -65); break; } case PoweredUP.Consts.ButtonState.RELEASED: // Stop the track when the button is released diff --git a/src/__tests__/boostmovehub.test.ts b/src/__tests__/boostmovehub.test.ts new file mode 100644 index 0000000..73fff2c --- /dev/null +++ b/src/__tests__/boostmovehub.test.ts @@ -0,0 +1,64 @@ +import * as Consts from "../consts"; +import { BoostMoveHub } from "../boostmovehub"; +import { TestDevice } from "../testdevice"; + +const device = new TestDevice() +const hub = new BoostMoveHub(device); + + +beforeAll((done) => { + device.on("discoverComplete", async () => { + await hub.connect(); + device.clearOutbox(Consts.BLECharacteristic.LPF2_ALL); + done(); + }); +}); + + +afterAll(async (done) => { + await hub.disconnect(); + done(); +}); + + +test("Set motor speed", async (done) => { + hub.setMotorSpeed("A", 52); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x37, 0x11, 0x51, 0x00, 0x34])); + done(); +}); + + +test("Set motor speed for specific amount of time", async (done) => { + hub.setMotorSpeed("A", 52, 3000); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x37, 0x11, 0x51, 0x00, 0x34])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x37, 0x11, 0x51, 0x00, 0x00])); + done(); +}); + + +test("Brake a motor", async (done) => { + hub.brakeMotor("A"); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x37, 0x11, 0x51, 0x00, 0x7f])); + done(); +}); + + +test("Attempt to set two speeds on a port that only accepts one", async (done) => { + expect(() => hub.setMotorSpeed("A", [52, 32])).toThrow("Port A can only accept a single speed"); + done(); +}); + + +test("Set light brightness", async (done) => { + hub.setLightBrightness("B", 74); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x38, 0x11, 0x51, 0x00, 0x4a])); + done(); +}); + + +test("Set light brightness for specific amount of time", async (done) => { + hub.setLightBrightness("B", 74, 2000); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x38, 0x11, 0x51, 0x00, 0x4a])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x38, 0x11, 0x51, 0x00, 0x00])); + done(); +}); \ No newline at end of file diff --git a/src/__tests__/puphub.test.ts b/src/__tests__/puphub.test.ts index 3e2babd..fbbe4a1 100644 --- a/src/__tests__/puphub.test.ts +++ b/src/__tests__/puphub.test.ts @@ -9,7 +9,7 @@ const hub = new PUPHub(device); beforeAll((done) => { device.on("discoverComplete", async () => { await hub.connect(); - device.clearOutbox(); + device.clearOutbox(Consts.BLECharacteristic.LPF2_ALL); done(); }); }); @@ -23,22 +23,22 @@ afterAll(async (done) => { test("Set motor speed", async (done) => { hub.setMotorSpeed("A", 52); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x0a, 0x00, 0x81, 0x00, 0x11, 0x60, 0x00, 0x34, 0x00, 0x00])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x0a, 0x00, 0x81, 0x00, 0x11, 0x60, 0x00, 0x34, 0x00, 0x00])); done(); }); test("Set motor speed for specific amount of time", async (done) => { hub.setMotorSpeed("A", 52, 3000); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x0a, 0x00, 0x81, 0x00, 0x11, 0x60, 0x00, 0x34, 0x00, 0x00])); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x0a, 0x00, 0x81, 0x00, 0x11, 0x60, 0x00, 0x00, 0x00, 0x00])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x0a, 0x00, 0x81, 0x00, 0x11, 0x60, 0x00, 0x34, 0x00, 0x00])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x0a, 0x00, 0x81, 0x00, 0x11, 0x60, 0x00, 0x00, 0x00, 0x00])); done(); }); test("Brake a motor", async (done) => { hub.brakeMotor("A"); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x0a, 0x00, 0x81, 0x00, 0x11, 0x60, 0x00, 0x7f, 0x00, 0x00])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x0a, 0x00, 0x81, 0x00, 0x11, 0x60, 0x00, 0x7f, 0x00, 0x00])); done(); }); @@ -51,14 +51,14 @@ test("Attempt to set two speeds on a port that only accepts one", async (done) = test("Set light brightness", async (done) => { hub.setLightBrightness("B", 74); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x01, 0x11, 0x51, 0x00, 0x4a])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x01, 0x11, 0x51, 0x00, 0x4a])); done(); }); test("Set light brightness for specific amount of time", async (done) => { hub.setLightBrightness("B", 74, 2000); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x01, 0x11, 0x51, 0x00, 0x4a])); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x01, 0x11, 0x51, 0x00, 0x00])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x01, 0x11, 0x51, 0x00, 0x4a])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x01, 0x11, 0x51, 0x00, 0x00])); done(); }); \ No newline at end of file diff --git a/src/__tests__/pupremote.test.ts b/src/__tests__/pupremote.test.ts index 1609b6e..1500eeb 100644 --- a/src/__tests__/pupremote.test.ts +++ b/src/__tests__/pupremote.test.ts @@ -9,7 +9,10 @@ const remote = new PUPRemote(device); beforeAll((done) => { device.on("discoverComplete", async () => { await remote.connect(); - device.clearOutbox(); + device.postToInbox(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x06, 0x00, 0x01, 0x02, 0x06, 0x00])); + device.postToInbox(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x0f, 0x00, 0x04, 0x00, 0x01, 0x37, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10])); + device.postToInbox(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x0f, 0x00, 0x04, 0x01, 0x01, 0x37, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10])); + device.clearOutbox(Consts.BLECharacteristic.LPF2_ALL); done(); }); }); @@ -23,23 +26,41 @@ afterAll(async (done) => { test("Set LED color via discrete value", async (done) => { remote.setLEDColor(Consts.Color.BLUE); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x0a, 0x00, 0x41, 0x34, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00])); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x34, 0x11, 0x51, 0x00, 0x03])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x0a, 0x00, 0x41, 0x34, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x34, 0x11, 0x51, 0x00, 0x03])); done(); }); test("Turn off LED", async (done) => { remote.setLEDColor(false); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x0a, 0x00, 0x41, 0x34, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00])); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x34, 0x11, 0x51, 0x00, 0x00])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x0a, 0x00, 0x41, 0x34, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x08, 0x00, 0x81, 0x34, 0x11, 0x51, 0x00, 0x00])); done(); }); test("Set LED color via RGB values", async (done) => { remote.setLEDRGB(127, 32, 233); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x0a, 0x00, 0x41, 0x34, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00])); - expect(await device.readFromOutbox()).toEqual(Buffer.from([0x0a, 0x00, 0x81, 0x34, 0x11, 0x51, 0x01, 0x7f, 0x20, 0xe9])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x0a, 0x00, 0x41, 0x34, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00])); + expect(await device.readFromOutbox(Consts.BLECharacteristic.LPF2_ALL)).toEqual(Buffer.from([0x0a, 0x00, 0x81, 0x34, 0x11, 0x51, 0x01, 0x7f, 0x20, 0xe9])); done(); +}); + + +test("Ensure buttons are correctly attached", async (done) => { + expect(remote.getPortDeviceType("LEFT")).toBe(Consts.DeviceType.POWERED_UP_REMOTE_BUTTON); + expect(remote.getPortDeviceType("RIGHT")).toBe(Consts.DeviceType.POWERED_UP_REMOTE_BUTTON); + done(); +}); + + +test("Ensure UP button press events are emitted", async (done) => { + remote.on("button", (button, state) => { + expect(button).toBe("LEFT"); + expect(state).toBe(Consts.ButtonState.UP); + remote.removeAllListeners(); + done(); + }); + device.postToInbox(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x05, 0x00, 0x45, 0x00, 0x01])); }); \ No newline at end of file diff --git a/src/lpf2hub.ts b/src/lpf2hub.ts index c7b6294..c1e4cec 100644 --- a/src/lpf2hub.ts +++ b/src/lpf2hub.ts @@ -36,6 +36,9 @@ export class LPF2Hub extends Hub { if (this.type === Consts.HubType.DUPLO_TRAIN_HUB) { this._writeMessage(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x41, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01])); } + setTimeout(() => { + this._writeMessage(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x01, 0x03, 0x05])); // Request firmware version again + }, 200); }); } diff --git a/src/puphub.ts b/src/puphub.ts index 8b24228..d8a845f 100644 --- a/src/puphub.ts +++ b/src/puphub.ts @@ -98,7 +98,7 @@ export class PUPHub extends LPF2Hub { data = Buffer.from([0x81, portObj.value, 0x11, 0x02, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed)]); } else { // @ts-ignore: The type of speed is properly checked at the start - data = Buffer.from([0x81, portObj.value, 0x11, 0x60, 0x00, this._mapSpeed(speed), 0x00, 0x00]); + data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]); } this._writeMessage(Consts.BLECharacteristic.LPF2_ALL, data); const timeout = global.setTimeout(() => { @@ -118,7 +118,7 @@ export class PUPHub extends LPF2Hub { data = Buffer.from([0x81, portObj.value, 0x11, 0x02, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed)]); } else { // @ts-ignore: The type of speed is properly checked at the start - data = Buffer.from([0x81, portObj.value, 0x11, 0x60, 0x00, this._mapSpeed(speed), 0x00, 0x00]); + data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]); } this._writeMessage(Consts.BLECharacteristic.LPF2_ALL, data); return resolve(); @@ -149,6 +149,37 @@ export class PUPHub extends LPF2Hub { } + + public setMotorAngle (port: string, angle: number, speed: number | [number, number] = 100) { + const portObj = this._portLookup(port); + if (!(portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR || portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR)) { + throw new Error("Angle rotation is only available when using a Boost Tacho Motor or Boost Move Hub Motor"); + } + if (portObj.id !== "AB" && speed instanceof Array) { + throw new Error(`Port ${portObj.id} can only accept a single speed`); + } + portObj.cancelEventTimer(); + return new Promise((resolve, reject) => { + portObj.busy = true; + let data = null; + if (portObj.id === "AB") { + data = Buffer.from([0x81, portObj.value, 0x11, 0x0c, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]); + } else { + // @ts-ignore: The type of speed is properly checked at the start + data = Buffer.from([0x81, portObj.value, 0x11, 0x0b, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]); + } + data.writeUInt32LE(angle, 4); + this._writeMessage(Consts.BLECharacteristic.LPF2_ALL, data); + portObj.finished = () => { + return resolve(); + }; + }); + } + + + + + /** * Fully (hard) stop the motor on a given port. * @method PUPHub#brakeMotor diff --git a/src/testdevice.ts b/src/testdevice.ts index c7e39a5..76d7b99 100644 --- a/src/testdevice.ts +++ b/src/testdevice.ts @@ -14,8 +14,11 @@ export class TestDevice extends EventEmitter implements IBLEDevice { private _queue: Promise = Promise.resolve(); private _mailbox: Buffer[] = []; - private _outbox: Buffer[] = []; - private _outboxCallback: (() => void) | null = null; + + private _outbox: {[uuid: string]: Buffer[]} = {}; + private _outboxCallback: {[uuid: string]: () => void} = {}; + private _inbox: {[uuid: string]: Buffer[]} = {}; + private _inboxCallback: {[uuid: string]: (data: Buffer) => void} = {}; private _connected: boolean = false; private _connecting: boolean = false; @@ -74,6 +77,14 @@ export class TestDevice extends EventEmitter implements IBLEDevice { public subscribeToCharacteristic (uuid: string, callback: (data: Buffer) => void) { + this._inbox[uuid] = this._inbox[uuid] || []; + if (this._inbox[uuid].length >= 1) { + // @ts-ignore + callback(this._inbox[uuid].shift()); + } + this._inboxCallback[uuid] = (data) => { + callback(data); + }; return; } @@ -89,10 +100,11 @@ export class TestDevice extends EventEmitter implements IBLEDevice { public writeToCharacteristic (uuid: string, data: Buffer, callback?: () => void) { - this._outbox.push(data); - if (this._outboxCallback) { - this._outboxCallback(); - this._outboxCallback = null; + this._outbox[uuid] = this._outbox[uuid] || []; + this._outbox[uuid].push(data); + if (this._outboxCallback[uuid]) { + this._outboxCallback[uuid](); + delete this._outboxCallback[uuid]; } if (callback) { callback(); @@ -100,22 +112,33 @@ export class TestDevice extends EventEmitter implements IBLEDevice { } - public readFromOutbox () { + public readFromOutbox (uuid: string) { return new Promise((resolve, reject) => { - if (this._outbox.length >= 1) { - return resolve(this._outbox.shift()); + this._outbox[uuid] = this._outbox[uuid] || []; + if (this._outbox[uuid].length >= 1) { + return resolve(this._outbox[uuid].shift()); } else { - this._outboxCallback = () => { - return resolve(this._outbox.shift()); + this._outboxCallback[uuid] = () => { + return resolve(this._outbox[uuid].shift()); }; } }); } - public clearOutbox () { - this._outbox = []; - this._outboxCallback = null; + public postToInbox (uuid: string, data: Buffer) { + if (this._inboxCallback[uuid]) { + this._inboxCallback[uuid](data); + } else { + this._inbox[uuid] = this._inbox[uuid] || []; + this._inbox[uuid].push(data); + } + } + + + public clearOutbox (uuid: string) { + this._outbox[uuid] = []; + delete this._outboxCallback[uuid]; }