UNPKG

johnny-five

Version:

The JavaScript Robotics and Hardware Programming Framework. Use with: Arduino (all models), Electric Imp, Beagle Bone, Intel Galileo & Edison, Linino One, Pinoccio, pcDuino3, Raspberry Pi, Particle/Spark Core & Photon, Tessel 2, TI Launchpad and more!

519 lines (425 loc) 10.9 kB
var IS_TEST_MODE = !!process.env.IS_TEST_MODE; var Board = require("../board"); var Animation = require("../animation"); var Expander = require("../expander"); var Fn = require("../fn"); var Pins = Board.Pins; var priv = new Map(); var Controllers = { PCA9685: { initialize: { value: function(opts) { var state = priv.get(this); this.address = opts.address || 0x40; this.pwmRange = opts.pwmRange || [0, 4095]; this.frequency = opts.frequency || 200; state.expander = Expander.get({ address: this.address, controller: this.controller, bus: this.bus, pwmRange: this.pwmRange, frequency: this.frequency, }); this.pin = state.expander.normalize(opts.pin); state.mode = this.io.MODES.PWM; } }, update: { writable: true, value: function(input) { var state = priv.get(this); var output = typeof input !== "undefined" ? input : state.value; var value = state.isAnode ? 255 - Board.constrain(output, 0, 255) : output; this.write(value); } }, write: { writable: true, value: function(value) { var state = priv.get(this); state.expander.analogWrite(this.pin, value); } } }, DEFAULT: { initialize: { value: function(opts, pinValue) { var state = priv.get(this); var isFirmata = true; var defaultLed; isFirmata = Pins.isFirmata(this); if (isFirmata && typeof pinValue === "string" && pinValue[0] === "A") { pinValue = this.io.analogPins[+pinValue.slice(1)]; } defaultLed = this.io.defaultLed || 13; pinValue = +pinValue; if (isFirmata && this.io.analogPins.includes(pinValue)) { this.pin = pinValue; state.mode = this.io.MODES.OUTPUT; } else { this.pin = typeof opts.pin === "undefined" ? defaultLed : opts.pin; state.mode = this.io.MODES[ (this.board.pins.isPwm(this.pin) ? "PWM" : "OUTPUT") ]; } this.io.pinMode(this.pin, state.mode); } }, update: { writable: true, value: function(input) { var state = priv.get(this); var output = typeof input !== "undefined" ? input : state.value; var value = state.isAnode ? 255 - Board.constrain(output, 0, 255) : output; // If pin is not a PWM pin and brightness is not HIGH or LOW, emit an error if (value !== this.io.LOW && value !== this.io.HIGH && this.mode !== this.io.MODES.PWM) { Board.Pins.Error({ pin: this.pin, type: "PWM", via: "Led" }); } if (state.mode === this.io.MODES.OUTPUT) { value = output; } this.write(value); } }, write: { writable: true, value: function(value) { var state = priv.get(this); if (state.mode === this.io.MODES.OUTPUT) { this.io.digitalWrite(this.pin, value); } if (state.mode === this.io.MODES.PWM) { this.io.analogWrite(this.pin, value); } } } } }; /** * Led * @constructor * * five.Led(pin); * * five.Led({ * pin: number * }); * * * @param {Object} opts [description] * */ function Led(opts) { if (!(this instanceof Led)) { return new Led(opts); } var pinValue = typeof opts === "object" ? opts.pin : opts; var controller = null; Board.Component.call( this, opts = Board.Options(opts) ); if (opts.controller && typeof opts.controller === "string") { controller = Controllers[opts.controller.toUpperCase()]; } else { controller = opts.controller; } if (controller == null) { controller = Controllers.DEFAULT; } Object.defineProperties(this, controller); var state = { isAnode: opts.isAnode, isOn: false, isRunning: false, value: null, direction: 1, mode: null, intensity: 0, interval: null }; priv.set(this, state); Object.defineProperties(this, { value: { get: function() { return state.value; } }, mode: { get: function() { return state.mode; } }, isOn: { get: function() { return !!state.value; } }, isRunning: { get: function() { return state.isRunning; } }, animation: { get: function() { return state.animation; } } }); /* istanbul ignore else */ if (typeof this.initialize === "function") { this.initialize(opts, pinValue); } } /** * on Turn the led on * @return {Led} */ Led.prototype.on = function() { var state = priv.get(this); if (state.mode === this.io.MODES.OUTPUT) { state.value = this.io.HIGH; } if (state.mode === this.io.MODES.PWM) { // Assume we need to simply turn this all the way on, when: // ...state.value is null if (state.value === null) { state.value = 255; } // ...there is no active interval if (!state.interval) { state.value = 255; } // ...the last value was 0 if (state.value === 0) { state.value = 255; } } this.update(); return this; }; /** * off Turn the led off * @return {Led} */ Led.prototype.off = function() { var state = priv.get(this); state.value = 0; this.update(); return this; }; /** * toggle Toggle the on/off state of an led * @return {Led} */ Led.prototype.toggle = function() { return this[this.isOn ? "off" : "on"](); }; /** * brightness * @param {Number} value analog brightness value 0-255 * @return {Led} */ Led.prototype.brightness = function(brightness) { var state = priv.get(this); state.value = brightness; this.update(); return this; }; /** * intensity * @param {Number} value Light intensity 0-100 * @return {Led} */ Led.prototype.intensity = function(intensity) { var state = priv.get(this); if (arguments.length === 0) { return state.intensity; } state.intensity = Fn.constrain(intensity, 0, 100); return this.brightness(Fn.scale(state.intensity, 0, 100, 0, 255)); }; /** * Animation.normalize * * @param [number || object] keyFrames An array of step values or a keyFrame objects */ Led.prototype[Animation.normalize] = function(keyFrames) { var state = priv.get(this); // If user passes null as the first element in keyFrames use current value /* istanbul ignore else */ if (keyFrames[0] === null) { keyFrames[0] = { value: state.value || 0 }; } return keyFrames.map(function(frame) { var value = frame; /* istanbul ignore else */ if (frame !== null) { // frames that are just numbers represent values if (typeof frame === "number") { frame = { value: value, }; } else { if (typeof frame.brightness === "number") { frame.value = frame.brightness; delete frame.brightness; } if (typeof frame.intensity === "number") { frame.value = Fn.scale(frame.intensity, 0, 100, 0, 255); delete frame.intensity; } } /* istanbul ignore else */ if (!frame.easing) { frame.easing = "linear"; } } return frame; }); }; /** * Animation.render * * @position [number] value to set the led to */ Led.prototype[Animation.render] = function(position) { var state = priv.get(this); state.value = position[0]; return this.update(); }; /** * pulse Fade the Led in and out in a loop with specified time * @param {number} duration Time in ms that a fade in/out will elapse * @return {Led} * * - or - * * @param {Object} val An Animation() segment config object */ Led.prototype.pulse = function(duration, callback) { var state = priv.get(this); this.stop(); var options = { duration: typeof duration === "number" ? duration : 1000, keyFrames: [0, 0xff], metronomic: true, loop: true, easing: "inOutSine", onloop: function() { /* istanbul ignore else */ if (typeof callback === "function") { callback(); } } }; if (typeof duration === "object") { Object.assign(options, duration); } if (typeof duration === "function") { callback = duration; } state.isRunning = true; state.animation = state.animation || new Animation(this); state.animation.enqueue(options); return this; }; /** * fade Fade an led in and out * @param {Number} val Analog brightness value 0-255 * @param {Number} duration Time in ms that a fade in/out will elapse * @return {Led} * * - or - * * @param {Object} val An Animation() segment config object */ Led.prototype.fade = function(val, duration, callback) { var state = priv.get(this); this.stop(); var options = { duration: typeof duration === "number" ? duration : 1000, keyFrames: [null, typeof val === "number" ? val : 0xff], easing: "outSine", oncomplete: function() { state.isRunning = false; /* istanbul ignore else */ if (typeof callback === "function") { callback(); } } }; if (typeof val === "object") { Object.assign(options, val); } if (typeof val === "function") { callback = val; } if (typeof duration === "object") { Object.assign(options, duration); } if (typeof duration === "function") { callback = duration; } state.isRunning = true; state.animation = state.animation || new Animation(this); state.animation.enqueue(options); return this; }; Led.prototype.fadeIn = function(duration, callback) { return this.fade(255, duration || 1000, callback); }; Led.prototype.fadeOut = function(duration, callback) { return this.fade(0, duration || 1000, callback); }; /** * blink * @param {Number} duration Time in ms on, time in ms off * @return {Led} */ Led.prototype.blink = function(duration, callback) { var state = priv.get(this); // Avoid traffic jams this.stop(); if (typeof duration === "function") { callback = duration; duration = null; } state.isRunning = true; state.interval = setInterval(function() { this.toggle(); if (typeof callback === "function") { callback(); } }.bind(this), duration || 100); return this; }; Led.prototype.strobe = Led.prototype.blink; /** * stop Stop the led from strobing, pulsing or fading * @return {Led} */ Led.prototype.stop = function() { var state = priv.get(this); if (state.interval) { clearInterval(state.interval); } if (state.animation) { state.animation.stop(); } state.interval = null; state.isRunning = false; return this; }; /* istanbul ignore else */ if (IS_TEST_MODE) { Led.purge = function() { priv.clear(); }; } module.exports = Led;