Last active
July 10, 2019 16:46
-
-
Save smittytone/20ed962d9f06072bd1c0 to your computer and use it in GitHub Desktop.
Electric Imp Squirrel class for SSD1306 with alternative, proportionally spaced character set, and 128 x 32 and 128 x 64 OLED support
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.1 | |
static HIGH = 1; | |
static LOW = 0; | |
static SSD1306_SETCONTRAST = "\x81"; | |
static SSD1306_DISPLAYALLON_RESUME = "\xA4"; | |
static SSD1306_DISPLAYALLON = "\xA5"; | |
static SSD1306_NORMALDISPLAY = "\xA6"; | |
static SSD1306_INVERTDISPLAY = "\xA7"; | |
static SSD1306_DISPLAYOFF = "\xAE"; | |
static SSD1306_DISPLAYON = "\xAF"; | |
static SSD1306_SETDISPLAYOFFSET = "\xD3"; | |
static SSD1306_SETCOMPINS = "\xDA"; | |
static SSD1306_SETVCOMDETECT = "\xDB"; | |
static SSD1306_SETDISPLAYCLOCKDIV = "\xD5"; | |
static SSD1306_SETPRECHARGE = "\xD9"; | |
static SSD1306_SETMULTIPLEX = "\xA8"; | |
static SSD1306_SETLOWCOLUMN = "\x00"; | |
static SSD1306_SETHIGHCOLUMN = "\x10"; | |
static SSD1306_SETSTARTLINE = "\x40"; | |
static SSD1306_MEMORYMODE = "\x20"; | |
static SSD1306_COLUMNADDR = "\x21"; | |
static SSD1306_PAGEADDR = "\x22"; | |
static SSD1306_COMSCANINC = "\xC0"; | |
static SSD1306_COMSCANDEC = "\xC8"; | |
static SSD1306_SEGREMAP = "\xA1"; | |
static SSD1306_CHARGEPUMP = "\x8D"; | |
static SSD1306_EXTERNALVCC = "\x01"; | |
static SSD1306_SWITCHCAPVCC = "\x02"; | |
// Scrolling statics | |
static SSD1306_ACTIVATE_SCROLL = 0x2F; | |
static SSD1306_DEACTIVATE_SCROLL = 0x2E; | |
static SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3; | |
static SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26; | |
static SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27; | |
static SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29; | |
static SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A; | |
// Constants for the alphanumeric character set | |
// Each character is an proportionally spaced bitmap rendered | |
// as 8-bit values | |
static alpha_count = 96; | |
static charset = [ | |
[0x00, 0x00], // space - Ascii 32 | |
[0xfa], // ! | |
[0xe0, 0xc0, 0x00, 0xe0, 0xc0], // " | |
[0x24, 0x7e, 0x24, 0x7e, 0x24], // # | |
[0x24, 0xd4, 0x56, 0x48], // $ | |
[0xc6, 0xc8, 0x10, 0x26, 0xc6], // % | |
[0x6c, 0x92, 0x6a, 0x04, 0x0a], // & | |
[0xc0], // ' | |
[0x7c, 0x82], // ( | |
[0x82, 0x7c], // ) | |
[0x10, 0x7c, 0x38, 0x7c, 0x10], // * | |
[0x10, 0x10, 0x7c, 0x10, 0x10], // + | |
[0x06, 0x07], // , | |
[0x10, 0x10, 0x10, 0x10, 0x10], // - | |
[0x06, 0x06], // . | |
[0x04, 0x08, 0x10, 0x20, 0x40], // / | |
[0x7c, 0x8a, 0x92, 0xa2, 0x7c], // 0 - Ascii 48 | |
[0x42, 0xfe, 0x02], // 1 | |
[0x46, 0x8a, 0x92, 0x92, 0x62], // 2 | |
[0x44, 0x92, 0x92, 0x92, 0x6c], // 3 | |
[0x18, 0x28, 0x48, 0xfe, 0x08], // 4 | |
[0xf4, 0x92, 0x92, 0x92, 0x8c], // 5 | |
[0x3c, 0x52, 0x92, 0x92, 0x8c], // 6 | |
[0x80, 0x8e, 0x90, 0xa0, 0xc0], // 7 | |
[0x6c, 0x92, 0x92, 0x92, 0x6c], // 8 | |
[0x60, 0x92, 0x92, 0x94, 0x78], // 9 | |
[0x36, 0x36], // : - Ascii 58 | |
[0x36, 0x37], // ; | |
[0x10, 0x28, 0x44, 0x82], // < | |
[0x24, 0x24, 0x24, 0x24, 0x24], // = | |
[0x82, 0x44, 0x28, 0x10], // > | |
[0x60, 0x80, 0x9a, 0x90, 0x60], // ? | |
[0x7c, 0x82, 0xba, 0xaa, 0x78], // @ | |
[0x7e, 0x90, 0x90, 0x90, 0x7e], // A - Ascii 65 | |
[0xfe, 0x92, 0x92, 0x92, 0x6c], // B | |
[0x7c, 0x82, 0x82, 0x82, 0x44], // C | |
[0xfe, 0x82, 0x82, 0x82, 0x7c], // D | |
[0xfe, 0x92, 0x92, 0x92, 0x82], // E | |
[0xfe, 0x90, 0x90, 0x90, 0x80], // F | |
[0x7c, 0x82, 0x92, 0x92, 0x5c], // G | |
[0xfe, 0x10, 0x10, 0x10, 0xfe], // H | |
[0x82, 0xfe, 0x82], // I | |
[0x0c, 0x02, 0x02, 0x02, 0xfc], // J | |
[0xfe, 0x10, 0x28, 0x44, 0x82], // K | |
[0xfe, 0x02, 0x02, 0x02, 0x02], // L | |
[0xfe, 0x40, 0x20, 0x40, 0xfe], // M | |
[0xfe, 0x40, 0x20, 0x10, 0xfe], // N | |
[0x7c, 0x82, 0x82, 0x82, 0x7c], // O | |
[0xfe, 0x90, 0x90, 0x90, 0x60], // P | |
[0x7c, 0x82, 0x92, 0x8c, 0x7a], // Q | |
[0xfe, 0x90, 0x90, 0x98, 0x66], // R | |
[0x64, 0x92, 0x92, 0x92, 0x4c], // S | |
[0x80, 0x80, 0xfe, 0x80, 0x80], // T | |
[0xfc, 0x02, 0x02, 0x02, 0xfc], // U | |
[0xf8, 0x04, 0x02, 0x04, 0xf8], // V | |
[0xfc, 0x02, 0x3c, 0x02, 0xfc], // W | |
[0xc6, 0x28, 0x10, 0x28, 0xc6], // X | |
[0xe0, 0x10, 0x0e, 0x10, 0xe0], // Y | |
[0x86, 0x8a, 0x92, 0xa2, 0xc2], // Z - Ascii 90 | |
[0xfe, 0x82, 0x82], // [ | |
[0x40, 0x20, 0x10, 0x08, 0x04], // \ | |
[0x82, 0x82, 0xfe], // ] | |
[0x20, 0x40, 0x80, 0x40, 0x20], // ^ | |
[0x02, 0x02, 0x02, 0x02, 0x02], // _ | |
[0xc0, 0xe0], // ' | |
[0x04, 0x2a, 0x2a, 0x2a, 0x1e], // a - Ascii 97 | |
[0xfe, 0x22, 0x22, 0x22, 0x1c], // b | |
[0x1c, 0x22, 0x22, 0x22], // c | |
[0x1c, 0x22, 0x22, 0x22, 0xfc], // d | |
[0x1c, 0x2a, 0x2a, 0x2a, 0x10], // e | |
[0x10, 0x7e, 0x90, 0x90, 0x80], // f | |
[0x18, 0x25, 0x25, 0x25, 0x3e], // g | |
[0xfe, 0x20, 0x20, 0x20, 0x1e], // h | |
[0xbe, 0x02], // i | |
[0x02, 0x01, 0x01, 0x21, 0xbe], // j | |
[0xfe, 0x08, 0x14, 0x22], // k | |
[0xfe, 0x02], // l | |
[0x3e, 0x20, 0x18, 0x20, 0x1e], // m | |
[0x3e, 0x20, 0x20, 0x20, 0x1e], // n | |
[0x1c, 0x22, 0x22, 0x22, 0x1c], // o | |
[0x3f, 0x22, 0x22, 0x22, 0x1c], // p | |
[0x1c, 0x22, 0x22, 0x22, 0x3f], // q | |
[0x22, 0x1e, 0x22, 0x20, 0x10], // r | |
[0x12, 0x2a, 0x2a, 0x2a, 0x04], // s | |
[0x20, 0x7c, 0x22, 0x22, 0x04], // t | |
[0x3c, 0x02, 0x02, 0x3e], // u | |
[0x38, 0x04, 0x02, 0x04, 0x38], // v | |
[0x3c, 0x06, 0x0c, 0x06, 0x3c], // w | |
[0x22, 0x14, 0x08, 0x14, 0x22], // x | |
[0x39, 0x05, 0x06, 0x3c], // y | |
[0x26, 0x2a, 0x2a, 0x32], // z - Ascii 122 | |
[0x10, 0x7c, 0x82, 0x82], // { | |
[0xee], // | | |
[0x82, 0x82, 0x7c, 0x10], // } | |
[0x40, 0x80, 0x40, 0x80], // ~ | |
[0x60, 0x90, 0x90, 0x60], // Degrees sign - Ascii 127 | |
] | |
// Zero instance variables ahead of constructor function | |
_i2c_address = 0 | |
_i2c = null | |
_rst = null | |
_buffer = null | |
_gbuffer = 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_rst_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_rst_pin | |
_rst.configure(DIGITAL_OUT) | |
_oled_width = pixel_width | |
_oled_height = pixel_height | |
_buffer = array((_oled_width * _oled_height / 8), 0) | |
_gbuffer = blob(_oled_width * _oled_height) | |
} | |
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_MEMORYMODE + "\x00") // Horizontal addressing mode | |
_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_display() | |
} | |
function set_gfx_cursor(x, y) | |
{ | |
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, col) | |
{ | |
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() | |
{ | |
for (local i = 0 ; i < 512 ; i++) | |
{ | |
_buffer[i] = 0x00; | |
} | |
} | |
function clear_display() | |
{ | |
// Wipes the display buffer by writing 0x00 to all array elements | |
clear_buffer() | |
set_gfx_cursor(0, 0) | |
display() | |
} | |
function inverse(setting) | |
{ | |
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. | |
local ascii = string_to_print[index] - 32 | |
local glyph = charset[ascii] | |
for (local j = 0 ; j < glyph.len() ; j++) | |
{ | |
local c = flip(glyph[j]) | |
data_string = data_string + c.tochar() | |
} | |
data_string = data_string + "\x00" | |
} | |
_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, x, y) | |
{ | |
// 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) | |
set_text_cursor(y, x) | |
local data_string = "" | |
local glyph = charset[ascii_value - 32] | |
for (local j = 0 ; j < glyph.len() ; j++) | |
{ | |
local a = flip(glyph[j]) | |
data_string = data_string + a.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 | |
local i = 0 | |
local j = 0 | |
local a = 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--) | |
{ | |
a = (line_value & math.pow(2, j).tointeger()) | |
if (a > 0) | |
{ | |
output_matrix[7-j] = output_matrix[7-j] + math.pow(2, i).tointeger() | |
} | |
} | |
} | |
return output_matrix | |
} | |
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 | |
set_text_cursor(0, 0) | |
local data_string = "" | |
foreach (byte in pixel_array) | |
{ | |
data_string = data_string + byte.tochar() | |
} | |
_i2c.write(_i2c_address, "\x40" + data_string) | |
} | |
function flip(value) | |
{ | |
local a = 0 | |
local b = 0 | |
for (local i = 0 ; i < 8 ; i++) | |
{ | |
a = value & math.pow(2, i).tointeger() | |
if (a > 0) | |
{ | |
b = b + math.pow(2, 7 - i).tointeger() | |
} | |
} | |
return b | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment