Skip to content

Instantly share code, notes, and snippets.

@electricimp
Last active December 22, 2017 06:17
Show Gist options
  • Save electricimp/7a3aa2776d440c81164c to your computer and use it in GitHub Desktop.
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
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