UNPKG

10.9 kBJavaScriptView Raw
1var Board = require("../board");
2var Animation = require("../animation");
3var Expander = require("../expander");
4var Fn = require("../fn");
5var Pins = Board.Pins;
6
7var priv = new Map();
8
9var Controllers = {
10 PCA9685: {
11 initialize: {
12 value: function(opts) {
13
14 var state = priv.get(this);
15
16 this.address = opts.address || 0x40;
17 this.pwmRange = opts.pwmRange || [0, 4095];
18 this.frequency = opts.frequency || 200;
19
20 state.expander = Expander.get({
21 address: this.address,
22 controller: this.controller,
23 bus: this.bus,
24 pwmRange: this.pwmRange,
25 frequency: this.frequency,
26 });
27
28 this.pin = state.expander.normalize(opts.pin);
29
30 state.mode = this.io.MODES.PWM;
31 }
32 },
33 update: {
34 writable: true,
35 value: function(input) {
36 var state = priv.get(this);
37 var output = typeof input !== "undefined" ? input : state.value;
38 var value = state.isAnode ? 255 - Board.constrain(output, 0, 255) : output;
39 this.write(value);
40 }
41 },
42 write: {
43 writable: true,
44 value: function(value) {
45 var state = priv.get(this);
46 state.expander.analogWrite(this.pin, value);
47 }
48 }
49 },
50 DEFAULT: {
51 initialize: {
52 value: function(opts, pinValue) {
53
54 var state = priv.get(this);
55 var isFirmata = Pins.isFirmata(this);
56 var defaultLed;
57
58 if (isFirmata && typeof pinValue === "string" && pinValue[0] === "A") {
59 pinValue = this.io.analogPins[+pinValue.slice(1)];
60 }
61
62 defaultLed = this.io.defaultLed || 13;
63 pinValue = +pinValue;
64
65 if (isFirmata && this.io.analogPins.includes(pinValue)) {
66 this.pin = pinValue;
67 state.mode = this.io.MODES.OUTPUT;
68 } else {
69 this.pin = typeof opts.pin === "undefined" ? defaultLed : opts.pin;
70 state.mode = this.io.MODES[
71 (this.board.pins.isPwm(this.pin) ? "PWM" : "OUTPUT")
72 ];
73 }
74
75 this.io.pinMode(this.pin, state.mode);
76 }
77 },
78 update: {
79 writable: true,
80 value: function(input) {
81 var state = priv.get(this);
82 var output = typeof input !== "undefined" ? input : state.value;
83 var value = state.isAnode ? 255 - Board.constrain(output, 0, 255) : output;
84 value = Fn.map(value, 0, 255, 0, this.board.RESOLUTION.PWM);
85
86 // If pin is not a PWM pin and brightness is not HIGH or LOW, emit an error
87 if (value !== this.io.LOW && value !== this.io.HIGH && this.mode !== this.io.MODES.PWM) {
88 Board.Pins.Error({
89 pin: this.pin,
90 type: "PWM",
91 via: "Led"
92 });
93 }
94
95 if (state.mode === this.io.MODES.OUTPUT) {
96 value = output;
97 }
98
99 this.write(value);
100 }
101 },
102 write: {
103 writable: true,
104 value: function(value) {
105 var state = priv.get(this);
106
107 if (state.mode === this.io.MODES.OUTPUT) {
108 this.io.digitalWrite(this.pin, value);
109 }
110
111 if (state.mode === this.io.MODES.PWM) {
112 this.io.analogWrite(this.pin, value);
113 }
114 }
115 }
116 }
117};
118
119/**
120 * Led
121 * @constructor
122 *
123 * five.Led(pin);
124 *
125 * five.Led({
126 * pin: number
127 * });
128 *
129 *
130 * @param {Object} opts [description]
131 *
132 */
133
134function Led(opts) {
135 if (!(this instanceof Led)) {
136 return new Led(opts);
137 }
138
139 var pinValue = typeof opts === "object" ? opts.pin : opts;
140 var controller = null;
141
142 Board.Component.call(
143 this, opts = Board.Options(opts)
144 );
145
146 if (opts.controller && typeof opts.controller === "string") {
147 controller = Controllers[opts.controller.toUpperCase()];
148 } else {
149 controller = opts.controller;
150 }
151
152 if (controller == null) {
153 controller = Controllers.DEFAULT;
154 }
155
156 Object.defineProperties(this, controller);
157
158 var state = {
159 isAnode: opts.isAnode,
160 isOn: false,
161 isRunning: false,
162 value: null,
163 direction: 1,
164 mode: null,
165 intensity: 0,
166 interval: null
167 };
168
169 priv.set(this, state);
170
171 Object.defineProperties(this, {
172 value: {
173 get: function() {
174 return state.value;
175 }
176 },
177 mode: {
178 get: function() {
179 return state.mode;
180 }
181 },
182 isOn: {
183 get: function() {
184 return !!state.value;
185 }
186 },
187 isRunning: {
188 get: function() {
189 return state.isRunning;
190 }
191 },
192 animation: {
193 get: function() {
194 return state.animation;
195 }
196 }
197 });
198
199 /* istanbul ignore else */
200 if (typeof this.initialize === "function") {
201 this.initialize(opts, pinValue);
202 }
203}
204
205/**
206 * on Turn the led on
207 * @return {Led}
208 */
209Led.prototype.on = function() {
210 var state = priv.get(this);
211
212 if (state.mode === this.io.MODES.OUTPUT) {
213 state.value = this.io.HIGH;
214 }
215
216 if (state.mode === this.io.MODES.PWM) {
217 // Assume we need to simply turn this all the way on, when:
218
219 // ...state.value is null
220 if (state.value === null) {
221 state.value = 255;
222 }
223
224 // ...there is no active interval
225 if (!state.interval) {
226 state.value = 255;
227 }
228
229 // ...the last value was 0
230 if (state.value === 0) {
231 state.value = 255;
232 }
233 }
234
235 this.update();
236
237 return this;
238};
239
240/**
241 * off Turn the led off
242 * @return {Led}
243 */
244Led.prototype.off = function() {
245 var state = priv.get(this);
246
247 state.value = 0;
248
249 this.update();
250
251 return this;
252};
253
254/**
255 * toggle Toggle the on/off state of an led
256 * @return {Led}
257 */
258Led.prototype.toggle = function() {
259 return this[this.isOn ? "off" : "on"]();
260};
261
262/**
263 * brightness
264 * @param {Number} value analog brightness value 0-255
265 * @return {Led}
266 */
267Led.prototype.brightness = function(brightness) {
268 var state = priv.get(this);
269 state.value = brightness;
270
271 this.update();
272
273 return this;
274};
275
276/**
277 * intensity
278 * @param {Number} value Light intensity 0-100
279 * @return {Led}
280 */
281Led.prototype.intensity = function(intensity) {
282 var state = priv.get(this);
283
284 if (arguments.length === 0) {
285 return state.intensity;
286 }
287
288 state.intensity = Fn.constrain(intensity, 0, 100);
289
290 return this.brightness(Fn.scale(state.intensity, 0, 100, 0, 255));
291};
292
293/**
294 * Animation.normalize
295 *
296 * @param [number || object] keyFrames An array of step values or a keyFrame objects
297 */
298
299Led.prototype[Animation.normalize] = function(keyFrames) {
300 var state = priv.get(this);
301
302 // If user passes null as the first element in keyFrames use current value
303 /* istanbul ignore else */
304 if (keyFrames[0] === null) {
305 keyFrames[0] = {
306 value: state.value || 0
307 };
308 }
309
310 return keyFrames.map(function(frame) {
311 var value = frame;
312 /* istanbul ignore else */
313 if (frame !== null) {
314 // frames that are just numbers represent values
315 if (typeof frame === "number") {
316 frame = {
317 value: value,
318 };
319 } else {
320 if (typeof frame.brightness === "number") {
321 frame.value = frame.brightness;
322 delete frame.brightness;
323 }
324 if (typeof frame.intensity === "number") {
325 frame.value = Fn.scale(frame.intensity, 0, 100, 0, 255);
326 delete frame.intensity;
327 }
328 }
329
330 /* istanbul ignore else */
331 if (!frame.easing) {
332 frame.easing = "linear";
333 }
334 }
335 return frame;
336 });
337};
338
339/**
340 * Animation.render
341 *
342 * @position [number] value to set the led to
343 */
344
345Led.prototype[Animation.render] = function(position) {
346 var state = priv.get(this);
347 state.value = position[0];
348 return this.update();
349};
350
351/**
352 * pulse Fade the Led in and out in a loop with specified time
353 * @param {number} duration Time in ms that a fade in/out will elapse
354 * @return {Led}
355 *
356 * - or -
357 *
358 * @param {Object} val An Animation() segment config object
359 */
360
361Led.prototype.pulse = function(duration, callback) {
362 var state = priv.get(this);
363
364 this.stop();
365
366 var options = {
367 duration: typeof duration === "number" ? duration : 1000,
368 keyFrames: [0, 0xff],
369 metronomic: true,
370 loop: true,
371 easing: "inOutSine",
372 onloop: function() {
373 /* istanbul ignore else */
374 if (typeof callback === "function") {
375 callback();
376 }
377 }
378 };
379
380 if (typeof duration === "object") {
381 Object.assign(options, duration);
382 }
383
384 if (typeof duration === "function") {
385 callback = duration;
386 }
387
388 state.isRunning = true;
389
390 state.animation = state.animation || new Animation(this);
391 state.animation.enqueue(options);
392 return this;
393};
394
395/**
396 * fade Fade an led in and out
397 * @param {Number} val Analog brightness value 0-255
398 * @param {Number} duration Time in ms that a fade in/out will elapse
399 * @return {Led}
400 *
401 * - or -
402 *
403 * @param {Object} val An Animation() segment config object
404 */
405
406Led.prototype.fade = function(val, duration, callback) {
407
408 var state = priv.get(this);
409
410 this.stop();
411
412 var options = {
413 duration: typeof duration === "number" ? duration : 1000,
414 keyFrames: [null, typeof val === "number" ? val : 0xff],
415 easing: "outSine",
416 oncomplete: function() {
417 state.isRunning = false;
418 /* istanbul ignore else */
419 if (typeof callback === "function") {
420 callback();
421 }
422 }
423 };
424
425 if (typeof val === "object") {
426 Object.assign(options, val);
427 }
428
429 if (typeof val === "function") {
430 callback = val;
431 }
432
433 if (typeof duration === "object") {
434 Object.assign(options, duration);
435 }
436
437 if (typeof duration === "function") {
438 callback = duration;
439 }
440
441 state.isRunning = true;
442
443 state.animation = state.animation || new Animation(this);
444 state.animation.enqueue(options);
445
446 return this;
447};
448
449Led.prototype.fadeIn = function(duration, callback) {
450 return this.fade(255, duration || 1000, callback);
451};
452
453Led.prototype.fadeOut = function(duration, callback) {
454 return this.fade(0, duration || 1000, callback);
455};
456
457/**
458 * blink
459 * @param {Number} duration Time in ms on, time in ms off
460 * @return {Led}
461 */
462Led.prototype.blink = function(duration, callback) {
463 var state = priv.get(this);
464
465 // Avoid traffic jams
466 this.stop();
467
468 if (typeof duration === "function") {
469 callback = duration;
470 duration = null;
471 }
472
473 state.isRunning = true;
474
475 state.interval = setInterval(function() {
476 this.toggle();
477 if (typeof callback === "function") {
478 callback();
479 }
480 }.bind(this), duration || 100);
481
482 return this;
483};
484
485Led.prototype.strobe = Led.prototype.blink;
486
487/**
488 * stop Stop the led from strobing, pulsing or fading
489 * @return {Led}
490 */
491Led.prototype.stop = function() {
492 var state = priv.get(this);
493
494 if (state.interval) {
495 clearInterval(state.interval);
496 }
497
498 if (state.animation) {
499 state.animation.stop();
500 }
501
502 state.interval = null;
503 state.isRunning = false;
504
505 return this;
506};
507
508/* istanbul ignore else */
509if (!!process.env.IS_TEST_MODE) {
510 Led.Controllers = Controllers;
511 Led.purge = function() {
512 priv.clear();
513 };
514}
515
516
517module.exports = Led;