UNPKG

6.11 kBJavaScriptView Raw
1const Board = require("./board");
2const Emitter = require("events");
3const { constrain, fscale } = require("./fn");
4const priv = new Map();
5const axes = ["x", "y"];
6
7class Multiplexer {
8 constructor({pins, io}) {
9 this.pins = pins;
10 this.io = io;
11
12 // Setup these "analog" pins as digital output.
13 this.io.pinMode(this.pins[0], this.io.MODES.OUTPUT);
14 this.io.pinMode(this.pins[1], this.io.MODES.OUTPUT);
15 this.io.pinMode(this.pins[2], this.io.MODES.OUTPUT);
16 this.io.pinMode(this.pins[3], this.io.MODES.OUTPUT);
17 }
18
19 select(channel) {
20 this.io.digitalWrite(this.pins[0], channel & 1 ? this.io.HIGH : this.io.LOW);
21 this.io.digitalWrite(this.pins[1], channel & 2 ? this.io.HIGH : this.io.LOW);
22 this.io.digitalWrite(this.pins[2], channel & 4 ? this.io.HIGH : this.io.LOW);
23 this.io.digitalWrite(this.pins[3], channel & 8 ? this.io.HIGH : this.io.LOW);
24 }
25}
26
27const Controllers = {
28 ANALOG: {
29 initialize: {
30 value({pins}, callback) {
31 const axisValues = {
32 x: null,
33 y: null
34 };
35
36 pins.forEach((pin, index) => {
37 this.io.pinMode(pin, this.io.MODES.ANALOG);
38 this.io.analogRead(pin, value => {
39 axisValues[axes[index]] = value;
40
41 if (axisValues.x !== null && axisValues.y !== null) {
42 callback({
43 x: axisValues.x,
44 y: axisValues.y
45 });
46
47 axisValues.x = null;
48 axisValues.y = null;
49 }
50 });
51 });
52 }
53 },
54 toAxis: {
55 value(raw, axis) {
56 const state = priv.get(this);
57 return constrain(fscale(raw - state[axis].zeroV, -511, 511, -1, 1), -1, 1);
58 }
59 }
60 },
61 ESPLORA: {
62 initialize: {
63 value(options, callback) {
64 // References:
65 //
66 // https://github.com/arduino/Arduino/blob/master/libraries/Esplora/src/Esplora.h
67 // https://github.com/arduino/Arduino/blob/master/libraries/Esplora/src/Esplora.cpp
68 //
69 const multiplexer = new Multiplexer({
70 // Since Multiplexer uses digitalWrite,
71 // we have to send the analog pin numbers
72 // in their "digital" pin order form.
73 pins: [18, 19, 20, 21],
74 io: this.io
75 });
76 const channels = [11, 12];
77 let index = 1;
78 const axisValues = {
79 x: null,
80 y: null
81 };
82
83 this.io.pinMode(4, this.io.MODES.ANALOG);
84
85 const handler = value => {
86 axisValues[axes[index]] = value;
87
88 if (axisValues.x !== null && axisValues.y !== null) {
89 callback({
90 x: axisValues.x,
91 y: axisValues.y
92 });
93
94 axisValues.x = null;
95 axisValues.y = null;
96 }
97
98 // Remove this handler to all the multiplexer
99 // to setup the next pin for the next read.
100 this.io.removeListener("analog-read-4", handler);
101
102 setTimeout(read, 10);
103 };
104
105 var read = () => {
106 multiplexer.select(channels[index ^= 1]);
107 this.io.analogRead(4, handler);
108 };
109
110 read();
111 }
112 },
113 toAxis: {
114 value(raw, axis) {
115 const state = priv.get(this);
116 return constrain(fscale(raw - state[axis].zeroV, -511, 511, -1, 1), -1, 1);
117 }
118 }
119 }
120};
121
122Controllers.DEFAULT = Controllers.ANALOG;
123
124/**
125 * Joystick
126 * @constructor
127 *
128 * five.Joystick([ x, y[, z] ]);
129 *
130 * five.Joystick({
131 * pins: [ x, y[, z] ]
132 * freq: ms
133 * });
134 *
135 *
136 * @param {Object} options [description]
137 *
138 */
139class Joystick extends Emitter {
140 constructor(options) {
141 super();
142
143 Board.Component.call(
144 this, options = Board.Options(options)
145 );
146
147 Board.Controller.call(this, Controllers, options);
148
149 if (!this.toAxis) {
150 this.toAxis = options.toAxis || (raw => raw);
151 }
152
153 const state = {
154 x: {
155 invert: false,
156 value: 0,
157 previous: 0,
158 zeroV: 0,
159 calibrated: false
160 },
161 y: {
162 invert: false,
163 value: 0,
164 previous: 0,
165 zeroV: 0,
166 calibrated: false
167 }
168 };
169
170 state.x.zeroV = options.zeroV === undefined ? 0 : (options.zeroV.x || 0);
171 state.y.zeroV = options.zeroV === undefined ? 0 : (options.zeroV.y || 0);
172
173 state.x.invert = options.invertX || options.invert || false;
174 state.y.invert = options.invertY || options.invert || false;
175
176 priv.set(this, state);
177
178 if (typeof this.initialize === "function") {
179 this.initialize(options, data => {
180 let isChange = false;
181 const computed = {
182 x: null,
183 y: null
184 };
185
186 Object.keys(data).forEach(axis => {
187 const value = data[axis];
188 const sensor = state[axis];
189
190 // Set the internal ADC reading value...
191 sensor.value = value;
192
193 if (!state[axis].calibrated) {
194 state[axis].calibrated = true;
195 state[axis].zeroV = value;
196 isChange = true;
197 }
198
199 // ... Get the computed axis value.
200 computed[axis] = this[axis];
201
202 const absAxis = Math.abs(computed[axis]);
203 const absPAxis = Math.abs(sensor.previous);
204
205 if ((absAxis < absPAxis) ||
206 (absAxis > absPAxis)) {
207 isChange = true;
208 }
209
210 sensor.previous = computed[axis];
211 });
212
213 this.emit("data", {
214 x: computed.x,
215 y: computed.y
216 });
217
218 if (isChange) {
219 this.emit("change", {
220 x: computed.x,
221 y: computed.y
222 });
223 }
224 });
225 }
226
227 Object.defineProperties(this, {
228 x: {
229 get() {
230 return this.toAxis(state.x.value, "x") * (state.x.invert ? -1 : 1);
231 }
232 },
233 y: {
234 get() {
235 return this.toAxis(state.y.value, "y") * (state.y.invert ? -1 : 1);
236 }
237 }
238 });
239 }
240}
241
242/* istanbul ignore else */
243if (!!process.env.IS_TEST_MODE) {
244 Joystick.Controllers = Controllers;
245 Joystick.purge = () => {
246 priv.clear();
247 };
248}
249
250module.exports = Joystick;