Last active
December 22, 2017 06:17
-
-
Save electricimp/7a3aa2776d440c81164c to your computer and use it in GitHub Desktop.
Squirrel Class to control Adafruit SSD1306-based 0.96in 128 x 32/64 OLED
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SSD1306OLED | |
{ | |
// Squirrel Class for Solomon SSD1306 OLED controller chip | |
// [http://www.adafruit.com/datasheets/SSD1306.pdf] | |
// As used on the Adafruit SSD1306 I2C breakout board | |
// [http://www.adafruit.com/products/931] | |
// Bus: I2C | |
// Code by Tony Smith (@smittytone) June 2014 | |
// Version 1.0.2 | |
HIGH = 1 | |
LOW = 0 | |
// Constants for the OLED and the SSD1306 | |
SSD1306_SETCONTRAST = "\x81" | |
SSD1306_DISPLAYALLON_RESUME = "\xA4" | |
SSD1306_DISPLAYALLON = "\xA5" | |
SSD1306_NORMALDISPLAY = "\xA6" | |
SSD1306_INVERTDISPLAY = "\xA7" | |
SSD1306_DISPLAYOFF = "\xAE" | |
SSD1306_DISPLAYON = "\xAF" | |
SSD1306_SETDISPLAYOFFSET = "\xD3" | |
SSD1306_SETCOMPINS = "\xDA" | |
SSD1306_SETVCOMDETECT = "\xDB" | |
SSD1306_SETDISPLAYCLOCKDIV = "\xD5" | |
SSD1306_SETPRECHARGE = "\xD9" | |
SSD1306_SETMULTIPLEX = "\xA8" | |
SSD1306_SETLOWCOLUMN = "\x00" | |
SSD1306_SETHIGHCOLUMN = "\x10" | |
SSD1306_SETSTARTLINE = "\x40" | |
SSD1306_ADDRESSMODE = "\x20" | |
SSD1306_COLUMNADDR = "\x21" | |
SSD1306_PAGEADDR = "\x22" | |
SSD1306_COMSCANINC = "\xC0" | |
SSD1306_COMSCANDEC = "\xC8" | |
SSD1306_SEGREMAP = "\xA1" | |
SSD1306_CHARGEPUMP = "\x8D" | |
SSD1306_EXTERNALVCC = "\x01" | |
SSD1306_SWITCHCAPVCC = "\x02" | |
// Constants for scolling, as yet unimplemented | |
SSD1306_ACTIVATE_SCROLL = 0x2F | |
SSD1306_DEACTIVATE_SCROLL = 0x2E | |
SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3 | |
SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26 | |
SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27 | |
SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29 | |
SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A | |
// Constants for the alphanumeric character set | |
// Each character is an 8 x 8 bitmap rendered | |
// as eight 8-bit values | |
charset = [ | |
[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // Space - Ascii 32 | |
[0x00,0x10,0x10,0x10,0x10,0x00,0x10,0x00], // ! | |
[0x00,0x24,0x24,0x00,0x00,0x00,0x00,0x00], // ” | |
[0x00,0x24,0x7E,0x24,0x24,0x7E,0x24,0x00], // # | |
[0x00,0x08,0x3E,0x28,0x3E,0x0A,0x3E,0x08], // $ | |
[0x00,0x62,0x64,0x08,0x10,0x26,0x46,0x00], // % | |
[0x00,0x10,0x28,0x10,0x2A,0x44,0x3A,0x00], // & | |
[0x00,0x08,0x10,0x00,0x00,0x00,0x00,0x00], // ‘ | |
[0x00,0x04,0x08,0x08,0x08,0x08,0x04,0x00], // ( | |
[0x00,0x20,0x10,0x10,0x10,0x10,0x20,0x00], // ) | |
[0x00,0x00,0x14,0x08,0x3E,0x08,0x14,0x00], // * | |
[0x00,0x00,0x08,0x08,0x3E,0x08,0x08,0x00], // + | |
[0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x10], // , | |
[0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00], // - | |
[0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00], // . | |
[0x00,0x02,0x04,0x08,0x10,0x20,0x40,0x00], // / | |
[0x00,0x3C,0x46,0x4A,0x52,0x62,0x3C,0x00], // 0 - Ascii 48 | |
[0x00,0x30,0x50,0x10,0x10,0x10,0x7C,0x00], // 1 | |
[0x00,0x3C,0x42,0x02,0x3C,0x40,0x7E,0x00], // 2 | |
[0x00,0x3C,0x42,0x0C,0x02,0x42,0x3C,0x00], // 3 | |
[0x00,0x08,0x18,0x28,0x48,0x7E,0x08,0x00], // 4 | |
[0x00,0x7E,0x40,0x7C,0x02,0x42,0x3C,0x00], // 5 | |
[0x00,0x3C,0x40,0x7C,0x42,0x42,0x3C,0x00], // 6 | |
[0x00,0x7E,0x02,0x04,0x08,0x10,0x10,0x00], // 7 | |
[0x00,0x3C,0x42,0x3C,0x42,0x42,0x3C,0x00], // 8 | |
[0x00,0x3C,0x42,0x42,0x3E,0x02,0x3C,0x00], // 9 | |
[0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00], // : - Ascii 58 | |
[0x00,0x00,0x10,0x00,0x00,0x10,0x10,0x20], // ; | |
[0x00,0x00,0x04,0x08,0x10,0x08,0x04,0x00], // < | |
[0x00,0x00,0x00,0x3E,0x00,0x3E,0x00,0x00], // = | |
[0x00,0x00,0x10,0x08,0x04,0x08,0x10,0x00], // > | |
[0x00,0x3C,0x42,0x04,0x08,0x00,0x08,0x00], // ? | |
[0x00,0x3C,0x4A,0x56,0x5E,0x40,0x3C,0x00], // @ | |
[0x00,0x3C,0x42,0x42,0x7E,0x42,0x42,0x00], // A - Ascii 65 | |
[0x00,0x7C,0x42,0x7C,0x42,0x42,0x7C,0x00], // B | |
[0x00,0x3C,0x42,0x40,0x40,0x42,0x3C,0x00], // C | |
[0x00,0x78,0x44,0x42,0x42,0x44,0x78,0x00], // D | |
[0x00,0x7E,0x40,0x7C,0x40,0x40,0x7E,0x00], // E | |
[0x00,0x7E,0x40,0x7C,0x40,0x40,0x40,0x00], // F | |
[0x00,0x3C,0x42,0x40,0x4E,0x42,0x3C,0x00], // G | |
[0x00,0x42,0x42,0x7E,0x42,0x42,0x42,0x00], // H | |
[0x00,0x7C,0x10,0x10,0x10,0x10,0x7C,0x00], // I | |
[0x00,0x02,0x02,0x02,0x02,0x42,0x3C,0x00], // J | |
[0x00,0x44,0x48,0x70,0x48,0x44,0x42,0x00], // K | |
[0x00,0x40,0x40,0x40,0x40,0x40,0x7E,0x00], // L | |
[0x00,0x42,0x66,0x5A,0x42,0x42,0x42,0x00], // M | |
[0x00,0x42,0x62,0x52,0x4A,0x46,0x42,0x00], // N | |
[0x00,0x3C,0x42,0x42,0x42,0x42,0x3C,0x00], // O | |
[0x00,0x7C,0x42,0x42,0x7C,0x40,0x40,0x00], // P | |
[0x00,0x3C,0x42,0x42,0x52,0x4A,0x3C,0x00], // Q | |
[0x00,0x7C,0x42,0x42,0x7C,0x44,0x42,0x00], // R | |
[0x00,0x3C,0x40,0x3C,0x02,0x42,0x3C,0x00], // S | |
[0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x00], // T | |
[0x00,0x42,0x42,0x42,0x42,0x42,0x3C,0x00], // U | |
[0x00,0x42,0x42,0x42,0x42,0x24,0x18,0x00], // V | |
[0x00,0x42,0x42,0x42,0x42,0x5A,0x24,0x00], // W | |
[0x00,0x42,0x24,0x18,0x18,0x24,0x42,0x00], // X | |
[0x00,0x44,0x28,0x10,0x10,0x10,0x10,0x00], // Y | |
[0x00,0x7E,0x04,0x08,0x10,0x20,0x7E,0x00], // Z - Ascii 90 | |
[0x00,0x0E,0x08,0x08,0x08,0x08,0x0E,0x00], // [ | |
[0x00,0x00,0x40,0x20,0x10,0x08,0x04,0x00], // \ | |
[0x00,0x70,0x10,0x10,0x10,0x10,0x70,0x00], // ] | |
[0x00,0x10,0x38,0x54,0x10,0x10,0x10,0x00], // ^ | |
[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF], // _ | |
[0x00,0x1C,0x22,0x78,0x20,0x20,0x7E,0x00], // £ | |
[0x00,0x00,0x38,0x04,0x3C,0x44,0x3C,0x00], // a - Ascii 97 | |
[0x00,0x40,0x40,0x78,0x44,0x44,0x78,0x00], // b | |
[0x00,0x00,0x38,0x40,0x40,0x40,0x38,0x00], // c | |
[0x00,0x04,0x04,0x3C,0x44,0x44,0x3C,0x00], // d | |
[0x00,0x00,0x38,0x44,0x78,0x40,0x3C,0x00], // e | |
[0x00,0x30,0x40,0x60,0x40,0x40,0x40,0x00], // f | |
[0x00,0x3C,0x44,0x44,0x3C,0x04,0x38,0x00], // g | |
[0x00,0x40,0x40,0x40,0x78,0x44,0x44,0x00], // h | |
[0x00,0x20,0x00,0x60,0x20,0x20,0x70,0x00], // i | |
[0x00,0x08,0x00,0x08,0x08,0x48,0x30,0x00], // j | |
[0x00,0x40,0x50,0x60,0x60,0x50,0x48,0x00], // k | |
[0x00,0x40,0x40,0x40,0x40,0x40,0x30,0x00], // l | |
[0x00,0x00,0x68,0x54,0x54,0x54,0x54,0x00], // m | |
[0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x00], // n | |
[0x00,0x00,0x38,0x44,0x44,0x44,0x38,0x00], // o | |
[0x00,0x78,0x44,0x44,0x78,0x40,0x40,0x00], // p | |
[0x00,0x3C,0x44,0x44,0x3C,0x04,0x06,0x00], // q | |
[0x00,0x00,0x1C,0x20,0x20,0x20,0x20,0x00], // r | |
[0x00,0x00,0x38,0x40,0x38,0x04,0x78,0x00], // s | |
[0x00,0x20,0x70,0x20,0x20,0x20,0x18,0x00], // t | |
[0x00,0x00,0x44,0x44,0x44,0x44,0x38,0x00], // u | |
[0x00,0x00,0x44,0x44,0x28,0x28,0x10,0x00], // v | |
[0x00,0x00,0x44,0x54,0x54,0x54,0x28,0x00], // w | |
[0x00,0x00,0x44,0x28,0x10,0x28,0x44,0x00], // x | |
[0x00,0x00,0x44,0x44,0x3C,0x04,0x38,0x00], // y | |
[0x00,0x00,0x7C,0x08,0x10,0x20,0x7C,0x00], // z - Ascii 122 | |
[0x00,0x0E,0x08,0x30,0x08,0x08,0x0E,0x00], // { | |
[0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00], // | | |
[0x00,0x70,0x10,0x0C,0x10,0x10,0x70,0x00], // } | |
[0x00,0x14,0x28,0x00,0x00,0x00,0x00,0x00], // ~ | |
[0x3C,0x42,0x99,0xA1,0xA1,0x99,0x42,0x3C], // © - Ascii 127 | |
[0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF], // Block Graphic 1 | |
[0x0F,0x0F,0x0F,0x0F,0xFF,0xFF,0xFF,0xFF], | |
[0xF0,0xF0,0xF0,0xF0,0xFF,0xFF,0xFF,0xFF], | |
[0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF], | |
[0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F], | |
[0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F], | |
[0xF0,0xF0,0xF0,0xF0,0x0F,0x0F,0x0F,0x0F], | |
[0x00,0x00,0x00,0x00,0x0F,0x0F,0x0F,0x0F], | |
[0xFF,0xFF,0xFF,0xFF,0x55,0xAA,0x55,0xAA], | |
[0xAA,0x55,0xAA,0x55,0xFF,0xFF,0xFF,0xFF], | |
[0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55] // Block Graphic 11 | |
] | |
// Zero instance variables ahead of constructor function | |
_i2cAddress = 0 | |
_i2c = null | |
_rst = null | |
_buffer = null | |
_alphaCount = 107 | |
_pixelCursor_x = 0 | |
_pixelCursor_y = 0 | |
_textCursor_x = 0 | |
_textCursor_y = 0 | |
_oledWidth = 0 | |
_oledHeight = 0 | |
constructor(bus, address, resetPin, pixelWidth = 128, pixelHeight = 32) | |
{ | |
// Parameters: | |
// 1. The chosen imp I2C bus object | |
// 2. The OLED's 7-bit I2C address as an integer | |
// 3. The chosen imp pin object to control the Reset line | |
// 4. OLED pixel width | |
// 5. OLED pixel height | |
_i2c = bus | |
_i2cAddress = address | |
_i2c.configure(CLOCK_SPEED_400_KHZ) | |
_rst = resetPin | |
_rst.configure(DIGITAL_OUT) | |
_oledWidth = pixelWidth | |
_oledHeight = pixelHeight | |
_buffer = array((_oledWidth * _oledHeight / 8) , 0) | |
} | |
function init() | |
{ | |
// Toggle the RST pin over 1ms + 10ms | |
_rst.write(HIGH) | |
imp.sleep(0.001) | |
_rst.write(LOW) | |
imp.sleep(0.01) | |
_rst.write(HIGH) | |
// Write the display settings | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_DISPLAYOFF) | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_SETDISPLAYCLOCKDIV + "\x80") | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_SETMULTIPLEX + "\x1F") | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_SETDISPLAYOFFSET + "\x00") | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_SETSTARTLINE) | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_CHARGEPUMP + "\x14") | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_ADDRESSMODE + "\x00") | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_SEGREMAP) | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_COMSCANDEC) | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_SETCOMPINS + "\x02") | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_SETCONTRAST + "\x8F") | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_SETPRECHARGE + "\xF1") | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_SETVCOMDETECT + "\x40") | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_DISPLAYALLON_RESUME) | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_NORMALDISPLAY) | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_DISPLAYON) | |
// Clear the screen | |
clearDisplay() | |
} | |
function setGraphicsCursor(x = 0, y = 0) | |
{ | |
if (x < 0 || x > 127 || y < 0 || y > 31) return | |
_pixelCursor_x = x | |
_pixelCursor_y = y | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_COLUMNADDR) | |
_i2c.write(_i2cAddress, "\x00" + _pixelCursor_x.tochar()) | |
_i2c.write(_i2cAddress, "\x00" + (_oledWidth - 1).tochar()) | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_PAGEADDR) | |
_i2c.write(_i2cAddress, "\x00" + _pixelCursor_y.tochar()) | |
_i2c.write(_i2cAddress, "\x00" + (_oledHeight - 1).tochar()) | |
} | |
function setTextCursor(row = 0, col = 0) | |
{ | |
if (row < 0 || row > 3 || col < 0 || col > 15) return | |
_textCursor_x = col | |
_textCursor_y = row | |
local a = _textCursor_x * 8 | |
local b = _textCursor_y | |
local c = "\x03" | |
if (_oledHeight == 64) c = "\x07" | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_COLUMNADDR + a.tochar() + (_oledWidth - 1).tochar()) | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_PAGEADDR + b.tochar() + c) | |
} | |
function display() | |
{ | |
// Writes the display buffer to screen as a single I2C string | |
local dataString = "" | |
for (local i = 0; i < (_oledWidth * _oledHeight / 8) ; i++) | |
{ | |
dataString = dataString + _buffer[i].tochar() | |
} | |
_i2c.write(_i2cAddress, "\x40" + dataString) | |
} | |
function clearBuffer() | |
{ | |
// Wipes the display buffer by writing 0x00 to all array elements | |
for (local i = 0 ; i < 512 ; i++) | |
{ | |
_buffer[i] = 0x00 | |
} | |
} | |
function clearDisplay() | |
{ | |
// Clear the display: zero the buffer then write it to the screen | |
setGraphicsCursor(0, 0) | |
clearBuffer() | |
display() | |
} | |
function inverse(setting = false) | |
{ | |
// Set the display to black-on-white or white-on-black | |
// Parameter: | |
// 1. Boolean value determining inverse state of display | |
// 'true' to inverse dislay, 'false' to put it back | |
if (setting) | |
{ | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_INVERTDISPLAY) | |
} | |
else | |
{ | |
_i2c.write(_i2cAddress, "\x00" + SSD1306_NORMALDISPLAY) | |
} | |
} | |
function printText(stringToPrint) | |
{ | |
// Display the input string on the display | |
// Parameter: | |
// 1. String to print | |
if (stringToPrint = "" || stringToPrint == null) return | |
local dataString = "" | |
foreach (index, character in stringToPrint) | |
{ | |
// Read in each character in the source string, determine its | |
// Ascii value and use this to find the character's bitmap array | |
// in the charset. This array is rotated to match the byte | |
// structure of the OLED's on-board buffer, then its byte values | |
// are added to an I2C data string and transmitted to OLED RAM | |
local ascii = stringToPrint[index] - 32 | |
local glyph = rotateMatrix(charset[ascii]) | |
for (local j = 0 ; j < 8 ; j++) | |
{ | |
dataString = dataString + glyph[j].tochar() | |
} | |
} | |
_i2c.write(_i2cAddress, "\x40" + dataString) | |
} | |
function printIcon(iconArray) | |
{ | |
// Display a custom 8 x 8 character icon on the display | |
// Parameter: | |
// 1. Array of eight 8-bit values forming the icon bitmap | |
local data_string = "" | |
local icon = rotateMatrix(iconArray) | |
for (local i = 0 ; i < 8 ; i++) | |
{ | |
dataString = dataString + icon[i].tochar() | |
} | |
_i2c.write(_i2cAddress, "\x40" + dataString) | |
} | |
function printChar(asciiVal = 32, row = 0, col = 0) | |
{ | |
// Display a specific character at a set position | |
// Parameters: | |
// 1. Integer Ascii code of the character to print | |
// 2. Integer row value (0-3) | |
// 3. Integer column value (0-15) | |
asciiVal = asciVal - 32 | |
if (asciiVal < 0 || asciiVal > _alphaCount) return | |
local dataString = "" | |
local glyph = rotateMatrix(charset[asciiVal]) | |
setTextCursor(row, col) | |
for (local j = 0 ; j < 8 ; j++) | |
{ | |
dataString = dataString + glyph[j].tochar() | |
} | |
_i2c.write(_i2cAddress, "\x40" + dataString) | |
} | |
function printBitmap(pixelArray) | |
{ | |
// Displays a preformatted 128 x 32 or 128 x 64 bitmap image stored in a byte array | |
// Note each byte consists of a row-high set of *vertical* pixels | |
// Parameter: | |
// 1. Array of 512 or 1024 8-bit values | |
if (pixelArray == null || pixelArray.len() <= 0) return | |
local dataString = "" | |
setGraphicsCursor(0, 0) | |
foreach (byte in pixelArray) | |
{ | |
dataString = dataString + byte.tochar() | |
} | |
_i2c.write(_i2cAddress, "\x40" + dataString) | |
} | |
function rotateMatrix(inputMatrix) | |
{ | |
// Rotate the character matrix through 90 degrees anti-clockwise | |
// Used if the OLED matrix pins are connected directly to a breadboard | |
// Returns a new array | |
local i = 0 | |
local j = 0 | |
local bit = 0 | |
local lineVal = 0 | |
local outputMatrix = [0,0,0,0,0,0,0,0] | |
for (i = 0 ; i < 8 ; i++) | |
{ | |
lineVal = inputMatrix[i] | |
for (j = 7 ; j > -1 ; j--) | |
{ | |
bit = (lineVal & math.pow(2, j).tointeger()) | |
if (bit > 0) outputMatrix[7-j] = outputMatrix[7-j] + math.pow(2, i).tointeger() | |
} | |
} | |
return outputMatrix | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment