UNPKG

johnny-five-electron

Version:

Temporary fork to support Electron (to be deprecated)

350 lines (285 loc) 8.67 kB
var Emitter = require("events").EventEmitter; var util = require("util"); var Board = require("../lib/board.js"); var __ = require("../lib/fn.js"); var int16 = __.int16; var priv = new Map(); var aliases = { down: ["down", "press", "tap", "impact", "hit"], up: ["up", "release"], hold: ["hold"] }; var trigger = function(key, value) { var event = { which: value, timestamp: Date.now() }; aliases[key].forEach(function(type) { this.emit(type, event); }, this); }; function flatKeys(opts) { var keys = []; if (opts.keys && Array.isArray(opts.keys)) { keys = opts.keys.slice(); if (keys.every(Array.isArray)) { keys = keys.reduce(function(accum, row) { return accum.concat(row); }, []); } } return keys; } // TODO: // // Provide a mechanism for explicitly naming aliases for buttons // // var Controllers = { MPR121QR2: { REGISTER: { value: require("../lib/definitions/mpr121.js") }, initialize: { value: function(opts, dataHandler) { var state = priv.get(this); var address = opts.address || 0x5A; var keys = flatKeys(opts); var keyMap = this.REGISTER.MAPS[opts.controller].KEYS; var targets = this.REGISTER.MAPS[opts.controller].TARGETS; var mapping = Object.keys(keyMap).reduce(function(accum, index) { accum[index] = keyMap[index]; return accum; }, []); var length = mapping.length; this.io.i2cConfig(opts); this.io.i2cWrite(address, this.REGISTER.MHD_RISING, 0x01); this.io.i2cWrite(address, this.REGISTER.NHD_AMOUNT_RISING, 0x01); this.io.i2cWrite(address, this.REGISTER.NCL_RISING, 0x00); this.io.i2cWrite(address, this.REGISTER.FDL_RISING, 0x00); this.io.i2cWrite(address, this.REGISTER.MHD_FALLING, 0x01); this.io.i2cWrite(address, this.REGISTER.NHD_AMOUNT_FALLING, 0x01); this.io.i2cWrite(address, this.REGISTER.NCL_FALLING, 0xFF); this.io.i2cWrite(address, this.REGISTER.FDL_FALLING, 0x02); for (var i = 0; i < 12; i++) { this.io.i2cWrite(address, this.REGISTER.ELE0_TOUCH_THRESHOLD + (i << 1), 40); this.io.i2cWrite(address, this.REGISTER.ELE0_RELEASE_THRESHOLD + (i << 1), 20); } this.io.i2cWrite(address, this.REGISTER.FILTER_CONFIG, 0x04); this.io.i2cWrite(address, this.REGISTER.ELECTRODE_CONFIG, 0x0C); if (!keys.length) { keys = Array.from(Object.assign({}, keyMap, {length: length})); } state.length = length; state.touches = touches(length); state.keys = keys; state.mapping = mapping; state.targets = targets; this.io.i2cRead(address, 0x00, 2, function(bytes) { dataHandler(int16(bytes[1], bytes[0])); }); } }, toAlias: { value: function(index) { var state = priv.get(this); return state.keys[index]; } }, toIndex: { value: function(raw) { var state = priv.get(this); // console.log("raw", raw, state.targets[raw]); return state.targets[raw]; } } }, // https://learn.sparkfun.com/tutorials/vkey-voltage-keypad-hookup-guide VKEY: { initialize: { value: function(opts, dataHandler) { var state = priv.get(this); var keys = flatKeys(opts); var mapping = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]; var length = 0; if (!keys.length) { keys = mapping; } length = mapping.length; state.length = length; state.scale = { bottom: 17, step: 40, top: 496 }; state.touches = touches(length); state.mapping = mapping; state.keys = keys; this.io.pinMode(this.pin, this.io.MODES.ANALOG); this.io.analogRead(this.pin, function(adc) { dataHandler(adc); }.bind(this)); }, }, toAlias: { value: function(index) { var state = priv.get(this); return state.keys[index]; } }, toIndex: { value: function(raw) { var state = priv.get(this); var scale = state.scale; var length = state.length; if (raw < scale.bottom || raw > scale.top) { return null; } return (length - ((raw - scale.bottom) / scale.step)) | 0; } } }, // WaveShare AD // - http://www.amazon.com/WaveShare-Accessory-buttons-controlled-keyboard/dp/B00KM6UXVS // - http://www.wvshare.com/product/A_D-Keypad.htm // // TODO: Create docs to show how to create a DIY keypad // that works with this class. // ANALOG: { initialize: { value: function(opts, dataHandler) { var keys = flatKeys(opts); var mapping = []; var length = 0; if (opts.length && !keys.length) { keys = Array.from({ length: opts.length }, function(_, key) { return key; }); } if (!keys.length) { throw new Error( "Missing `keys`. Analog Keypad requires either a numeric `length` or a `keys` array." ); } mapping = keys; length = mapping.length; var state = priv.get(this); // keys + Idle state == length + 1 var total = length + 1; var vrange = Math.round(1023 / total); var ranges = Array.from({ length: total }, function(_, index) { var start = vrange * index; return Array.from({ length: vrange - 1 }, function(_, index) { return start + index; }); }); state.length = length; state.ranges = ranges; state.touches = touches(length); state.mapping = mapping; state.keys = keys; this.io.pinMode(this.pin, this.io.MODES.ANALOG); this.io.analogRead(this.pin, function(adc) { dataHandler(adc); }); } }, toAlias: { value: function(index) { var state = priv.get(this); return state.keys[index]; } }, toIndex: { value: function(raw) { var state = priv.get(this); var ranges = state.ranges; var index = ranges.findIndex(function(range) { return range.includes(raw); }); if (index === state.length) { index--; } if (index < 0) { return null; } return index; } } } }; // Otherwise known as... Controllers["MPR121"] = Controllers.MPR121QR2; function touches(length) { return Array.from({ length: length }, function() { return 0; }); } function Keypad(opts) { if (!(this instanceof Keypad)) { return new Keypad(opts); } // Initialize a Device instance on a Board Board.Device.call( this, opts = Board.Options(opts) ); var raw = null; var controller = null; var state = { touches: null, timeout: null, length: null, keys: null, mapping: null, holdtime: null, }; if (opts.controller && typeof opts.controller === "string") { controller = Controllers[opts.controller.toUpperCase()]; } else { controller = opts.controller; } if (controller == null) { controller = Controllers.ANALOG; } Object.defineProperties(this, controller); state.holdtime = opts.holdtime ? opts.holdtime : 500; priv.set(this, state); if (typeof this.initialize === "function") { this.initialize(opts, function(data) { var target = this.toIndex(data); var length = state.length; var alias = null; raw = data; for (var i = 0; i < length; i++) { alias = this.toAlias(i); if (target === i) { if (state.touches[i] === 0) { state.timeout = Date.now() + state.holdtime; trigger.call(this, "down", alias); } else if (state.touches[i] === 1) { if (state.timeout !== null && Date.now() > state.timeout) { state.timeout = Date.now() + state.holdtime; trigger.call(this, "hold", alias); } } state.touches[i] = 1; } else { if (state.touches[i] === 1) { state.timeout = null; trigger.call(this, "up", alias); } state.touches[i] = 0; } alias = null; } }.bind(this)); } Object.defineProperties(this, { value: { get: function() { return raw; } }, target: { get: function() { return state.keys[this.toIndex(this.value)]; } } }); } util.inherits(Keypad, Emitter); module.exports = Keypad;