UNPKG

johnny-five

Version:

The JavaScript Arduino Programming Framework.

467 lines (340 loc) 8.92 kB
var Board = require("../lib/board.js"), events = require("events"), util = require("util"), __ = require("../lib/fn.js"); var priv = new Map(), Devices, characters; /** * This atrocitity is unfortunately necessary. * If any other approach can be found, patches * will gratefully be accepted. */ function sleep(ms) { var start = Date.now(); while (Date.now() < start + ms) {} } /** * LCD * @param {[type]} opts [description] */ function LCD(opts) { if (!(this instanceof LCD)) { return new LCD(opts); } // Initialize a Device instance on a Board Board.Device.call( this, opts = Board.Options(opts) ); var display = LCD.DISPLAYCONTROL | LCD.DISPLAYON; // options this.bitMode = opts.bitMode || 4; this.lines = opts.lines || 2; this.rows = opts.rows || 2; this.cols = opts.cols || 16; this.dots = opts.dots || "5x8"; this.pins = { rs: opts.pins[0], en: opts.pins[1], // TODO: Move to device map profile data: [ opts.pins[5], opts.pins[4], opts.pins[3], opts.pins[2] ] }; priv.set(this, { display: display, characters: {}, index: LCD.MEMORYLIMIT - 1 }); opts.pins.forEach(function(pin) { this.board.pinMode(pin, 1); }, this); // TODO: ALL OF THIS NEEDS TO BE RE-WRITTEN // // // RS to low (for command mode), EN to low to start // TODO: Move to device map profile this.board.digitalWrite(this.pins.rs, this.io.LOW); this.board.digitalWrite(this.pins.en, this.io.LOW); // Wait 50ms before initializing to make sure LCD is powered up // TODO: Don't use sleep setTimeout(function() { // Send 0011 thrice to make sure LCD is initialized properly this.writeBits(0x03); sleep(4); this.writeBits(0x03); sleep(4); this.writeBits(0x03); // Switch to 4-bit mode // TODO: Move to device map profile if (this.bitMode === 4) { this.writeBits(0x02); } // Set number of lines and dots // TODO: Move to device map profile this.command( LCD.FUNCTIONSET | LCD.LINE[this.lines] | LCD.DOTS[this.dots] ); // Clear display and turn it on this.command(display); this.clear(); this.home(); this.emit.call(this, "ready", null); }.bind(this), 50); } util.inherits(LCD, events.EventEmitter); // |this| sensitive, must be called as // LCD.hilo.call( lcd, callback ); LCD.hilo = function(callback) { if (!(this instanceof LCD)) { throw new Error("Cannot toggle a non-LCD instance"); } // RS High for write mode this.board.digitalWrite(this.pins.rs, this.io.HIGH); // Callback wrapped write this.command( charCode );functionality callback.call(this); // RS Low for command mode this.board.digitalWrite(this.pins.rs, this.io.LOW); }; LCD.prototype.pulse = function() { ["LOW", "HIGH", "LOW"].forEach(function(val, i) { this.board.digitalWrite(this.pins.en, this.io[val]); }, this); return this; }; LCD.prototype.writeBits = function(value) { var pin, mask; pin = 0; mask = { 4: 8, 8: 128 }[this.bitMode]; for (; mask > 0; mask = mask >> 1) { this.board.digitalWrite( this.pins.data[pin], this.io[value & mask ? "HIGH" : "LOW"] ); pin++; } this.pulse(); return this; }; LCD.prototype.command = function(command, callback) { if (this.bitMode === 4) { this.writeBits(command >> 4); } this.writeBits(command); if (callback) { process.nextTick(callback); } return this; }; var RE_SPECIALS = /:(\w+):/g; LCD.prototype.print = function(message, opts) { var state, dontProcessSpecials, hasCharacters, processed; message = message + ""; opts = opts || {}; state = priv.get(this); dontProcessSpecials = opts.dontProcessSpecials || false; hasCharacters = !dontProcessSpecials && RE_SPECIALS.test(message); if (message.length === 1) { LCD.hilo.call(this, function() { this.command(message.charCodeAt(0)); }); } else { if (hasCharacters) { processed = message.replace(RE_SPECIALS, function(match, name) { var address = state.characters[name]; return typeof address === "number" ? String.fromCharCode(address) : match; }); this.print(processed, { dontProcessSpecials: true }); } else { LCD.hilo.call(this, function() { var k, chars, char; k = -1; chars = [].slice.call(message); while ((char = chars[++k])) { this.command(char.charCodeAt(0)); } }); } } return this; }; LCD.prototype.write = function(charCode) { LCD.hilo.call(this, function() { this.command(charCode); }); return this; }; LCD.prototype.clear = function() { this.command(LCD.CLEARDISPLAY); return this; }; LCD.prototype.home = function() { this.command(LCD.RETURNHOME); sleep(2); // Command can be slow return this; }; LCD.prototype.setCursor = function(col, row) { var rowOffsets = [0x00, 0x40, 0x14, 0x54]; this.command(LCD.SETDDRAMADDR | (col + rowOffsets[row])); return this; }; LCD.prototype.display = function() { var state = priv.get(this); state.display |= LCD.DISPLAYON; this.command(state.display); return this; }; LCD.prototype.noDisplay = function() { var state = priv.get(this); state.display &= ~LCD.DISPLAYON; this.command(state.display); return this; }; LCD.prototype.cursor = function(row, col) { // When provided with col & row, cursor will behave like setCursor if (typeof col !== "undefined" && typeof row !== "undefined") { return this.setCursor(col, row); } var state = priv.get(this); state.display |= LCD.CURSORON; this.command(state.display); return this; }; LCD.prototype.noCursor = function() { var state = priv.get(this); state.display &= ~LCD.CURSORON; this.command(state.display); return this; }; LCD.prototype.blink = function() { var state = priv.get(this); state.display |= LCD.BLINKON; this.command(state.display); return this; }; LCD.prototype.noBlink = function() { var state = priv.get(this); state.display &= ~LCD.BLINKON; this.command(state.display); return this; }; LCD.prototype.autoscroll = function() { var state = priv.get(this); state.display |= LCD.ENTRYSHIFTINCREMENT; this.command(LCD.ENTRYMODESET | state.display); return this; }; LCD.prototype.noAutoscroll = function() { var state = priv.get(this); state.display &= ~LCD.ENTRYSHIFTINCREMENT; this.command(LCD.ENTRYMODESET | state.display); return this; }; LCD.prototype.createChar = function(name, charMap) { // Ensure location is never above 7 var state, address; state = priv.get(this); if (typeof name === "number") { address = name & 0x07; } else { address = state.index; state.index--; if (state.index === -1) { state.index = LCD.MEMORYLIMIT - 1; } } this.command(LCD.SETCGRAMADDR | (address << 3)); LCD.hilo.call(this, function() { var i; for (i = 0; i < 8; i++) { this.command(charMap[i]); } }); // Fill in address state.characters[name] = address; return address; }; LCD.prototype.useChar = function(name) { var state; // Derive the current private state cache state = priv.get(this); if (!state.characters[name]) { // Create the character in LCD memory and // Add character to current LCD character map state.characters[name] = this.createChar(name, LCD.Characters[name]); } return state.characters[name]; }; LCD.Characters = require("../lib/lcd-chars.js"); // Create custom characters: // http://www.darreltaylor.com/files/CustChar.htm // createChar resources: // http://www.hackmeister.dk/2010/08/custom-lcd-characters-with-arduino/ // /** * TODO: burst() scrollDisplayLeft() scrollDisplayRight() leftToRight() rightToLeft() */ // commands LCD.CLEARDISPLAY = 0x01; LCD.RETURNHOME = 0x02; LCD.ENTRYMODESET = 0x04; LCD.DISPLAYCONTROL = 0x08; LCD.CURSORSHIFT = 0x10; LCD.FUNCTIONSET = 0x20; LCD.SETCGRAMADDR = 0x40; LCD.SETDDRAMADDR = 0x80; // flags for display entry mode LCD.ENTRYRIGHT = 0x00; LCD.ENTRYLEFT = 0x02; LCD.ENTRYSHIFTINCREMENT = 0x01; LCD.ENTRYSHIFTDECREMENT = 0x00; // flags for display on/off control LCD.DISPLAYON = 0x04; LCD.DISPLAYOFF = 0x00; LCD.CURSORON = 0x02; LCD.CURSOROFF = 0x00; LCD.BLINKON = 0x01; LCD.BLINKOFF = 0x00; // flags for display/cursor shift LCD.DISPLAYMOVE = 0x08; LCD.CURSORMOVE = 0x00; LCD.MOVERIGHT = 0x04; LCD.MOVELEFT = 0x00; // flags for function set LCD.BITMODE = []; LCD.BITMODE[4] = 0x00; LCD.BITMODE[8] = 0x10; // 4 & 8 LCD.LINE = []; LCD.LINE[1] = 0x00; LCD.LINE[2] = 0x08; // TODO: Support for >2 lines // 1 & 2 LCD.DOTS = { "5x10": 0x04, "5x8": 0x00 }; // flags for backlight control LCD.BACKLIGHT = { ON: 0x08, OFF: 0x00 }; LCD.MEMORYLIMIT = 8; module.exports = LCD; // http://www.arduino.cc/playground/Code/LCDAPI