Last active
August 29, 2015 14:02
-
-
Save smittytone/56f94600c93b5d5522ca to your computer and use it in GitHub Desktop.
Electric Imp Squirrel class for the SSD1306 OLED controller
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 SSD1306_OLED | |
{ | |
// 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 | |
alpha_count = 107; | |
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 | |
_i2c_address = 0 | |
_i2c = null | |
_rst = null | |
_buffer = null | |
_pixel_cursor_x = 0 | |
_pixel_cursor_y = 0 | |
_text_cursor_x = 0 | |
_text_cursor_y = 0 | |
_oled_width = 0 | |
_oled_height = 0 | |
constructor(imp_i2c_bus, address, imp_reset_pin, pixel_width = 128, pixel_height = 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 = imp_i2c_bus | |
_i2c_address = address | |
_i2c.configure(CLOCK_SPEED_400_KHZ) | |
_rst = imp_reset_pin | |
_rst.configure(DIGITAL_OUT) | |
_oled_width = pixel_width | |
_oled_height = pixel_height | |
_buffer = array((_oled_width * _oled_height / 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(_i2c_address, "\x00" + SSD1306_DISPLAYOFF) | |
_i2c.write(_i2c_address, "\x00" + SSD1306_SETDISPLAYCLOCKDIV + "\x80") | |
_i2c.write(_i2c_address, "\x00" + SSD1306_SETMULTIPLEX + "\x1F") | |
_i2c.write(_i2c_address, "\x00" + SSD1306_SETDISPLAYOFFSET + "\x00") | |
_i2c.write(_i2c_address, "\x00" + SSD1306_SETSTARTLINE) | |
_i2c.write(_i2c_address, "\x00" + SSD1306_CHARGEPUMP + "\x14") | |
_i2c.write(_i2c_address, "\x00" + SSD1306_ADDRESSMODE + "\x00") | |
_i2c.write(_i2c_address, "\x00" + SSD1306_SEGREMAP) | |
_i2c.write(_i2c_address, "\x00" + SSD1306_COMSCANDEC) | |
_i2c.write(_i2c_address, "\x00" + SSD1306_SETCOMPINS + "\x02") | |
_i2c.write(_i2c_address, "\x00" + SSD1306_SETCONTRAST + "\x8F") | |
_i2c.write(_i2c_address, "\x00" + SSD1306_SETPRECHARGE + "\xF1") | |
_i2c.write(_i2c_address, "\x00" + SSD1306_SETVCOMDETECT + "\x40") | |
_i2c.write(_i2c_address, "\x00" + SSD1306_DISPLAYALLON_RESUME) | |
_i2c.write(_i2c_address, "\x00" + SSD1306_NORMALDISPLAY) | |
_i2c.write(_i2c_address, "\x00" + SSD1306_DISPLAYON) | |
// Clear the screen | |
clear_display() | |
} | |
function set_gfx_cursor(x = 0, y = 0) | |
{ | |
if (x < 0 || x > 127 || y < 0 || y > 31) return | |
_pixel_cursor_x = x | |
_pixel_cursor_y = y | |
_i2c.write(_i2c_address, "\x00" + SSD1306_COLUMNADDR) | |
_i2c.write(_i2c_address, "\x00" + _pixel_cursor_x.tochar()) | |
_i2c.write(_i2c_address, "\x00" + (_oled_width - 1).tochar()) | |
_i2c.write(_i2c_address, "\x00" + SSD1306_PAGEADDR) | |
_i2c.write(_i2c_address, "\x00" + _pixel_cursor_y.tochar()) | |
_i2c.write(_i2c_address, "\x00" + (_oled_height - 1).tochar()) | |
} | |
function set_text_cursor(row = 0, col = 0) | |
{ | |
if (row < 0 || row > 3 || col < 0 || col > 15) return | |
_text_cursor_x = col | |
_text_cursor_y = row | |
local a = _text_cursor_x * 8 | |
local b = _text_cursor_y | |
local c = "\x03" | |
if (_oled_height == 64) c = "\x07" | |
_i2c.write(_i2c_address, "\x00" + SSD1306_COLUMNADDR + a.tochar() + (_oled_width - 1).tochar()) | |
_i2c.write(_i2c_address, "\x00" + SSD1306_PAGEADDR + b.tochar() + c) | |
} | |
function display() | |
{ | |
// Writes the display buffer to screen as a single I2C string | |
local data_string = "" | |
for (local i = 0; i < (_oled_width * _oled_height / 8) ; i++) | |
{ | |
data_string = data_string + _buffer[i].tochar() | |
} | |
_i2c.write(_i2c_address, "\x40" + data_string) | |
} | |
function clear_buffer() | |
{ | |
// Wipes the display buffer by writing 0x00 to all array elements | |
for (local i = 0 ; i < 512 ; i++) | |
{ | |
_buffer[i] = 0x00; | |
} | |
} | |
function clear_display() | |
{ | |
// Clear the display: zero the buffer then write it to the screen | |
set_gfx_cursor(0, 0) | |
clear_buffer() | |
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(_i2c_address, "\x00" + SSD1306_INVERTDISPLAY) | |
} | |
else | |
{ | |
_i2c.write(_i2c_address, "\x00" + SSD1306_NORMALDISPLAY) | |
} | |
} | |
function print_text(string_to_print) | |
{ | |
// Display the input string on the display | |
// Parameter: | |
// 1. String to print | |
local data_string = "" | |
foreach (index, character in string_to_print) | |
{ | |
// 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 = string_to_print[index] - 32 | |
local glyph = rotate_matrix(charset[ascii]) | |
for (local j = 0 ; j < 8 ; j++) | |
{ | |
data_string = data_string + glyph[j].tochar() | |
} | |
} | |
_i2c.write(_i2c_address, "\x40" + data_string) | |
} | |
function print_icon(icon_array) | |
{ | |
// 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 = rotate_matrix(icon_array) | |
for (local i = 0 ; i < 8 ; i++) | |
{ | |
data_string = data_string + icon[i].tochar() | |
} | |
_i2c.write(_i2c_address, "\x40" + data_string) | |
} | |
function print_char(ascii_value = 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) | |
local data_string = "" | |
local glyph = rotate_matrix(charset[ascii_value - 32]) | |
set_text_cursor(row, col) | |
for (local j = 0 ; j < 8 ; j++) | |
{ | |
data_string = data_string + glyph[j].tochar() | |
} | |
_i2c.write(_i2c_address, "\x40" + data_string) | |
} | |
function print_bitmap(pixel_array) | |
{ | |
// 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 | |
local data_string = "" | |
set_gfx_cursor(0, 0) | |
foreach (byte in pixel_array) | |
{ | |
data_string = data_string + byte.tochar() | |
} | |
_i2c.write(_i2c_address, "\x40" + data_string) | |
} | |
function rotate_matrix(input_matrix) | |
{ | |
// 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 line_value = 0 | |
local output_matrix = [0,0,0,0,0,0,0,0] | |
for (i = 0 ; i < 8 ; i++) | |
{ | |
line_value = input_matrix[i] | |
for (j = 7 ; j > -1 ; j--) | |
{ | |
bit = (line_value & math.pow(2, j).tointeger()) | |
if (bit > 0) output_matrix[7-j] = output_matrix[7-j] + math.pow(2, i).tointeger() | |
} | |
} | |
return output_matrix | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment