Skip to content

Instantly share code, notes, and snippets.

@smittytone
Last active August 29, 2015 14:02
Show Gist options
  • Save smittytone/56f94600c93b5d5522ca to your computer and use it in GitHub Desktop.
Save smittytone/56f94600c93b5d5522ca to your computer and use it in GitHub Desktop.
Electric Imp Squirrel class for the SSD1306 OLED controller
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