From 587ab0adb76509881cd0860a66e0ff2041724bf6 Mon Sep 17 00:00:00 2001 From: Per Johan Groland Date: Wed, 13 Jul 2016 23:46:03 +0900 Subject: [PATCH 1/9] _transfer bugfix. Update with new methods from latest version. Cleanup. Added device option. Changed default device to i2c-1. --- LICENSE.md | 21 ++++++++ oled.js | 146 +++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 129 insertions(+), 38 deletions(-) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..5f76bc3 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Suz Hinton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/oled.js b/oled.js index c8fbf17..364ad77 100644 --- a/oled.js +++ b/oled.js @@ -5,7 +5,8 @@ var Oled = function(opts) { this.HEIGHT = opts.height || 32; this.WIDTH = opts.width || 128; this.ADDRESS = opts.address || 0x3C; - this.PROTOCOL = 'I2C'; + this.DEVICE = opts.device || '/dev/i2c-1'; + this.MICROVIEW = opts.microview || false; // create command buffers this.DISPLAY_OFF = 0xAE; @@ -15,7 +16,6 @@ var Oled = function(opts) { this.SET_DISPLAY_OFFSET = 0xD3; this.SET_START_LINE = 0x00; this.CHARGE_PUMP = 0x8D; - this.EXTERNAL_VCC = false; this.MEMORY_MODE = 0x20; this.SEG_REMAP = 0xA1; // using 0xA0 will flip screen this.COM_SCAN_DEC = 0xC8; @@ -61,18 +61,23 @@ var Oled = function(opts) { 'multiplex': 0x0F, 'compins': 0x2, 'coloffset': 0, - } - }; + }, + // this is blended microview / normal 64 x 48, currently wip + '64x48': { + 'multiplex': 0x2F, + 'compins': 0x12, + 'coloffset': (this.MICROVIEW) ? 32 : 0 + }, +}; // Setup i2c - console.log('this.ADDRESS: ' + this.ADDRESS); - this.wire = new i2c(this.ADDRESS, {device: '/dev/i2c-0'}); // point to your i2c address, debug provides REPL interface + this.wire = new i2c(this.ADDRESS, {device: this.DEVICE}); var screenSize = this.WIDTH + 'x' + this.HEIGHT; this.screenConfig = config[screenSize]; this._initialise(); -} +}; Oled.prototype._initialise = function() { @@ -102,7 +107,7 @@ Oled.prototype._initialise = function() { for (i = 0; i < initSeqLen; i ++) { this._transfer('cmd', initSeq[i]); } -} +}; // writes both commands and data buffers to this device Oled.prototype._transfer = function(type, val, fn) { @@ -115,22 +120,15 @@ Oled.prototype._transfer = function(type, val, fn) { return; } - // send control and actual val - // this.board.io.i2cWrite(this.ADDRESS, [control, val]); - this.wire.writeByte(control, function(err) { - this.wire.writeByte(val, function(err) { - fn(); - }); - }); -} + this.wire.writeBytes(control, [val], () => {}); +}; // read a byte from the oled Oled.prototype._readI2C = function(fn) { this.wire.readByte(function(err, data) { - // result is single byte fn(data); }); -} +}; // sometimes the oled gets a bit busy with lots of bytes. // Read the response byte to see if this is the case @@ -150,16 +148,16 @@ Oled.prototype._waitUntilReady = function(callback) { setTimeout(tick, 0); } }); - }; + } setTimeout(tick(callback), 0); -} +}; // set starting position of a text string on the oled Oled.prototype.setCursor = function(x, y) { this.cursor_x = x; this.cursor_y = y; -} +}; // write text to the oled Oled.prototype.writeString = function(font, size, string, color, wrap, sync) { @@ -210,7 +208,7 @@ Oled.prototype.writeString = function(font, size, string, color, wrap, sync) { if (immed) { this._updateDirtyBytes(this.dirtyBytes); } -} +}; // draw an individual character to the screen Oled.prototype._drawChar = function(byteArray, size, sync) { @@ -237,7 +235,7 @@ Oled.prototype._drawChar = function(byteArray, size, sync) { } } } -} +}; // get character bytes from the supplied font object in order to send to framebuffer Oled.prototype._readCharBytes = function(byteArray) { @@ -259,7 +257,7 @@ Oled.prototype._readCharBytes = function(byteArray) { bitArr = []; } return bitCharArr; -} +}; // find where the character exists within the font object Oled.prototype._findCharBuf = function(font, c) { @@ -268,7 +266,7 @@ Oled.prototype._findCharBuf = function(font, c) { // slice just the current char's bytes out of the fontData array and return var cBuf = font.fontData.slice(cBufPos, cBufPos + font.width); return cBuf; -} +}; // send the entire framebuffer to the oled Oled.prototype.update = function() { @@ -297,7 +295,7 @@ Oled.prototype.update = function() { } }.bind(this)); -} +}; // send dim display command to oled Oled.prototype.dimDisplay = function(bool) { @@ -311,17 +309,17 @@ Oled.prototype.dimDisplay = function(bool) { this._transfer('cmd', this.SET_CONTRAST); this._transfer('cmd', contrast); -} +}; // turn oled off Oled.prototype.turnOffDisplay = function() { this._transfer('cmd', this.DISPLAY_OFF); -} +}; // turn oled on Oled.prototype.turnOnDisplay = function() { this._transfer('cmd', this.DISPLAY_ON); -} +}; // clear all pixels currently on the display Oled.prototype.clearDisplay = function(sync) { @@ -339,7 +337,7 @@ Oled.prototype.clearDisplay = function(sync) { if (immed) { this._updateDirtyBytes(this.dirtyBytes); } -} +}; // invert pixels on oled Oled.prototype.invertDisplay = function(bool) { @@ -348,7 +346,7 @@ Oled.prototype.invertDisplay = function(bool) { } else { this._transfer('cmd', this.NORMAL_DISPLAY); // non inverted } -} +}; // draw an image pixel array on the screen Oled.prototype.drawBitmap = function(pixels, sync) { @@ -366,7 +364,7 @@ Oled.prototype.drawBitmap = function(pixels, sync) { if (immed) { this._updateDirtyBytes(this.dirtyBytes); } -} +}; // draw one or many pixels on oled Oled.prototype.drawPixel = function(pixels, sync) { @@ -408,7 +406,7 @@ Oled.prototype.drawPixel = function(pixels, sync) { if (immed) { this._updateDirtyBytes(this.dirtyBytes); } -} +}; // looks at dirty bytes, and sends the updated bytes to the display Oled.prototype._updateDirtyBytes = function(byteArray) { @@ -451,7 +449,7 @@ Oled.prototype._updateDirtyBytes = function(byteArray) { } // now that all bytes are synced, reset dirty state this.dirtyBytes = []; -} +}; // using Bresenham's line algorithm Oled.prototype.drawLine = function(x0, y0, x1, y1, color, sync) { @@ -475,7 +473,27 @@ Oled.prototype.drawLine = function(x0, y0, x1, y1, color, sync) { if (immed) { this._updateDirtyBytes(this.dirtyBytes); } -} +}; + +// Draw an outlined rectangle +Oled.prototype.drawRect = function(x, y, w, h, color, sync){ + var immed = (typeof sync === 'undefined') ? true : sync; + //top + this.drawLine(x, y, x + w, y,color,false); + + //left + this.drawLine(x, y + 1, x, y + h - 1, color, false); + + //right + this.drawLine(x + w, y + 1, x + w, y + h - 1, color, false); + + //bottom + this.drawLine(x, y + h - 1, x + w, y + h - 1, color, false); + + if (immed) { + this._updateDirtyBytes(this.dirtyBytes); + } +}; // draw a filled rectangle on the oled Oled.prototype.fillRect = function(x, y, w, h, color, sync) { @@ -488,7 +506,59 @@ Oled.prototype.fillRect = function(x, y, w, h, color, sync) { if (immed) { this._updateDirtyBytes(this.dirtyBytes); } -} +}; + +/** + * Draw a circle outline + * + * This method is ad verbatim translation from the corresponding + * method on the Adafruit GFX library + * https://github.com/adafruit/Adafruit-GFX-Library + */ +Oled.prototype.drawCircle = function(x0, y0, r, color, sync) { + var immed = (typeof sync === 'undefined') ? true : sync; + + var f = 1 - r; + var ddF_x = 1; + var ddF_y = -2 * r; + var x = 0; + var y = r; + + this.drawPixel( + [[x0, y0 + r, color], + [x0, y0 - r, color], + [x0 + r, y0, color], + [x0 - r, y0, color]], + false + ); + + while(x < y) { + if (f >=0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + this.drawPixel( + [[x0 + x, y0 + y, color], + [x0 - x, y0 + y, color], + [x0 + x, y0 - y, color], + [x0 - x, y0 - y, color], + [x0 + y, y0 + x, color], + [x0 - y, y0 + x, color], + [x0 + y, y0 - x, color], + [x0 - y, y0 - x, color]], + false + ); + } + + if (immed) { + this._updateDirtyBytes(this.dirtyBytes); + } +}; // activate scrolling for rows start through stop Oled.prototype.startScroll = function(dir, start, stop) { @@ -533,11 +603,11 @@ Oled.prototype.startScroll = function(dir, start, stop) { this._transfer('cmd', cmdSeq[i]); } }.bind(this)); -} +}; // stop scrolling display contents Oled.prototype.stopScroll = function() { this._transfer('cmd', this.DEACTIVATE_SCROLL); // stahp -} +}; module.exports = Oled; From 3420a7b4ae0aee2d23fff94d6aea7a28008e2a2d Mon Sep 17 00:00:00 2001 From: Per Johan Groland Date: Thu, 14 Jul 2016 11:44:52 +0900 Subject: [PATCH 2/9] Pulled code from latest noopkat version --- oled.js | 85 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 32 deletions(-) diff --git a/oled.js b/oled.js index 364ad77..05747cd 100644 --- a/oled.js +++ b/oled.js @@ -160,13 +160,14 @@ Oled.prototype.setCursor = function(x, y) { }; // write text to the oled -Oled.prototype.writeString = function(font, size, string, color, wrap, sync) { +Oled.prototype.writeString = function(font, size, string, color, wrap, linespacing, sync) { var immed = (typeof sync === 'undefined') ? true : sync; var wordArr = string.split(' '), len = wordArr.length, // start x offset at cursor pos offset = this.cursor_x, - padding = 0, letspace = 1, leading = 2; + padding = 0, letspace = 1; + var leading = linespacing || 2; // loop through words for (var w = 0; w < len; w += 1) { @@ -190,7 +191,7 @@ Oled.prototype.writeString = function(font, size, string, color, wrap, sync) { // read the bits in the bytes that make up the char var charBytes = this._readCharBytes(charBuf); // draw the entire character - this._drawChar(charBytes, size, false); + this._drawChar(font, charBytes, size, false); // calc new x position for the next char, add a touch of padding too if it's a non space char padding = (stringArr[i] === ' ') ? 0 : size + letspace; @@ -211,21 +212,24 @@ Oled.prototype.writeString = function(font, size, string, color, wrap, sync) { }; // draw an individual character to the screen -Oled.prototype._drawChar = function(byteArray, size, sync) { +Oled.prototype._drawChar = function(font, byteArray, size, sync) { // take your positions... var x = this.cursor_x, y = this.cursor_y; + var pagePos = 0; + var c = 0; // loop through the byte array containing the hexes for the char for (var i = 0; i < byteArray.length; i += 1) { + pagePos = Math.floor(i / font.width) * 8; for (var j = 0; j < 8; j += 1) { // pull color out var color = byteArray[i][j], xpos, ypos; // standard font size if (size === 1) { - xpos = x + i; - ypos = y + j; + xpos = x + c; + ypos = y + j + pagePos; this.drawPixel([xpos, ypos, color], false); } else { // MATH! Calculating pixel size multiplier to primitively scale the font @@ -234,6 +238,7 @@ Oled.prototype._drawChar = function(byteArray, size, sync) { this.fillRect(xpos, ypos, size, size, color, false); } } + c = (c < font.width -1) ? c += 1 : 0; } }; @@ -295,6 +300,9 @@ Oled.prototype.update = function() { } }.bind(this)); + + // now that all bytes are synced, reset dirty state + this.dirtyBytes = []; }; // send dim display command to oled @@ -473,26 +481,26 @@ Oled.prototype.drawLine = function(x0, y0, x1, y1, color, sync) { if (immed) { this._updateDirtyBytes(this.dirtyBytes); } -}; +} // Draw an outlined rectangle Oled.prototype.drawRect = function(x, y, w, h, color, sync){ - var immed = (typeof sync === 'undefined') ? true : sync; - //top - this.drawLine(x, y, x + w, y,color,false); + var immed = (typeof sync === 'undefined') ? true : sync; + //top + this.drawLine(x, y, x + w, y,color,false); - //left - this.drawLine(x, y + 1, x, y + h - 1, color, false); + //left + this.drawLine(x, y + 1, x, y + h - 1, color, false); - //right - this.drawLine(x + w, y + 1, x + w, y + h - 1, color, false); + //right + this.drawLine(x + w, y + 1, x + w, y + h - 1, color, false); - //bottom - this.drawLine(x, y + h - 1, x + w, y + h - 1, color, false); + //bottom + this.drawLine(x, y + h - 1, x + w, y + h - 1, color, false); - if (immed) { - this._updateDirtyBytes(this.dirtyBytes); - } + if (immed) { + this._updateDirtyBytes(this.dirtyBytes); + } }; // draw a filled rectangle on the oled @@ -570,32 +578,45 @@ Oled.prototype.startScroll = function(dir, start, stop) { cmdSeq.push(this.RIGHT_HORIZONTAL_SCROLL); break; case 'left': cmdSeq.push(this.LEFT_HORIZONTAL_SCROLL); break; - // TODO: left diag and right diag not working yet case 'left diagonal': cmdSeq.push( - this.SET_VERTICAL_SCROLL_AREA, 0x00, + this.SET_VERTICAL_SCROLL_AREA, + 0x00, + this.HEIGHT, this.VERTICAL_AND_LEFT_HORIZONTAL_SCROLL, - this.HEIGHT + 0x00, + start, + 0x00, + stop, + 0x01, + this.ACTIVATE_SCROLL ); break; - // TODO: left diag and right diag not working yet case 'right diagonal': cmdSeq.push( - this.SET_VERTICAL_SCROLL_AREA, 0x00, + this.SET_VERTICAL_SCROLL_AREA, + 0x00, + this.HEIGHT, this.VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL, - this.HEIGHT + 0x00, + start, + 0x00, + stop, + 0x01, + this.ACTIVATE_SCROLL ); break; } this._waitUntilReady(function() { - cmdSeq.push( - 0x00, start, - 0x00, stop, - // TODO: these need to change when diagonal - 0x00, 0xFF, - this.ACTIVATE_SCROLL - ); + if(dir === 'right' || dir === 'left'){ + cmdSeq.push( + 0x00, start, + 0x00, stop, + 0x00, 0xFF, + this.ACTIVATE_SCROLL + ); + } var i, cmdSeqLen = cmdSeq.length; From fc5c20fad5cf781165c00d444d12d335b35afc4e Mon Sep 17 00:00:00 2001 From: Per Johan Groland Date: Thu, 14 Jul 2016 11:55:17 +0900 Subject: [PATCH 3/9] Improved README --- README.md | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 866c0d2..f2e8d79 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ OLED JS Pi ## What is this? -A NodeJS driver for I2C/SPI compatible monochrome OLED screens; to be used on the Raspberry Pi! Works with 128 x 32, 128 x 64 and 96 x 16 sized screens, of the SSD1306 OLED/PLED Controller (read the [datasheet here](http://www.adafruit.com/datasheets/SSD1306.pdf)). +A NodeJS driver for I2C/SPI compatible monochrome OLED screens. Only I2C is supported at this time. +Compatible with Raspberry Pi, Works with 128 x 32, 128 x 64, 96 x 16 and 64x48 sized screens, of the SSD1306 OLED/PLED Controller (read the [datasheet here](http://www.adafruit.com/datasheets/SSD1306.pdf)). This based on the Blog Post and code by Suz Hinton - [Read her blog post about how OLED screens work](http://meow.noopkat.com/oled-js/)! @@ -20,7 +21,8 @@ If you haven't already, install [NodeJS](http://nodejs.org/). `npm install oled-js-pi` ## I2C screens -Hook up I2C compatible oled to the Raspberry Pi. Pins: SDL and SCL + +Hook up I2C compatible oled to the Raspberry Pi. Pins: SDL and SCL. ### I2C example @@ -30,17 +32,53 @@ var oled = require('oled-js-pi'); var opts = { width: 128, height: 64, - address: 0x3D }; var oled = new oled(opts); // do cool oled things here +``` + +The above code uses the default I2C address of 0x3C and the I2C device /dev/i2c-1 (default on newer Raspberry Pi boards). +Additional options that can be passed, with default values shown: + +```javascript +var opts = { + width: 128, // screen width + height; 32, // screen height + address: 0x3C, // Pass I2C address of screen if it is not the default of 0x3C + device: '/dev/i2c-1', // Pass your i2c device here if it is not /dev/i2c-1 + microview: true, // set to true if you have a microview display +}; ``` +Allowable combinations for screen width and height are: +128x32, 128x64, 96x16 and 64x48. + ### Wait, how do I find out the I2C address of my OLED screen? -Check your screen's documentation... + +You can use the i2c npm library for this. Make sure the display is connected to the I2C bus with pull-ups and run the +following: + +``` +npm install i2c +``` + +Then run the following script: + +```javascript +var i2c = require('i2c'); +var address = 0x3C; +var wire = new i2c(address, {device: '/dev/i2c-1'}); + +wire.scan(function(err, data) { + // result contains an array of addresses +}); +``` + +This will return the I2C addresses of any I2C devices connected to the bus at /dev/i2c-1. If you have other devices +than the screen attached, you will need to manually filter those out. ## Available methods @@ -136,6 +174,40 @@ Usage: oled.fillRect(1, 1, 10, 20, 1); ``` +### drawRect +Draws an empty rectangle. + +Arguments: ++ int **x0, y0** - top left corner of rectangle ++ int **x1, y1** - bottom right corner of rectangle ++ int **color** - can be specified as either 0 for 'off' or black, and 1 or 255 for 'on' or white. + +Optional bool as last argument specifies whether screen updates immediately with result. Default is true. + +Usage: +```javascript +// args: (x0, y0, x1, y1, color) +oled.drawRect(1, 1, 10, 20, 1); +``` + +### drawCircle +Draws an empty circle. + +Arguments: ++ int **x** - x of circle's center ++ int **y** - y of circle's center ++ int **r** - radius of circle ++ int **color** - can be specified as either 0 for 'off' or black, and 1 or 255 for 'on' or white. + +Optional bool as last argument specifies whether screen updates immediately with result. Default is true. + +Usage: +```javascript +// args: (x, y, r, color) +oled.drawCircle(30, 10, 5, 1); +``` + + ### drawBitmap Draws a bitmap using raw pixel data returned from an image parser. The image sourced must be monochrome, and indexed to only 2 colors. Resize the bitmap to your screen dimensions first. Using an image editor or ImageMagick might be required. @@ -214,6 +286,7 @@ Arguments: + string **text** - the actual text you want to show on the display. + int **color** - color of text. Can be specified as either 0 for 'off' or black, and 1 or 255 for 'on' or white. + bool **wrapping** - true applies word wrapping at the screen limit, false for no wrapping. If a long string without spaces is supplied as the text, just letter wrapping will apply instead. ++ int **linespacing** - amount of spacing between lines of text on the screen. Negative numbers are also ok. Optional bool as last argument specifies whether screen updates immediately with result. Default is true. From 83282478b131ee0c1b65f6a56aa46a6682794d77 Mon Sep 17 00:00:00 2001 From: Per Johan Groland Date: Thu, 14 Jul 2016 16:27:20 +0900 Subject: [PATCH 4/9] More code updatres from noopkat version --- oled.js | 67 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/oled.js b/oled.js index 05747cd..8fb4dc9 100644 --- a/oled.js +++ b/oled.js @@ -110,7 +110,7 @@ Oled.prototype._initialise = function() { }; // writes both commands and data buffers to this device -Oled.prototype._transfer = function(type, val, fn) { +Oled.prototype._transfer = function(type, val) { var control; if (type === 'data') { control = 0x40; @@ -120,7 +120,7 @@ Oled.prototype._transfer = function(type, val, fn) { return; } - this.wire.writeBytes(control, [val], () => {}); + this.wire.writeBytes(control, [val], function () {}); }; // read a byte from the oled @@ -144,7 +144,6 @@ Oled.prototype._waitUntilReady = function(callback) { // if not busy, it's ready for callback callback(); } else { - console.log('I\'m busy!'); setTimeout(tick, 0); } }); @@ -421,40 +420,46 @@ Oled.prototype._updateDirtyBytes = function(byteArray) { var blen = byteArray.length, i, displaySeq = []; - // check to see if this will even save time - if (blen > (this.buffer.length / 7)) { - // just call regular update at this stage, saves on bytes sent - this.update(); - // now that all bytes are synced, reset dirty state - this.dirtyBytes = []; + this._waitUntilReady(function() { + var pageStart = Infinity, pageEnd = 0; + var colStart = Infinity, colEnd = 0, any = false; + + // iterate through dirty bytes + for (var i = 0; i < blen; i += 1) { + var b = byteArray[i]; + if ((b >= 0) && (b < this.buffer.length)) { + var page = b / this.WIDTH | 0; + if (page < pageStart) pageStart = page; + if (page > pageEnd) pageEnd = page; + var col = b % this.WIDTH; + if (col < colStart) colStart = col; + if (col > colEnd) colEnd = col; + any = true; + } + } - } else { + if (!any) return; - this._waitUntilReady(function() { - // iterate through dirty bytes - for (var i = 0; i < blen; i += 1) { + displaySeq = [ + this.COLUMN_ADDR, colStart, colEnd, // column start and end address + this.PAGE_ADDR, pageStart, pageEnd // page start and end address + ]; - var byte = byteArray[i]; - var page = Math.floor(byte / this.WIDTH); - var col = Math.floor(byte % this.WIDTH); + var displaySeqLen = displaySeq.length, v, vp, vc; - var displaySeq = [ - this.COLUMN_ADDR, col, col, // column start and end address - this.PAGE_ADDR, page, page // page start and end address - ]; + // send intro seq + for (v = 0; v < displaySeqLen; v += 1) { + this._transfer('cmd', displaySeq[v]); + } + // send byte, then move on to next byte + for (vp = pageStart; vp <= pageEnd; vp += 1) { + for (vc = colStart; vc <= colEnd; vc += 1) { + this._transfer('data', this.buffer[this.WIDTH * vp + vc]); + } + } - var displaySeqLen = displaySeq.length, v; + }.bind(this)); - // send intro seq - for (v = 0; v < displaySeqLen; v += 1) { - this._transfer('cmd', displaySeq[v]); - } - // send byte, then move on to next byte - this._transfer('data', this.buffer[byte]); - this.buffer[byte]; - } - }.bind(this)); - } // now that all bytes are synced, reset dirty state this.dirtyBytes = []; }; From fd0134e2e13d3ebb1323b242aa799cc39104b013 Mon Sep 17 00:00:00 2001 From: Per Johan Groland Date: Thu, 14 Jul 2016 16:27:52 +0900 Subject: [PATCH 5/9] Imported tests directory from noopkat repo --- tests/buffers/adafruitLogoBuf.js | 37 +++++++ tests/demoTime.js | 165 +++++++++++++++++++++++++++++++ tests/demoTime_bt.js | 103 +++++++++++++++++++ tests/images/cat-128x64.png | Bin 0 -> 17966 bytes tests/images/cat-microview.png | Bin 0 -> 6524 bytes tests/images/cat.png | Bin 0 -> 8992 bytes tests/images/noopkat-mono.png | Bin 0 -> 532 bytes tests/images/noopkat.png | Bin 0 -> 2354 bytes tests/images/parrot-tiny.png | Bin 0 -> 6957 bytes 9 files changed, 305 insertions(+) create mode 100644 tests/buffers/adafruitLogoBuf.js create mode 100644 tests/demoTime.js create mode 100644 tests/demoTime_bt.js create mode 100644 tests/images/cat-128x64.png create mode 100644 tests/images/cat-microview.png create mode 100644 tests/images/cat.png create mode 100644 tests/images/noopkat-mono.png create mode 100644 tests/images/noopkat.png create mode 100644 tests/images/parrot-tiny.png diff --git a/tests/buffers/adafruitLogoBuf.js b/tests/buffers/adafruitLogoBuf.js new file mode 100644 index 0000000..d29e174 --- /dev/null +++ b/tests/buffers/adafruitLogoBuf.js @@ -0,0 +1,37 @@ +// example picture using just pixels, in the framebuffer size allowed (512) +var adafruitLogo = [ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, +0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x80, 0x80, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, +0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0xFF, +0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, +0x80, 0xFF, 0xFF, 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, +0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x8C, 0x8E, 0x84, 0x00, 0x00, 0x80, 0xF8, +0xF8, 0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80, +0x00, 0xE0, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0x01, 0x01, +0x01, 0x01, 0x83, 0xFF, 0xFF, 0x00, 0x00, 0x7C, 0xFE, 0xC7, 0x01, 0x01, 0x01, 0x01, 0x83, 0xFF, +0xFF, 0xFF, 0x00, 0x38, 0xFE, 0xC7, 0x83, 0x01, 0x01, 0x01, 0x83, 0xC7, 0xFF, 0xFF, 0x00, 0x00, +0x01, 0xFF, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0xFF, 0x07, 0x01, 0x01, 0x01, 0x00, 0x00, 0x7F, 0xFF, +0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF, +0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x0F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC7, 0xC7, 0x8F, +0x8F, 0x9F, 0xBF, 0xFF, 0xFF, 0xC3, 0xC0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xFC, +0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xC0, 0x00, 0x01, 0x03, 0x03, 0x03, +0x03, 0x03, 0x01, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, +0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x03, 0x03, 0x00, 0x00, +0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, +0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +]; + +module.exports = adafruitLogo; \ No newline at end of file diff --git a/tests/demoTime.js b/tests/demoTime.js new file mode 100644 index 0000000..fb9238a --- /dev/null +++ b/tests/demoTime.js @@ -0,0 +1,165 @@ +var five = require('johnny-five'), + pngtolcd = require('png-to-lcd'), + board = new five.Board(), + Oled = require('../oled'), + font = require('oled-font-5x7'), + temporal = require('temporal'); + +// testing features +board.on('ready', function() { + console.log('Connected to Arduino, ready.'); + + // I2C va USB + // var opts = { + // width: 128, + // height: 64, + // address: 0x3D + // }; + + // SPI via USB + // var opts = { + // width: 128, + // height: 64, + // slavePin: 12 + // }; + + // SPI Microview via USB + var opts = { + width: 64, + height: 48, + microview: true + }; + + var oled = new Oled(board, five, opts); + + test(oled); +}); + +// sequence of test displays +function test(oled) { + + // if it was already scrolling, stop + oled.stopScroll(); + + // clear first just in case + oled.update(); + + // make it prettier + oled.dimDisplay(true); + + + temporal.queue([ + { + delay: 100, + task: function() { + // draw some test pixels + oled.drawPixel([ + [127, 0, 1], + [127, 31, 1], + [127, 16, 1], + [64, 16, 1] + ]); + } + }, + { + delay: 10000, + task: function() { + oled.clearDisplay(); + // display a bitmap + pngtolcd(__dirname + '/images/cat-128x64.png', true, function(err, bitmapbuf) { + oled.buffer = bitmapbuf; + oled.update(); + }); + + } + }, + { + delay: 10000, + task: function() { + oled.clearDisplay(); + // display text + oled.setCursor(0, 0); + oled.writeString(font, 1, 'Cats and dogs are really cool animals, you know.', 1, true, 2); + } + }, + { + delay: 10000, + task: function() { + oled.clearDisplay(); + // draw some lines + oled.drawLine(0, 0, 127, 31, 1); + oled.drawLine(64, 16, 127, 16, 1); + oled.drawLine(0, 10, 40, 10, 1); + } + }, + { + delay: 10000, + task: function() { + oled.clearDisplay(); + // draw a rectangle + oled.fillRect(0, 0, 10, 20, 1); + } + }, + { + delay: 10000, + task: function() { + // create concenctric rectangle outlines + oled.clearDisplay(); + + //calc how many squares we can fit on the screen + var padding = 2; + var square_count = ((oled.WIDTH / 2 ) / (padding * 2) ) - 1; + + for(var i = 0; i < square_count; i ++){ + var x = ((i + 1) * padding); + var y = ((i + 1) * padding); + var w = oled.WIDTH - (x * padding); + var h = oled.HEIGHT - (y * padding); + oled.drawRect(x, y, w, h, 1, false); + } + oled.update(); + } + }, + { + delay: 10000, + task: function() { + // create concenctric circle outlines + oled.clearDisplay(); + + var x = oled.WIDTH / 2; + var y = oled.HEIGHT / 2; + var radius = oled.HEIGHT - 1 + + //calc how many circles we can fit on the screen + var circle_count = radius / 3; + + for(var i = 0; i < circle_count; i++){ + var r = radius - (i * 3); + oled.drawCircle(x, y, r, 1, false); + } + oled.update(); + } + }, + { + delay: 10000, + task: function() { + oled.clearDisplay(); + // display text + oled.setCursor(0, 7); + oled.writeString(font, 2, 'SCROLL!', 1, true, 1); + oled.startScroll('left', 0, 6); + } + }, + { + delay: 10000, + task: function() { + oled.stopScroll(); + oled.clearDisplay(); + oled.update(); + oled.setCursor(0, 7); + oled.writeString(font, 2, 'DIAGONAL SCROLL', 1, true, 1); + oled.startScroll('left diagonal', 0, 15); + } + } + ]); +} diff --git a/tests/demoTime_bt.js b/tests/demoTime_bt.js new file mode 100644 index 0000000..94d54aa --- /dev/null +++ b/tests/demoTime_bt.js @@ -0,0 +1,103 @@ +var five = require('johnny-five'), + pngtolcd = require('png-to-lcd'), + blendMicroIO = require('blend-micro-io'), + Oled = require('../oled'), + font = require('oled-font-5x7'), + temporal = require('temporal'); + +var board = new five.Board({ + io: new blendMicroIO() +}); + +// testing features +board.on('ready', function() { + console.log('Connected to Arduino, ready.'); + + var opts = { + width: 128, + height: 64, + address: 0x3D + }; + + var oled = new Oled(board, five, opts); + + test(oled); +}); + +// sequence of test displays +function test(oled) { + + // if it was already scrolling, stop + oled.stopScroll(); + + // clear first just in case + oled.update(); + + // make it prettier + oled.dimDisplay(true); + + + temporal.queue([ + { + delay: 100, + task: function() { + // draw some test pixels + oled.drawPixel([ + [127, 0, 1], + [127, 31, 1], + [127, 16, 1], + [64, 16, 1] + ]); + } + }, + { + delay: 10000, + task: function() { + oled.clearDisplay(); + // display a bitmap + pngtolcd(__dirname + '/images/cat-128x64.png', true, function(err, bitmapbuf) { + oled.buffer = bitmapbuf; + oled.update(); + }); + + } + }, + { + delay: 10000, + task: function() { + oled.clearDisplay(); + // display text + oled.setCursor(0, 0); + oled.writeString(font, 1, 'Cats and dogs are really cool animals, you know.', 1, true); + } + }, + { + delay: 10000, + task: function() { + oled.clearDisplay(); + // draw some lines + oled.drawLine(0, 0, 127, 31, 1); + oled.drawLine(64, 16, 127, 16, 1); + oled.drawLine(0, 10, 40, 10, 1); + } + }, + { + delay: 10000, + task: function() { + oled.clearDisplay(); + // draw a rectangle + oled.fillRect(0, 0, 10, 20, 1); + } + }, + { + delay: 10000, + task: function() { + oled.clearDisplay(); + // display text + oled.setCursor(0, 7); + oled.writeString(font, 2, 'SCROLL!', 1, true); + oled.startScroll('left', 0, 6); + } + } + ]); +} \ No newline at end of file diff --git a/tests/images/cat-128x64.png b/tests/images/cat-128x64.png new file mode 100644 index 0000000000000000000000000000000000000000..dfb523979c1059f27874181d9c27aaca68b48feb GIT binary patch literal 17966 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

?M^^n5q+E!C@^T^%2RcW-$6rBr`?xFY{ zL$|@Sf%S^Mil#v1u?#HVG2#cHlng;4g@_6O&&=f~MKNuzm8Bz$QHDX{(|TQ#Db7IQ zx+nE@>tW>Kj{M=&0|!HY>N2wnoEsSHLuul7%1zB&xYhjl1j~jG5%a2666Qp(H%uJA zd&bHvJ#ufw1?e;>%zAdF4Tk6YDKFW!t0#>t3#;bhMizNF{jyRYb30(yOpuZcrx!Ek zsXaE{43rdDGY%R9@FabTJlaV{Vx9Zn<&`C*{levbl7ZQD!liR67*hrds@-b%pWA!CXyE z&5U~<8$oZBQD&XN3xAlV#Bz(CnfKyPOn*w?X?tNT$fZ197&${)Es(Z+_J08$1>yPy z#t>nIA;gPiMHywBQGzMO=Y1q07(w`VC2s-{dheaH&YJV~#u#gjJAZkINnR9Z3q(w? z%q3@R8mFP_r*WF6xjnUGKU(8tCh{WV976yJPSJKV#%6#$>%!YDzH*&V}IK81LOUj35DoAOZkm=&hZGuJ2ptT#896qjh-y)i;av z#b&*dj66I%L6np+)@YPMnalN}JasCBYQ`I`xsOBCHu}86GDU#oh3%iYIFPQ)n>EFib58-vlbH^V?69n zF+_q%N&rI48C$LvLU13Pwc0zUls+BW!{Njj%`&N!iZN1*y0#;X-riglIX@guecNJ8 zK#UM3kN`*k0ti5K))7Pygb)Jfyf&IqMi2rZ>S;Xf_g&X|7tUl>E{p5eZ{EClg$ef7 z3W{enE2U4T_G$a%tbf?bc;PWiotT*2K zd7h+{DS>I4jrEjLnM;_!H1vnV!FiWb!h`?-v{E4i2q0seQic&K%IvH~fDl@12mlZv zm=Z`S#*ku638ZsOGUv zSv!r>#pUw(@sU*EyKlex{>Q)FUSC51F4xsxe){?DahCve&O7UakGtm`!RS0Xef{D6 z)#Zjz=ADBOf)ptsAc5e65B>~#0|3q>R6-bK45UN}g%BbPC?yCX&KP4f#GsAB2*D6y z2qDA-5;)tv#Pj+R0Eo^T8hGwgQv#H-X0^O|bGzI$LpvOHdt)rZm~$=#9r}5i6h=79 zc#&r*Mdw^hDbEBYG$uqDMG)voi%dG_t#y{1TzAp1T z<3IlRb0I}p=8xO`^LE!PvT>UFe)#nHPHA;}b6M5psco&XS)O_8e2k1UjIj`samECr zoX*p5JnV*H(%PImNlK}Z8Rvp$Ij4*pF M07*qoM6N<$g6htQ3jhEB literal 0 HcmV?d00001 diff --git a/tests/images/cat-microview.png b/tests/images/cat-microview.png new file mode 100644 index 0000000000000000000000000000000000000000..79175e7498e5f9bedeb0a4d3b6031280625eed90 GIT binary patch literal 6524 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 literal 0 HcmV?d00001 diff --git a/tests/images/cat.png b/tests/images/cat.png new file mode 100644 index 0000000000000000000000000000000000000000..53fa8b7c406f2e728734ee491ce7b5fa4e6f5a80 GIT binary patch literal 8992 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 literal 0 HcmV?d00001 diff --git a/tests/images/noopkat-mono.png b/tests/images/noopkat-mono.png new file mode 100644 index 0000000000000000000000000000000000000000..30ef35483e698b94ab0be5547d22dc1064b5634e GIT binary patch literal 532 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G$P6SOym^!cq*&4&eH|GXHuiJ>Nn{1`8H;15^MoJ zA+G=b{|7Qd4_&SUQjAI7?k)@+tg;?JwxFkrV~EE2trIqSu^0-prE^LJ+%O5x*;>hZ zy5aO%eS*pU7o%sBUOGy;48L?JxTph zI(v$X7;Y_?$RkzAf2@n~jYJOHQMnvjm4HrFi6`gxF}zq4;q3f~G3LwiiH#5Kw9h47 z;0!rxUhsZl?2{>sJgWWSB`2@Q1g(3a9k%A6Ab;9KjpLgRMXhYO5II$J?{n2ldc9X~ zKF)J4UhBqoJ@)^JE>Zpo?*q5zYbW&!?45OrFW}`w#_K#?-9^XFUhL+On+{)O<%E(;Tz}(8f j;9QiZ2#SW>{FKbJO57Uuvrk+B)WG2B>gTe~DWM4f-k8J% literal 0 HcmV?d00001 diff --git a/tests/images/noopkat.png b/tests/images/noopkat.png new file mode 100644 index 0000000000000000000000000000000000000000..b73a1c485590bfbd4a9745c17e913f2d2c99ae1b GIT binary patch literal 2354 zcmV-23C;G2P)n`H3$>JsfF&&#Q#$8i|7U0ZX=i}eNKOvl)9za9TYK;IwX-i% z1pPn5=FOWC1VIo)B9Z)s?zCDh@?Ugybr}axkwq*h76n*fEDEr|up)PBwc76PZVrcI zYim2ljB2&It*wntr}O!ICnu+Qjm8r0(9n=vE@vM z>*nUhWHPB#3%8u}iK6JkhY#1STL*~@1|u>u z5=_JxR8)U|KXe%kh8Hhh#K*_u)TPtusi~>;_4NjW0h{XV*|R(z zkIUu4NDhYsGRx(181|0#4I4IOXJ=o#cCDzWC?g{S4hBJxbLY-M%H_+K1p+}tLD}mS7*Vni&kO2fxC=}e6Y~BLUXtd9tKb!2v*Vh-sMfIYm+P# zMQ`1@1!AaF>eSSf7K)<7!^3dr z0H3XvaG?MP4;~~U4h;>#nO9d=LpxRgxm@neZkzIXMX$GMNlCVq;^8SO|hRIy$QB)q)5Kx1rd%-9R6@8{1udkPv7oX2x zyLPROjm_P=cY)XVIYADK1mJKuDwPUB5V#K`0tiD81a9~&;X+8IQtVkD2c=R88mm^V zGWN#B#Z^>PFc^%itgN!KvbMH11VM6ga^mCTnM@`O(`YpN_wNU77KPahZnhAP7)@SN+%{JKpQK7*<1+h_5%ll!2qSj9ghKy967>ZFc1Wx(P%es z-UO$dk&%(?>}+gUQ&ST>h}G5A8CM_q_aZ-EG5(AI6%`e*OvVt4#h}4tGT|y`2^WGy zfQpKWU%!5Vwm={tnFWF%At51f0eJN2(evlejl1yi@ge%P)z#IJ=O=+h0_@qdhfb#> z2r@c4T2@wOGDfG<-M@byv~zQF;oXBJT!^Zws-~tUlU#Vt2o4U$K2nHq;lc%Y9ee1| zq4Du?(AMkqph0xSJvBA;;K74g^AiFb2jD^M{Q2`l$BpC1kAs+$loV*p&CLZyJ3G6Y zni?EsdU`r8E)E1ZIy!2#TCCS>F7_(I)zww4R%403fBzN=g)rdRvu9W@$!#dMjv|o= z7^|wPfQ3S#7@ydOhK9nz!m#zk-6QDrdiW}(w6xT?s+I~cHZ}&!VP$2NmzP&rS^49~ zkJi@K>({SipO_^jCAfrUb0G_CIoND=W@hH&$B$cETW{RB5gr~6iCedB#dS*lJ^-Ey zDHKXWLj#JU8jS|tN7>uki$tQjy1K5euF}%d^z?LRXJ-UKmM>opWAgL!J3Biy8V$_3 zefxIcVzF5H`T51g#Xo=k{9^*3DEjW*JLBaM3p$;C`}S>{SD2i5BSCfp8$z(P+H|OQ$VU0jMfU8%p;wWVA1I!QrMNy4L zlbV`pa(wRY?kz1XL^;jlLID~Z8#_8W;Cc?hU@!^_3UoT1F?ZI#r4$qtz&v?*dB7-< zNQ}1t*4EZSp%ATV#eh($RN$i9)6)}s zqWqHr{59YNSm@zcgvGz5EHD-YSYRv)u)tUpV1cnHzyjmXybt)xIC0_xxW9vMbpLnp YAL2St`|uezWB>pF07*qoM6N<$f~WXT>;M1& literal 0 HcmV?d00001 diff --git a/tests/images/parrot-tiny.png b/tests/images/parrot-tiny.png new file mode 100644 index 0000000000000000000000000000000000000000..d27740092bf176fdf6d6b80a9a86c9f988a5a2d9 GIT binary patch literal 6957 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 literal 0 HcmV?d00001 From a9696407efe57aec8b95cb15593674f2144a1fbb Mon Sep 17 00:00:00 2001 From: Per Johan Groland Date: Thu, 14 Jul 2016 18:06:19 +0900 Subject: [PATCH 6/9] Modified tests to work with new code --- oled.js | 4 ++ tests/demoTime.js | 38 +++------------- tests/demoTime_bt.js | 103 ------------------------------------------- 3 files changed, 10 insertions(+), 135 deletions(-) delete mode 100644 tests/demoTime_bt.js diff --git a/oled.js b/oled.js index 8fb4dc9..2b8e2bd 100644 --- a/oled.js +++ b/oled.js @@ -2,6 +2,10 @@ var i2c = require('i2c'); var Oled = function(opts) { + if (opts == null) { + opts = {} + } + this.HEIGHT = opts.height || 32; this.WIDTH = opts.width || 128; this.ADDRESS = opts.address || 0x3C; diff --git a/tests/demoTime.js b/tests/demoTime.js index fb9238a..da4031d 100644 --- a/tests/demoTime.js +++ b/tests/demoTime.js @@ -1,40 +1,15 @@ -var five = require('johnny-five'), - pngtolcd = require('png-to-lcd'), - board = new five.Board(), +var pngtolcd = require('png-to-lcd'), Oled = require('../oled'), font = require('oled-font-5x7'), temporal = require('temporal'); -// testing features -board.on('ready', function() { - console.log('Connected to Arduino, ready.'); - - // I2C va USB - // var opts = { - // width: 128, - // height: 64, - // address: 0x3D - // }; - - // SPI via USB - // var opts = { - // width: 128, - // height: 64, - // slavePin: 12 - // }; - - // SPI Microview via USB - var opts = { - width: 64, - height: 48, - microview: true - }; - - var oled = new Oled(board, five, opts); - - test(oled); +var oled = new Oled({ + width: 128, + height: 64, }); +test(oled); + // sequence of test displays function test(oled) { @@ -47,7 +22,6 @@ function test(oled) { // make it prettier oled.dimDisplay(true); - temporal.queue([ { delay: 100, diff --git a/tests/demoTime_bt.js b/tests/demoTime_bt.js deleted file mode 100644 index 94d54aa..0000000 --- a/tests/demoTime_bt.js +++ /dev/null @@ -1,103 +0,0 @@ -var five = require('johnny-five'), - pngtolcd = require('png-to-lcd'), - blendMicroIO = require('blend-micro-io'), - Oled = require('../oled'), - font = require('oled-font-5x7'), - temporal = require('temporal'); - -var board = new five.Board({ - io: new blendMicroIO() -}); - -// testing features -board.on('ready', function() { - console.log('Connected to Arduino, ready.'); - - var opts = { - width: 128, - height: 64, - address: 0x3D - }; - - var oled = new Oled(board, five, opts); - - test(oled); -}); - -// sequence of test displays -function test(oled) { - - // if it was already scrolling, stop - oled.stopScroll(); - - // clear first just in case - oled.update(); - - // make it prettier - oled.dimDisplay(true); - - - temporal.queue([ - { - delay: 100, - task: function() { - // draw some test pixels - oled.drawPixel([ - [127, 0, 1], - [127, 31, 1], - [127, 16, 1], - [64, 16, 1] - ]); - } - }, - { - delay: 10000, - task: function() { - oled.clearDisplay(); - // display a bitmap - pngtolcd(__dirname + '/images/cat-128x64.png', true, function(err, bitmapbuf) { - oled.buffer = bitmapbuf; - oled.update(); - }); - - } - }, - { - delay: 10000, - task: function() { - oled.clearDisplay(); - // display text - oled.setCursor(0, 0); - oled.writeString(font, 1, 'Cats and dogs are really cool animals, you know.', 1, true); - } - }, - { - delay: 10000, - task: function() { - oled.clearDisplay(); - // draw some lines - oled.drawLine(0, 0, 127, 31, 1); - oled.drawLine(64, 16, 127, 16, 1); - oled.drawLine(0, 10, 40, 10, 1); - } - }, - { - delay: 10000, - task: function() { - oled.clearDisplay(); - // draw a rectangle - oled.fillRect(0, 0, 10, 20, 1); - } - }, - { - delay: 10000, - task: function() { - oled.clearDisplay(); - // display text - oled.setCursor(0, 7); - oled.writeString(font, 2, 'SCROLL!', 1, true); - oled.startScroll('left', 0, 6); - } - } - ]); -} \ No newline at end of file From e6f25c1d5bb7d28f136fd3b4ad15279baae68f43 Mon Sep 17 00:00:00 2001 From: Per Johan Groland Date: Thu, 14 Jul 2016 18:34:23 +0900 Subject: [PATCH 7/9] Updated dependencies --- package.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6b43e40..3401052 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,17 @@ "version": "1.0.5", "description": "NodeJS module for controlling oled devices on the Raspbery Pi (including the SSD1306 OLED screens)", "main": "oled.js", - "dependencies": {"i2c": "~0.2.1"}, + "dependencies": { + "i2c": "0.2.3", + "oled-font-5x7": "1.0.0", + "png-to-lcd": "1.0.3", + "pngparse": "2.0.1" + }, "devDependencies": { "oled-font-5x7": "~1.0.0", "png-to-lcd": "~1.0.2", "pngparse": "~2.0.1", + "q": "1.4.1", "temporal": "^0.3.8" }, "scripts": { @@ -24,7 +30,10 @@ "oled", "SSD1306" ], - "author": ["Judd Flamm", "Suz Hinton"], + "author": [ + "Judd Flamm", + "Suz Hinton", + ], "license": "MIT", "bugs": { "url": "https://github.com/kd7yva/oled-js-pi/issues" From 1c83fadd84012fd2259dfa28f0784306aace67ef Mon Sep 17 00:00:00 2001 From: Per Johan Groland Date: Thu, 14 Jul 2016 18:34:23 +0900 Subject: [PATCH 8/9] Updated dependencies --- package.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6b43e40..ff2e3fb 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,17 @@ "version": "1.0.5", "description": "NodeJS module for controlling oled devices on the Raspbery Pi (including the SSD1306 OLED screens)", "main": "oled.js", - "dependencies": {"i2c": "~0.2.1"}, + "dependencies": { + "i2c": "0.2.3", + "oled-font-5x7": "1.0.0", + "png-to-lcd": "1.0.3", + "pngparse": "2.0.1" + }, "devDependencies": { "oled-font-5x7": "~1.0.0", "png-to-lcd": "~1.0.2", "pngparse": "~2.0.1", + "q": "1.4.1", "temporal": "^0.3.8" }, "scripts": { @@ -24,7 +30,10 @@ "oled", "SSD1306" ], - "author": ["Judd Flamm", "Suz Hinton"], + "author": [ + "Judd Flamm", + "Suz Hinton" + ], "license": "MIT", "bugs": { "url": "https://github.com/kd7yva/oled-js-pi/issues" From df61173933650418dcc2c5cf3252a0acdad4dbd7 Mon Sep 17 00:00:00 2001 From: Per Johan Groland Date: Thu, 1 Dec 2016 11:33:35 +0900 Subject: [PATCH 9/9] Prepare for republishing on npm --- README.md | 7 +++++-- package.json | 11 ++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f2e8d79..00d14fe 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ OLED screens are really cool - now you can control them with JavaScript! If you haven't already, install [NodeJS](http://nodejs.org/). -`npm install oled-js-pi` +`npm install oled-ssd1306-i2c` ## I2C screens @@ -27,7 +27,7 @@ Hook up I2C compatible oled to the Raspberry Pi. Pins: SDL and SCL. ### I2C example ```javascript -var oled = require('oled-js-pi'); +var oled = require('oled-ssd1306-i2c'); var opts = { width: 128, @@ -312,3 +312,6 @@ Usage: ```javascript oled.update(); ``` + +Forked from https://github.com/kd7yva/oled-js-pi + diff --git a/package.json b/package.json index ff2e3fb..de1df9d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "oled-js-pi", - "version": "1.0.5", + "name": "oled-ssd1306-i2c", + "version": "1.0.6", "description": "NodeJS module for controlling oled devices on the Raspbery Pi (including the SSD1306 OLED screens)", "main": "oled.js", "dependencies": { @@ -21,7 +21,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/kd7yva/oled-js-pi.git" + "url": "https://github.com/perjg/oled_ssd1306_i2c.git" }, "keywords": [ "Raspbery Pi", @@ -32,11 +32,12 @@ ], "author": [ "Judd Flamm", - "Suz Hinton" + "Suz Hinton", + "Per Johan Groland" ], "license": "MIT", "bugs": { - "url": "https://github.com/kd7yva/oled-js-pi/issues" + "url": "https://github.com/perjg/oled_ssd1306_i2c/issues" }, "homepage": "https://github.com/kd7yva/oled-js-pi" }