1 | var Board = require("./board");
|
2 | var Emitter = require("events").EventEmitter;
|
3 | var util = require("util");
|
4 | var Fn = require("./fn");
|
5 | var priv = new Map();
|
6 | var axes = ["x", "y"];
|
7 |
|
8 | function Multiplexer(options) {
|
9 | this.pins = options.pins;
|
10 | this.io = options.io;
|
11 |
|
12 |
|
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 | Multiplexer.prototype.select = function(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 | var Controllers = {
|
27 | ANALOG: {
|
28 | initialize: {
|
29 | value: function(opts, dataHandler) {
|
30 | var axisValues = {
|
31 | x: null,
|
32 | y: null
|
33 | };
|
34 |
|
35 | opts.pins.forEach(function(pin, index) {
|
36 | this.io.pinMode(pin, this.io.MODES.ANALOG);
|
37 | this.io.analogRead(pin, function(value) {
|
38 | axisValues[axes[index]] = value;
|
39 |
|
40 | if (axisValues.x !== null && axisValues.y !== null) {
|
41 | dataHandler({
|
42 | x: axisValues.x,
|
43 | y: axisValues.y
|
44 | });
|
45 |
|
46 | axisValues.x = null;
|
47 | axisValues.y = null;
|
48 | }
|
49 | }.bind(this));
|
50 | }, this);
|
51 | }
|
52 | },
|
53 | toAxis: {
|
54 | value: function(raw, axis) {
|
55 | var state = priv.get(this);
|
56 | return Fn.constrain(Fn.fscale(raw - state[axis].zeroV, -511, 511, -1, 1), -1, 1);
|
57 | }
|
58 | }
|
59 | },
|
60 | ESPLORA: {
|
61 | initialize: {
|
62 | value: function(opts, dataHandler) {
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 | var multiplexer = new Multiplexer({
|
69 |
|
70 |
|
71 |
|
72 | pins: [18, 19, 20, 21],
|
73 | io: this.io
|
74 | });
|
75 | var channels = [11, 12];
|
76 | var index = 1;
|
77 | var axisValues = {
|
78 | x: null,
|
79 | y: null
|
80 | };
|
81 |
|
82 | this.io.pinMode(4, this.io.MODES.ANALOG);
|
83 |
|
84 | var handler = function(value) {
|
85 | axisValues[axes[index]] = value;
|
86 |
|
87 | if (axisValues.x !== null && axisValues.y !== null) {
|
88 | dataHandler({
|
89 | x: axisValues.x,
|
90 | y: axisValues.y
|
91 | });
|
92 |
|
93 | axisValues.x = null;
|
94 | axisValues.y = null;
|
95 | }
|
96 |
|
97 |
|
98 |
|
99 | this.io.removeListener("analog-read-4", handler);
|
100 |
|
101 | setTimeout(read, 10);
|
102 | }.bind(this);
|
103 |
|
104 | var read = function() {
|
105 | multiplexer.select(channels[index ^= 1]);
|
106 | this.io.analogRead(4, handler);
|
107 | }.bind(this);
|
108 |
|
109 | read();
|
110 | }
|
111 | },
|
112 | toAxis: {
|
113 | value: function(raw, axis) {
|
114 | var state = priv.get(this);
|
115 | return Fn.constrain(Fn.fscale(raw - state[axis].zeroV, -511, 511, -1, 1), -1, 1);
|
116 | }
|
117 | }
|
118 | }
|
119 | };
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 | function Joystick(opts) {
|
138 | if (!(this instanceof Joystick)) {
|
139 | return new Joystick(opts);
|
140 | }
|
141 |
|
142 | var controller = null;
|
143 |
|
144 | var state = {
|
145 | x: {
|
146 | invert: false,
|
147 | value: 0,
|
148 | previous: 0,
|
149 | zeroV: 0,
|
150 | calibrated: false
|
151 | },
|
152 | y: {
|
153 | invert: false,
|
154 | value: 0,
|
155 | previous: 0,
|
156 | zeroV: 0,
|
157 | calibrated: false
|
158 | }
|
159 | };
|
160 |
|
161 | Board.Component.call(
|
162 | this, opts = Board.Options(opts)
|
163 | );
|
164 |
|
165 | if (opts.controller && typeof opts.controller === "string") {
|
166 | controller = Controllers[opts.controller.toUpperCase()];
|
167 | } else {
|
168 | controller = opts.controller;
|
169 | }
|
170 |
|
171 | if (controller == null) {
|
172 | controller = Controllers.ANALOG;
|
173 | }
|
174 |
|
175 | Board.Controller.call(this, controller, opts);
|
176 |
|
177 | if (!this.toAxis) {
|
178 | this.toAxis = opts.toAxis || function(raw) {
|
179 | return raw;
|
180 | };
|
181 | }
|
182 |
|
183 | state.x.zeroV = opts.zeroV === undefined ? 0 : (opts.zeroV.x || 0);
|
184 | state.y.zeroV = opts.zeroV === undefined ? 0 : (opts.zeroV.y || 0);
|
185 |
|
186 | state.x.invert = opts.invertX || opts.invert || false;
|
187 | state.y.invert = opts.invertY || opts.invert || false;
|
188 |
|
189 | priv.set(this, state);
|
190 |
|
191 | if (typeof this.initialize === "function") {
|
192 | this.initialize(opts, function(data) {
|
193 | var isChange = false;
|
194 | var computed = {
|
195 | x: null,
|
196 | y: null
|
197 | };
|
198 |
|
199 | Object.keys(data).forEach(function(axis) {
|
200 | var value = data[axis];
|
201 | var sensor = state[axis];
|
202 |
|
203 |
|
204 | sensor.value = value;
|
205 |
|
206 | if (!state[axis].calibrated) {
|
207 | state[axis].calibrated = true;
|
208 | state[axis].zeroV = value;
|
209 | isChange = true;
|
210 | }
|
211 |
|
212 |
|
213 | computed[axis] = this[axis];
|
214 |
|
215 | var absAxis = Math.abs(computed[axis]);
|
216 | var absPAxis = Math.abs(sensor.previous);
|
217 |
|
218 | if ((absAxis < absPAxis) ||
|
219 | (absAxis > absPAxis)) {
|
220 | isChange = true;
|
221 | }
|
222 |
|
223 | sensor.previous = computed[axis];
|
224 | }, this);
|
225 |
|
226 | this.emit("data", {
|
227 | x: computed.x,
|
228 | y: computed.y
|
229 | });
|
230 |
|
231 | if (isChange) {
|
232 | this.emit("change", {
|
233 | x: computed.x,
|
234 | y: computed.y
|
235 | });
|
236 | }
|
237 | }.bind(this));
|
238 | }
|
239 |
|
240 | Object.defineProperties(this, {
|
241 | x: {
|
242 | get: function() {
|
243 | return this.toAxis(state.x.value, "x") * (state.x.invert ? -1 : 1);
|
244 | }
|
245 | },
|
246 | y: {
|
247 | get: function() {
|
248 | return this.toAxis(state.y.value, "y") * (state.y.invert ? -1 : 1);
|
249 | }
|
250 | }
|
251 | });
|
252 | }
|
253 |
|
254 | util.inherits(Joystick, Emitter);
|
255 |
|
256 |
|
257 | if (!!process.env.IS_TEST_MODE) {
|
258 | Joystick.Controllers = Controllers;
|
259 | Joystick.purge = function() {
|
260 | priv.clear();
|
261 | };
|
262 | }
|
263 |
|
264 | module.exports = Joystick;
|