UNPKG

johnny-five

Version:

The JavaScript Arduino Programming Framework.

607 lines (490 loc) 11.8 kB
var Board = require("../lib/board.js"); var priv = new WeakMap(), LEDS = []; /** * Led * @constructor * * five.Led(pin); * * five.Led({ * pin: number * }); * * * @param {Object} opts [description] * */ function Led(opts) { var pinValue; if (!(this instanceof Led)) { return new Led(opts); } pinValue = typeof opts === "object" ? opts.pin : opts; // Initialize a Device instance on a Board Board.Device.call( this, opts = Board.Options(opts) ); if (typeof pinValue === "string" && pinValue[0] === "A") { pinValue = this.io.analogPins[+pinValue.slice(1)]; } pinValue = +pinValue; // LED instance properties this.value = 0; this.interval = null; // TODO: use pin capability checks for LED value writing. // Create a "state" entry for privately // storing the state of the led LEDS.push(this); priv.set(this, { isOn: false, isRunning: false, value: null, direction: 1, mode: null }); Object.defineProperties(this, { value: { get: function() { return priv.get(this).value; } }, isOn: { get: function() { return priv.get(this).isOn; } }, isRunning: { get: function() { return priv.get(this).isRunning; } }, mode: { set: function(mode) { var state = priv.get(this); // set mode // TODO: if setting to PWM, check if this pin is capable of PWM // log error if not capable if (state.mode !== mode) { state.mode = mode; this.io.pinMode(this.pin, mode); } }, get: function() { return priv.get(this).mode; } } }); if (this.io.analogPins.indexOf(pinValue) !== -1) { this.pin = pinValue; this.mode = this.io.MODES.OUTPUT; } else { this.pin = typeof opts.pin === "undefined" ? 9 : opts.pin; this.mode = this.io.MODES[ opts.type && opts.type.toUpperCase() || (this.board.pins.isPwm(this.pin) ? "PWM" : "OUTPUT") ]; } } /** * on Turn the led on * @return {Led} */ Led.prototype.on = function() { var state = priv.get(this); if (state.mode === this.io.MODES.OUTPUT) { this.io.digitalWrite(this.pin, this.io.HIGH); } if (state.mode === this.io.MODES.PWM) { // If there is no active interval, and state.value is null // then assume we need to simply turn this all the way on. if (!this.interval && state.value === null) { state.value = 255; } this.io.analogWrite(this.pin, state.value); } state.isOn = true; return this; }; /** * off Turn the led off * @return {Led} */ Led.prototype.off = function() { var state = priv.get(this); if (state.mode === this.io.MODES.OUTPUT) { this.io.digitalWrite(this.pin, this.io.LOW); } if (state.mode === this.io.MODES.PWM) { this.io.analogWrite(this.pin, 0); } state.isOn = false; return this; }; /** * toggle Toggle the on/off state of an led * @return {Led} */ Led.prototype.toggle = function() { var state = priv.get(this); if (state.isOn) { this.off(); } else { this.on(); } return this; }; /** * brightness * @param {Number} value analog brightness value 0-255 * @return {Led} */ Led.prototype.brightness = function(value) { var state = priv.get(this); // If pin is not a PWM pin, emit an error if (!this.board.pins.isPwm(this.pin)) { Board.Pins.Error({ pin: this.pin, type: "PWM", via: "Led", }); } // Reset mode to PWM this.mode = this.io.MODES.PWM; this.io.analogWrite(this.pin, value); state.value = value; return this; }; /** * pulse Fade the Led in and out in a loop with specified time * @param {number} rate Time in ms that a fade in/out will elapse * @return {Led} */ Led.prototype.pulse = function(time) { var direction, to, state; to = (time || 1000) / (255 * 2); state = priv.get(this); if (!this.board.pins.isPwm(this.pin)) { Board.Pins.Error({ pin: this.pin, type: "PWM", via: "Led", }); } // Avoid traffic jams when pulse() is called // more then once on the same instance, with no // calls to stop() if (this.interval) { clearInterval(this.interval); // Use the previous direction direction = state.direction; } // Ensure mode is PWM this.mode = this.io.MODES.PWM; state.isOn = true; state.isRunning = true; this.interval = setInterval(function() { // Ensure the current value is at least the // number 0 (it may be null or 0) var valueAt = this.value || 0; // If state.isOn is true, then change // the visible state of the LED if (state.isOn) { if (valueAt === 0) { direction = 1; } if (valueAt === 255) { direction = -1; } this.io.analogWrite( this.pin, valueAt + direction ); state.value = valueAt + direction; state.direction = direction; } }.bind(this), to); return this; }; /** * fade Fade an led in and out * @param {Number} val Analog brightness value 0-255 * @param {Number} time Time in ms that a fade in/out will elapse * @return {Led} */ Led.prototype.fade = function(val, time) { var direction, to, state; direction = this.value <= val ? 1 : -1; to = (time || 1000) / ((val || 255) * 2); state = priv.get(this); if (!this.board.pins.isPwm(this.pin)) { Board.Pins.Error({ pin: this.pin, type: "PWM", via: "Led", }); } // Avoid traffic jams if (this.interval) { clearInterval(this.interval); } // Reset mode to PWM this.mode = this.io.MODES.PWM; state.isOn = true; this.interval = setInterval(function() { var valueAt = this.value; // If state.isOn is true, then change // the visible state of the LED if (state.isOn) { if ((direction > 0 && valueAt === 255) || (direction < 0 && valueAt === 0) || valueAt === val) { this.stop(); } else { this.io.analogWrite( this.pin, valueAt + direction ); state.value = valueAt + direction; state.direction = direction; } } }.bind(this), to); return this; }; Led.prototype.fadeIn = function(time) { return this.fade(255, time || 1000); }; Led.prototype.fadeOut = function(time) { return this.fade(0, time || 1000); }; /** * strobe * @param {Number} rate Time in ms to strobe/blink * @return {Led} */ Led.prototype.strobe = function(rate) { var isHigh, state; isHigh = false; state = priv.get(this); // Avoid traffic jams if (this.interval) { clearInterval(this.interval); } // Reset mode to OUTPUT this.mode = this.io.MODES.OUTPUT; state.isOn = true; state.isRunning = true; state.value = this.value; this.interval = setInterval(function() { // If state.isOn is true, then change // the visible state of the LED if (state.isOn) { if (isHigh) { this.io.digitalWrite( this.pin, this.io.LOW ); } else { this.io.digitalWrite( this.pin, this.io.HIGH ); } isHigh = !isHigh; } }.bind(this), rate || 100); return this; }; Led.prototype.blink = Led.prototype.strobe; /** * stop Stop the led from strobing, pulsing or fading * @return {Led} */ Led.prototype.stop = function() { var state = priv.get(this); clearInterval(this.interval); state.isOn = false; state.isRunning = false; state.value = this.value; return this; }; // TODO: // Led.prototype.color = function() { // ... // return this; // }; /** * Led.Array() * new Led.Array() * * Create an Array-like object instance of LEDS * * @return {Led.Array} */ Led.Array = function(pins) { if (!(this instanceof Led.Array)) { return new Led.Array(pins); } var leds = []; if (pins) { while (pins.length) { leds.push( new Led(pins.shift()) ); } } else { leds = LEDS.slice(); } this.length = 0; leds.forEach(function(led, index) { this[index] = led; this.length++; }, this); }; /** * each Execute callbackFn for each active led instance in an Led.Array * @param {Function} callbackFn * @return {Led.Array} */ Led.Array.prototype.each = function(callbackFn) { var led, i, length; length = this.length; for (i = 0; i < length; i++) { led = this[i]; callbackFn.call(led, led, i); } return this; }; [ "on", "off", "toggle", "brightness", "fade", "fadeIn", "fadeOut", "pulse", "strobe", "stop" ].forEach(function(method) { // Create Led.Array wrappers for each method listed. // This will allow us control over all Led instances // simultaneously. Led.Array.prototype[method] = function() { var args = [].slice.call(arguments); this.each(function(led) { Led.prototype[method].apply(led, args); }); return this; }; }); /** * Led.RGB * * * @param {[type]} opts [description] * @return {[type]} [description] */ Led.RGB = function(opts) { if (!(this instanceof Led.RGB)) { return new Led.RGB(opts); } // Initialize a Device instance on a Board Board.Device.call( this, opts = Board.Options(opts) ); var color, colors, k; colors = Led.RGB.colors.slice(); k = -1; // This will normalize an array of pins in [ r, g, b ] // order to an object that's shaped like: // { // red: r, // green: g, // blue: b // } if (Array.isArray(opts.pins)) { opts.pins = colors.reduce(function(pins, pin, i, list) { return (pins[list[i]] = opts.pins[i], pins); }, {}); } // Initialize each Led instance while (colors.length) { color = colors.shift(); this[color] = new Led({ pin: opts.pins[color], board: opts.board }); } priv.set(this, { red: 0, green: 0, blue: 0 }); }; Led.RGB.colors = ["red", "green", "blue"]; /** * color * * @param {String} color Hexadecimal color string * @param {Array} color Array of color values * * @return {Led.RGB} */ Led.RGB.prototype.color = function(value) { var state, update; state = priv.get(this); update = { red: 0, green: 0, blue: 0 }; if (!value) { // Return a "copy" of the state values, // not a reference to the state object itself. return Led.RGB.colors.reduce(function(current, color) { return (current[color] = state[color], current); }, {}); } // Allows hex colors with leading #: // eg. #ff00ff if (value[0] === "#") { value = value.slice(1); } if (typeof value === "string") { update.red = parseInt(value.slice(0, 2), 16); update.green = parseInt(value.slice(2, 4), 16); update.blue = parseInt(value.slice(4, 6), 16); } else { update.red = value[0]; update.green = value[1]; update.blue = value[2]; } Led.RGB.colors.forEach(function(color) { state[color] = update[color]; this[color].brightness(update[color]); }, this); return this; }; Led.RGB.prototype.on = function() { var state = priv.get(this); Led.RGB.colors.forEach(function(color) { this[color].on(); this[color].brightness( state[color] !== 0 ? state[color] : 255 ); }, this); }; Led.RGB.prototype.off = function() { Led.RGB.colors.forEach(function(color) { this[color].off(); }, this); }; [ "toggle", "brightness", "fade", "fadeIn", "fadeOut", "pulse", "strobe", "stop" ].forEach(function(method) { // Create Led.Array wrappers for each method listed. // This will allow us control over all Led instances // simultaneously. Led.RGB.prototype[method] = function() { var args = [].slice.call(arguments); Led.RGB.colors.forEach(function(color) { this[color][method](args); }, this); return this; }; }); module.exports = Led;