commit c403ec3f2b8575576c463b8e30c55df70d62ca96 Author: Nathan Kunicki Date: Sat Jun 18 00:11:35 2016 +0100 Initial commit - working Toy Pad interaction diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1fe1b00 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +node_modules/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..cffe8cd --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +save-exact=true diff --git a/data/minifigs.json b/data/minifigs.json new file mode 100644 index 0000000..22126a3 --- /dev/null +++ b/data/minifigs.json @@ -0,0 +1,10 @@ +{ + "47 5c 0a 6e 40 81": "Batman", + "19 a9 7a 99 40 80": "Gandalf", + "11 d3 da 98 40 81": "Wyldstyle", + "d9 e6 52 5c 49 80": "Unikitty", + "40 42 b2 2a 49 80": "Slimer", + "ed 0c 32 75 40 84": "Bane", + "d4 61 82 55 42 80": "Lloyd", + "54 60 a2 5b 49 81": "Krusty-the-Clown" +} \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..56518dc --- /dev/null +++ b/main.js @@ -0,0 +1,24 @@ +let ToyPad = require("./src/toypad.js"); + +let toyPad = new ToyPad(); + +toyPad.on("connect", () => { + console.log("Toy Pad connected"); + toyPad.setColor(ToyPad.Panel.CENTER, 0xffff44, 0.5); +}); + +toyPad.on("error", () => { + console.log("Toy Pad connection error"); +}); + +toyPad.on("add", (data) => { + let name = ToyPad.minifigNameFromSignature(data.sig); + console.log(`Minifig added to panel ${data.panel} (${name})`); +}); + +toyPad.on("remove", (data) => { + let name = ToyPad.minifigNameFromSignature(data.sig); + console.log(`Minifig removed from panel ${data.panel} (${name})`); +}); + +toyPad.connect(); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..f2b7661 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "retrodimensions", + "version": "1.0.0", + "description": "Retro Dimensions", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Nathan Kunicki ", + "license": "MIT", + "dependencies": { + "node-hid": "0.5.1" + } +} diff --git a/src/toypad.js b/src/toypad.js new file mode 100644 index 0000000..ceb534b --- /dev/null +++ b/src/toypad.js @@ -0,0 +1,167 @@ +let EventEmitter = require("events").EventEmitter, + HID = require("node-hid"); + + +const minifigData = require("../data/minifigs.json"); +let Minifig = {}; + +Object.keys(minifigData).forEach((sig) => { + Minifig[minifigData[sig].toUpperCase().replace(/\W+/g, "_")] = sig; +}); + + +const Action = { + ADD: 0, + REMOVE: 1 +}; + + +const Command = { + CONNECTED: 0x19, + ACTION: 0x0b +}; + + +const Panel = { + ALL: 0, + CENTER: 1, + LEFT: 2, + RIGHT: 3 +}; + + +class ToyPad extends EventEmitter { + + + constructor () { + super(); + this._device = null; + this._requestId = 0; + } + + + static get Panel () { + return Panel; + } + + + static get Minifig () { + return Minifig; + } + + + static minifigNameFromSignature (sig) { + if (minifigData[sig]) { + return minifigData[sig]; + } else { + return `Unknown - ${sig}`; + } + } + + + static _bufferToHexString (buf) { + let str = ""; + for (let i = 0; i < buf.length; i++) { + str += + ((buf[i] >> 4) & 0xf).toString(16) + + (buf[i] & 0xf).toString(16) + + " "; + } + return str.trim(); + } + + + static _checksum (data) { + let checksum = 0; + for (let i = 0; i < data.length; i++) { + checksum += data[i]; + } + data.push(checksum & 0xFF); + return data; + } + + + static _pad (data) { + while (data.length < 32) { + data.push(0x00); + } + return data; + } + + + connect () { + + try { + + this._device = new HID.HID(3695, 577); + + this._device.on("data", (data) => { + + let cmd = data[1]; + + if (cmd == Command.CONNECTED) { + this.emit("connect"); + } else if (cmd == Command.ACTION) { + + let action = data[5]; + + let emitPayload = { + panel: data[2], + sig: ToyPad._bufferToHexString(data.slice(7, 13)) + }; + + if (action == Action.ADD) { + this.emit("add", emitPayload); + } else if (action == Action.REMOVE) { + this.emit("remove", emitPayload); + } + + } + + }); + + this._device.on("error", (err) => { + this.emit(err); + }); + + // Wake the Toy Pad + this._device.write([0x00, + 0x55, 0x0f, 0xb0, 0x01, + 0x28, 0x63, 0x29, 0x20, + 0x4c, 0x45, 0x47, 0x4f, + 0x20, 0x32, 0x30, 0x31, + 0x34, 0xf7, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00]); + + } catch (err) { + this.emit("error"); + } + + } + + + setColor (panel, color, speed = 1) { + let data = [ + this._requestId & 0xff, + panel, + ((1 - speed) * 0xff) & 0xff, + 0x01, (color >> 16) & 0xff, + (color >> 8) & 0xff, + color & 0xff + ]; + this._requestId++; + this._write([0x55, 0x08, 0xc2].concat(data)); + }; + + + _write (data) { + this._device.write([0x00].concat(ToyPad._pad(ToyPad._checksum(data)))); + } + + +} + + +module.exports = ToyPad; \ No newline at end of file