1 | var Board = require("./board"),
|
2 | events = require("events"),
|
3 | util = require("util");
|
4 |
|
5 | var Devices, Change, Update;
|
6 |
|
7 |
|
8 | var aliases = {
|
9 | down: ["down", "press", "tap", "impact", "hit"],
|
10 | up: ["up", "release"],
|
11 | hold: ["hold"]
|
12 | };
|
13 |
|
14 |
|
15 | var priv = new Map();
|
16 |
|
17 |
|
18 | var holdTimeout = new Map();
|
19 |
|
20 |
|
21 |
|
22 | var last = new Map();
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | function Wii(opts) {
|
49 |
|
50 | if (!(this instanceof Wii)) {
|
51 | return new Wii(opts);
|
52 | }
|
53 |
|
54 | Board.Component.call(this, opts);
|
55 |
|
56 |
|
57 | var device = Devices[opts.device];
|
58 | var address = device.address;
|
59 | var bytes = device.bytes;
|
60 | var delay = device.delay;
|
61 | var data = device.data.bind(this);
|
62 | var setup = device.setup;
|
63 | var preread = device.preread;
|
64 |
|
65 |
|
66 | this.freq = opts.freq || 100;
|
67 |
|
68 |
|
69 | this.holdtime = opts.holdtime || 500;
|
70 | this.threshold = opts.threshold || 10;
|
71 |
|
72 |
|
73 | device.initialize.call(this);
|
74 |
|
75 |
|
76 | last.set(this, [0, 0, 0, 0, 0, 0, 0]);
|
77 |
|
78 |
|
79 | this.io.i2cConfig(opts);
|
80 |
|
81 |
|
82 | setup.forEach(function(bytes) {
|
83 | this.io.i2cWrite(address, bytes);
|
84 | }, this);
|
85 |
|
86 |
|
87 | setInterval(function() {
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | preread.forEach(function(bytes) {
|
95 | this.io.i2cWrite(address, bytes);
|
96 | }, this);
|
97 |
|
98 |
|
99 |
|
100 | this.io.i2cReadOnce(address, bytes, data);
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 | if (typeof device.read !== "undefined") {
|
112 | device.read.call(this);
|
113 | }
|
114 | }.bind(this), delay || this.freq);
|
115 |
|
116 |
|
117 | setInterval(function() {
|
118 | var event = new Board.Event({
|
119 | target: this
|
120 | });
|
121 |
|
122 | this.emit("data", event);
|
123 |
|
124 | }.bind(this), this.freq);
|
125 | }
|
126 |
|
127 | Wii.Components = {};
|
128 |
|
129 |
|
130 | Wii.Components.Button = function(which, controller) {
|
131 |
|
132 | if (!(this instanceof Wii.Components.Button)) {
|
133 | return new Wii.Components.Button(which, controller);
|
134 | }
|
135 |
|
136 |
|
137 | this.which = which;
|
138 |
|
139 |
|
140 | this.controller = controller;
|
141 |
|
142 |
|
143 | var state = {
|
144 | isDown: false
|
145 | };
|
146 | priv.set(this, state);
|
147 |
|
148 | Object.defineProperties(this, {
|
149 |
|
150 | isUp: {
|
151 | get: function() {
|
152 | return !state.isDown;
|
153 | }
|
154 | },
|
155 |
|
156 |
|
157 | isDown: {
|
158 | get: function() {
|
159 | return state.isDown;
|
160 | }
|
161 | }
|
162 | });
|
163 | };
|
164 |
|
165 | Wii.Components.Joystick = function(controller) {
|
166 |
|
167 | if (!(this instanceof Wii.Components.Joystick)) {
|
168 | return new Wii.Components.Joystick(controller);
|
169 | }
|
170 |
|
171 | this.controller = controller;
|
172 |
|
173 | var state, accessors;
|
174 |
|
175 |
|
176 | state = {};
|
177 |
|
178 |
|
179 | accessors = {};
|
180 |
|
181 |
|
182 | ["x", "y", "dx", "dy"].forEach(function(key) {
|
183 |
|
184 | state[key] = 0;
|
185 |
|
186 |
|
187 | accessors[key] = {
|
188 | get: function() {
|
189 | return state[key];
|
190 | }
|
191 | };
|
192 | }, this);
|
193 |
|
194 |
|
195 | priv.set(this, state);
|
196 |
|
197 |
|
198 | Object.defineProperties(this, accessors);
|
199 | };
|
200 |
|
201 | Wii.Components.Accelerometer = function(controller) {
|
202 |
|
203 | if (!(this instanceof Wii.Components.Accelerometer)) {
|
204 | return new Wii.Components.Accelerometer(controller);
|
205 | }
|
206 |
|
207 | this.controller = controller;
|
208 |
|
209 | var state, accessors;
|
210 |
|
211 |
|
212 | state = {};
|
213 |
|
214 |
|
215 | accessors = {};
|
216 |
|
217 |
|
218 | ["x", "y", "z", "dx", "dy", "dz"].forEach(function(key) {
|
219 |
|
220 | state[key] = 0;
|
221 |
|
222 |
|
223 | accessors[key] = {
|
224 | get: function() {
|
225 | return state[key];
|
226 | }
|
227 | };
|
228 | }, this);
|
229 |
|
230 |
|
231 | priv.set(this, state);
|
232 |
|
233 |
|
234 | Object.defineProperties(this, accessors);
|
235 | };
|
236 |
|
237 | util.inherits(Wii, events.EventEmitter);
|
238 | util.inherits(Wii.Components.Button, events.EventEmitter);
|
239 | util.inherits(Wii.Components.Joystick, events.EventEmitter);
|
240 | util.inherits(Wii.Components.Accelerometer, events.EventEmitter);
|
241 |
|
242 |
|
243 |
|
244 |
|
245 | function decodeByte(x) {
|
246 | return (x ^ 0x17) + 0x17;
|
247 | }
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 | Change = {
|
260 |
|
261 |
|
262 |
|
263 | button: function(key) {
|
264 |
|
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 | aliases[key].forEach(function(type) {
|
271 | var event = new Board.Event({
|
272 |
|
273 | target: this,
|
274 | type: type
|
275 | });
|
276 |
|
277 |
|
278 | this.emit(type, event);
|
279 |
|
280 |
|
281 | this.controller.emit(type, event);
|
282 | }, this);
|
283 | },
|
284 |
|
285 |
|
286 | component: function(coordinate) {
|
287 |
|
288 |
|
289 |
|
290 |
|
291 | ["axischange", "change"].forEach(function(type) {
|
292 | var event;
|
293 |
|
294 | if (this._events && this._events[type]) {
|
295 | event = new Board.Event({
|
296 |
|
297 | target: this,
|
298 | type: type,
|
299 | axis: coordinate,
|
300 |
|
301 | direction: this["d" + coordinate] < 0 ? -1 : 1
|
302 | });
|
303 |
|
304 |
|
305 | this.emit(type, event);
|
306 |
|
307 |
|
308 | this.controller.emit(type, event);
|
309 | }
|
310 | }, this);
|
311 | }
|
312 | };
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 |
|
322 |
|
323 |
|
324 |
|
325 | Update = {
|
326 |
|
327 | button: function(isDown) {
|
328 |
|
329 |
|
330 |
|
331 |
|
332 | var state, isFireable;
|
333 |
|
334 |
|
335 | state = priv.get(this);
|
336 |
|
337 |
|
338 |
|
339 | isFireable = false;
|
340 |
|
341 | if (isDown !== state.isDown) {
|
342 | isFireable = true;
|
343 | }
|
344 |
|
345 | state.isDown = isDown;
|
346 |
|
347 | if (isFireable) {
|
348 |
|
349 | holdTimeout.set(this, setTimeout(function() {
|
350 | if (state.isDown) {
|
351 | Change.button.call(this, "hold");
|
352 | }
|
353 | }.bind(this), this.controller.holdtime));
|
354 |
|
355 | Change.button.call(this, isDown ? "down" : "up");
|
356 | }
|
357 | },
|
358 |
|
359 |
|
360 | component: function(coordinate, val) {
|
361 |
|
362 |
|
363 |
|
364 |
|
365 | var state = priv.get(this);
|
366 | state["d" + coordinate] = val - state[coordinate];
|
367 | state[coordinate] = val;
|
368 | }
|
369 | };
|
370 |
|
371 |
|
372 | Devices = {
|
373 |
|
374 |
|
375 | "RVL-004": {
|
376 | address: 0x52,
|
377 | bytes: 6,
|
378 | delay: 100,
|
379 | setup: [
|
380 | [0x40, 0x00]
|
381 | ],
|
382 | preread: [
|
383 | [0x00]
|
384 | ],
|
385 |
|
386 | read: function() {
|
387 | var axes = ["x", "y", "z"];
|
388 |
|
389 | [
|
390 | this.joystick,
|
391 | this.accelerometer
|
392 | ].forEach(function(component) {
|
393 | axes.forEach(function(axis) {
|
394 | var delta = "d" + axis;
|
395 | if (typeof component[delta] !== "undefined") {
|
396 | if (Math.abs(component[delta]) > this.threshold) {
|
397 | Change.component.call(component, axis);
|
398 | }
|
399 | }
|
400 | }, this);
|
401 | }, this);
|
402 | },
|
403 |
|
404 |
|
405 | initialize: function() {
|
406 | this.joystick = new Wii.Components.Joystick(this);
|
407 | this.accelerometer = new Wii.Components.Accelerometer(this);
|
408 | this.c = new Wii.Components.Button("c", this);
|
409 | this.z = new Wii.Components.Button("z", this);
|
410 | },
|
411 | data: function(data) {
|
412 |
|
413 |
|
414 |
|
415 |
|
416 | if (data[0] !== 254 && data[1] !== 254 && data[2] !== 254) {
|
417 |
|
418 |
|
419 | Update.component.call(
|
420 | this.joystick,
|
421 | "x", decodeByte(data[0]) << 2
|
422 | );
|
423 |
|
424 |
|
425 | Update.component.call(
|
426 | this.joystick,
|
427 | "y", decodeByte(data[1]) << 2
|
428 | );
|
429 |
|
430 |
|
431 | Update.component.call(
|
432 | this.accelerometer,
|
433 | "x", decodeByte(data[2]) << 2
|
434 | );
|
435 |
|
436 |
|
437 | Update.component.call(
|
438 | this.accelerometer,
|
439 | "y", decodeByte(data[3]) << 2
|
440 | );
|
441 |
|
442 |
|
443 | Update.component.call(
|
444 | this.accelerometer,
|
445 | "z", decodeByte(data[4]) << 2
|
446 | );
|
447 |
|
448 |
|
449 |
|
450 | Update.button.call(
|
451 | this.z, (decodeByte(data[5]) & 0x01) === 0
|
452 | );
|
453 |
|
454 |
|
455 |
|
456 | Update.button.call(
|
457 | this.c, (decodeByte(data[5]) & 0x02) === 0
|
458 | );
|
459 |
|
460 |
|
461 | last.set(this, data);
|
462 | }
|
463 | }
|
464 | },
|
465 |
|
466 |
|
467 | "RVL-005": {
|
468 | address: 0x52,
|
469 | bytes: 6,
|
470 | delay: 100,
|
471 | setup: [
|
472 | [0x40, 0x00]
|
473 | ],
|
474 | preread: [
|
475 | [0x00]
|
476 | ],
|
477 |
|
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 |
|
484 |
|
485 |
|
486 |
|
487 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 | initialize: function() {
|
493 |
|
494 | this.joystick = {
|
495 | left: new Wii.Components.Joystick(this),
|
496 | right: new Wii.Components.Joystick(this)
|
497 | };
|
498 |
|
499 |
|
500 | [
|
501 | "y", "x", "up", "down", "left", "right",
|
502 | "a", "b", "l", "r", "zl", "zr", "start", "home", "select"
|
503 | ].forEach(function(id) {
|
504 |
|
505 | this[id] = new Wii.Components.Button(id, this);
|
506 |
|
507 | }, this);
|
508 | },
|
509 | data: function(data) {
|
510 |
|
511 |
|
512 | if (data[0] !== 254 && data[1] !== 254 && data[2] !== 254) {
|
513 |
|
514 |
|
515 | Update.button.call(
|
516 | this.l, (decodeByte(data[4]) & 0x20) === 0
|
517 | );
|
518 |
|
519 | Update.button.call(
|
520 | this.r, (decodeByte(data[4]) & 0x02) === 0
|
521 | );
|
522 |
|
523 |
|
524 | Update.button.call(
|
525 | this.up, (decodeByte(data[5]) & 0x01) === 0
|
526 | );
|
527 |
|
528 | Update.button.call(
|
529 | this.left, (decodeByte(data[5]) & 0x02) === 0
|
530 | );
|
531 |
|
532 | Update.button.call(
|
533 | this.down, (decodeByte(data[4]) & 0x40) === 0
|
534 | );
|
535 |
|
536 | Update.button.call(
|
537 | this.right, (decodeByte(data[4]) & 0x80) === 0
|
538 | );
|
539 |
|
540 |
|
541 | Update.button.call(
|
542 | this.zr, (decodeByte(data[5]) & 0x04) === 0
|
543 | );
|
544 |
|
545 | Update.button.call(
|
546 | this.zl, (decodeByte(data[5]) & 0x80) === 0
|
547 | );
|
548 |
|
549 |
|
550 | Update.button.call(
|
551 | this.x, (decodeByte(data[5]) & 0x08) === 0
|
552 | );
|
553 |
|
554 | Update.button.call(
|
555 | this.y, (decodeByte(data[5]) & 0x20) === 0
|
556 | );
|
557 |
|
558 |
|
559 | Update.button.call(
|
560 | this.a, (decodeByte(data[5]) & 0x10) === 0
|
561 | );
|
562 |
|
563 | Update.button.call(
|
564 | this.b, (decodeByte(data[5]) & 0x40) === 0
|
565 | );
|
566 |
|
567 |
|
568 | Update.button.call(
|
569 | this.select, (decodeByte(data[4]) & 0x10) === 0
|
570 | );
|
571 |
|
572 | Update.button.call(
|
573 | this.start, (decodeByte(data[4]) & 0x04) === 0
|
574 | );
|
575 |
|
576 | Update.button.call(
|
577 | this.home, (decodeByte(data[4]) & 0x08) === 0
|
578 | );
|
579 |
|
580 |
|
581 | Update.component.call(
|
582 | this.joystick.left,
|
583 | "x", decodeByte(data[0]) & 0x3f
|
584 | );
|
585 |
|
586 |
|
587 | Update.component.call(
|
588 | this.joystick.left,
|
589 | "y", decodeByte(data[0]) & 0x3f
|
590 | );
|
591 |
|
592 | Update.component.call(
|
593 | this.joystick.right,
|
594 | "x", ((data[0] & 0xc0) >> 3) + ((data[1] & 0xc0) >> 5) + ((data[2] & 0x80) >> 7)
|
595 | );
|
596 |
|
597 | Update.component.call(
|
598 | this.joystick.right,
|
599 | "y", data[2] & 0x1f
|
600 | );
|
601 |
|
602 |
|
603 | last.set(this, data);
|
604 | }
|
605 | }
|
606 | }
|
607 | };
|
608 |
|
609 |
|
610 | Wii.Nunchuk = function(opts) {
|
611 | opts = opts || {};
|
612 | opts.device = "RVL-004";
|
613 |
|
614 | return new Wii(opts);
|
615 | };
|
616 |
|
617 | Wii.Classic = function(opts) {
|
618 | opts = opts || {};
|
619 | opts.device = "RVL-005";
|
620 |
|
621 | return new Wii(opts);
|
622 | };
|
623 |
|
624 | module.exports = Wii;
|