Skip to content

Commit

Permalink
Merge pull request #31 from natevw/master
Browse files Browse the repository at this point in the history
LCD support, misc. other fixes
  • Loading branch information
Cam Pedersen committed Jul 14, 2013
2 parents a99a514 + 7d7ef3c commit a3822af
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 5 deletions.
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,67 @@ Fade the to full brightness then back to minimal brightness in `interval` ms. De

Current brightness of the LED

##lcd

This is a port of the [LiquidCrystal library](http://arduino.cc/en/Reference/LiquidCrystal) into JavaScript. Note that communicating with the LCD requires use of the synchronous `board.delay()` busy loop which will block other node.js events from being processed for several milliseconds at a time. (This could be converted to pause a board-level buffered message queue instead.)

````javascript
var lcd = new d.LCD({
board: board,
pins: {rs:12, rw:11, e:10, data:[5, 4, 3, 2]}
});
lcd.begin(16, 2);
lcd.print("Hello Internet.");
````

In `options`, the "pins" field can either be an array matching a call to any of the [LiquidCrystal constructors](http://arduino.cc/en/Reference/LiquidCrystalConstructor) or an object with "rs", "rw" (optional), "e" and a 4- or 8-long array of "data" pins. Pins will default to `[12, 11, 5, 4, 3, 2]` if not provided.

###lcd.begin(), lcd.clear(), lcd.home(), lcd.setCursor(), lcd.scrollDisplayLeft(), lcd.scrollDisplayRight()

These should behave the same as their counterparts in the [LiquidCrystal library](http://arduino.cc/en/Reference/LiquidCrystal).

###lcd.display(on), lcd.cursor(on), lcd.blink(on), lcd.autoscroll(on)

These are similar to the methods in the [LiquidCrystal library](http://arduino.cc/en/Reference/LiquidCrystal), however they can take an optional boolean parameter. If true or not provided, the setting is enabled. If false, the setting is disabled. For compatibility `.noDisplay()`, `.noCursor()`, `.noBlink()` and `.noAutoscroll()` methods are provided as well.

###lcd.write(val), lcd.print(val)

These take a buffer, string or integer and send it to the display. The `.write` and `print` methods are equivalent, aliases to the same function.

###lcd.createChar(location, charmap)

Configures a custom character for code `location` (numbers 0–7). `charmap` can be a 40-byte buffer as in [the C++ method](http://arduino.cc/en/Reference/LiquidCrystalCreateChar), or an array of 5-bit binary strings, or a 40-character string with pixels denoted by any non-space (`' '`) character. These bits determine the 5x8 pixel pattern of the custom character.

````javascript
var square = new Buffer("1f1f1f1f1f1f1f1f", 'hex');

var smiley = [
'00000',
'10001',
'00000',
'00000',
'10001',
'01110',
'00000'
];

var random =
". .." +
" . . " +
". . ." +
" . . " +
" .. " +
". . " +
" . ." +
".. .." ;

lcd.createChar(0, square);
lcd.createChar(1, smiley);
lcd.createChar(2, random);
lcd.setCursor(5,2);
lcd.print(new Buffer("\0\1\2\1\0")); // NOTE: when `.print`ing a string, 'ascii' turns \0 into a space
````

##piezo

````javascript
Expand Down Expand Up @@ -164,6 +225,20 @@ setInterval(function(){
}, 1000);
````

##ping

See: <http://arduino.cc/en/Tutorial/Ping>

````javascript
var range = new arduino.Ping({
board: board
});

range.on('read', function () {
console.log("Distance to target (cm)", range.centimeters);
});
````

##servo

````javascript
Expand Down Expand Up @@ -213,6 +288,8 @@ What is implemented right now:
* `02` digitalRead
* `03` analogWrite
* `04` analogRead
* `97` ping
* `98` servo
* `99` debug

##pin
Expand Down
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ module.exports = {
Servo: require('./lib/servo'),
Sensor: require('./lib/sensor'),
Ping: require('./lib/ping'),
PIR: require('./lib/pir')
PIR: require('./lib/pir'),
LCD: require('./lib/lcd')
};
254 changes: 254 additions & 0 deletions lib/lcd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
// port of LiquidCrystal.cpp by natevw


// 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_8BITMODE = 0x10;
LCD_4BITMODE = 0x00;
LCD_2LINE = 0x08;
LCD_1LINE = 0x00;
LCD_5x10DOTS = 0x04;
LCD_5x8DOTS = 0x00;



var LCD = function (options) {
if (!options || !options.board) throw new Error('Must supply required options');
this.board = options.board;

var pins = options.pins || [12, 11, 5, 4, 3, 2];
if (!Array.isArray(pins))
this.pins = pins;
else if (pins.length % 2)
this.pins = {rs:pins[0], rw:pins[1], e:pins[2], data:pins.slice(3)};
else
this.pins = {rs:pins[0], e:pins[1], data:pins.slice(2)};
if (!('rw' in this.pins)) this.pins.rw = 255;

this.board.pinMode(this.pins.rs, 'out');
if (this.pins.rw !== 255) {
this.board.pinMode(this.pins.rw, 'out');
}
this.board.pinMode(this.pins.e, 'out');

this.begin(16, 1);
}

LCD.prototype.begin = function (cols, lines, dotsize) {
this._numlines = lines;

var displayfunction = 0;
displayfunction |= (lines > 1) ? LCD_2LINE : LCD_1LINE;
displayfunction |= (dotsize && lines === 1) ? LCD_5x10DOTS : LCD_5x8DOTS;

this._delayMicroseconds(50000);
this.board.digitalWrite(this.pins.rs, this.board.LOW);
this.board.digitalWrite(this.pins.e, this.board.LOW);
if (this.pins.rw !== 255)
this.board.digitalWrite(this.pins.rw, this.board.LOW);

// put the LCD into 4 bit or 8 bit mode
if (this.pins.data.length === 4) {
displayfunction |= LCD_4BITMODE;
this._writeNbits(4, 0x03);
this._delayMicroseconds(4500);
this._writeNbits(4, 0x03);
this._delayMicroseconds(4500);
this._writeNbits(4, 0x03);
this._delayMicroseconds(150);
this._writeNbits(4, 0x02);
} else {
displayfunction |= LCD_8BITMODE;
this.command(LCD_FUNCTIONSET | displayfunction);
this._delayMicroseconds(4500);
this.command(LCD_FUNCTIONSET | displayfunction);
this._delayMicroseconds(150);
this.command(LCD_FUNCTIONSET | displayfunction);
}

this.command(LCD_FUNCTIONSET | displayfunction);

this._displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
this.display();

this.clear();

this._displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
this.leftToRight();
}

LCD.prototype.clear = function () {
this.command(LCD_CLEARDISPLAY);
this._delayMicroseconds(2000); // this command takes a long time!
}

LCD.prototype.home = function () {
this.command(LCD_RETURNHOME);
this._delayMicroseconds(2000);
}

LCD.prototype.setCursor = function (col, row) {
if (row >= this._numlines) {
row = this._numlines - 1;
}

var row_offsets = [0x00, 0x40, 0x14, 0x54];
this.command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}

LCD.prototype.display = function (on) {
on = (arguments.length) ? on : true;
if (on) this._displaycontrol |= LCD_DISPLAYON
else this._displaycontrol &= ~LCD_DISPLAYON;
this.command(LCD_DISPLAYCONTROL | this._displaycontrol);
}

LCD.prototype.noDisplay = function () { this.display(false); }

LCD.prototype.cursor = function (on) {
on = (arguments.length) ? on : true;
if (on) this._displaycontrol |= LCD_CURSORON
else this._displaycontrol &= ~LCD_CURSORON;
this.command(LCD_DISPLAYCONTROL | this._displaycontrol);
}

LCD.prototype.noCursor = function () { this.cursor(false); }

LCD.prototype.blink = function (on) {
on = (arguments.length) ? on : true;
if (on) this._displaycontrol |= LCD_BLINKON
else this._displaycontrol &= ~LCD_BLINKON;
this.command(LCD_DISPLAYCONTROL | this._displaycontrol);
}

LCD.prototype.noBlink = function () { this.blink(false); }

LCD.prototype.scrollDisplayLeft = function () {
this.command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}

LCD.prototype.scrollDisplayRight = function () {
this.command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}


LCD.prototype.leftToRight = function () {
this._displaymode |= LCD_ENTRYLEFT;
this.command(LCD_ENTRYMODESET | this._displaymode);
}

LCD.prototype.rightToLeft = function () {
this._displaymode &= ~LCD_ENTRYLEFT;
this.command(LCD_ENTRYMODESET | this._displaymode);
}

LCD.prototype.autoscroll = function (on) {
on = (arguments.length) ? on : true;
if (on) this._displaymode |= LCD_ENTRYSHIFTINCREMENT
else this._displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
this.command(LCD_ENTRYMODESET | this._displaymode);
}

LCD.prototype.noAutoscroll = function () { this.autoscroll(false); }

LCD.prototype.createChar = function (location, charmap) {
location &= 0x7;
this.command(LCD_SETCGRAMADDR | (location << 3));

var buffer = new Buffer(8);
if (Array.isArray(charmap)) for (var i = 0; i < 8; i++) {
buffer[i] = parseInt(charmap[i], 2);
} else if (typeof charmap === 'string') for (var i = 0; i < 8; i++) {
var byte = 0;
if (charmap[5*i + 0] !== ' ') byte |= 0x10;
if (charmap[5*i + 1] !== ' ') byte |= 0x08;
if (charmap[5*i + 2] !== ' ') byte |= 0x04;
if (charmap[5*i + 3] !== ' ') byte |= 0x02;
if (charmap[5*i + 4] !== ' ') byte |= 0x01;
buffer[i] = byte;
} else buffer = charmap;
this.write(buffer);
}

LCD.prototype.write = LCD.prototype.print = function (str) {
// TODO: map misc Unicode chars to typical LCD extended charset?
var bytes = (typeof str === 'string') ? new Buffer(str, 'ascii') :
(typeof str === 'object') ? str : new Buffer([str]);
for (var i = 0, len = bytes.length; i < len; i++)
this.send(bytes[i], this.board.HIGH);
}


/*
* mid/low level stuff
*/

LCD.prototype.command = function (value) {
this.send(value, this.board.LOW);
}

LCD.prototype.send = function (value, mode) {
this.board.digitalWrite(this.pins.rs, mode);
if (this.pins.rw !== 255) {
this.board.digitalWrite(this.pins.rw, this.board.LOW);
}
if (this.pins.data.length === 8) {
this._writeNbits(8, value);
} else {
this._writeNbits(4, value >> 4);
this._writeNbits(4, value & 0xF);
}
}

LCD.prototype._writeNbits = function (n, value) {
for (var i = 0; i < n; i++) {
this.board.pinMode(this.pins.data[i], 'out');
var bit = (value >> i) & 0x01;
this.board.digitalWrite(this.pins.data[i], (bit) ? this.board.HIGH : this.board.LOW);
}
this._pulseEnable();
}

LCD.prototype._pulseEnable = function () {
this.board.digitalWrite(this.pins.e, this.board.LOW);
this._delayMicroseconds(1);
this.board.digitalWrite(this.pins.e, this.board.HIGH);
this._delayMicroseconds(1); // enable pulse must be >450ns
this.board.digitalWrite(this.pins.e, this.board.LOW);
this._delayMicroseconds(100); // commands need > 37us to settle
}

LCD.prototype._delayMicroseconds = function (us) {
this.board.delay(us/1000);
}

module.exports = LCD;
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
"contributors": [
{ "name": "Rick Waldron", "email": "waldron.rick@gmail.com" },
{ "name": "Leonhardt Wille", "email": "wille@riverlabs.de" },
{ "name": "Seiya Konno", "email": "nulltask@gmail.com" }
{ "name": "Seiya Konno", "email": "nulltask@gmail.com" },
{ "name": "Nathan Vander Wilt", "email": "nate@calftrail.com" },
{ "name": "Adam Brault (&yet)", "email": "contact@andyet.com" }
],
"name": "duino",
"description": "Arduino framework for mad scientists",
"version": "0.0.7",
"version": "0.0.8",
"keywords": [
"arduino",
"serial",
Expand Down
4 changes: 2 additions & 2 deletions src/du.ino
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ void process() {
aux[3] = '\0';
} else {
strncpy(val, messageBuffer + 4, 3);
val[4] = '\0';
val[3] = '\0';
strncpy(aux, messageBuffer + 7, 3);
aux[4] = '\0';
aux[3] = '\0';
}

if (debug) {
Expand Down

0 comments on commit a3822af

Please sign in to comment.