Skip to content

Instantly share code, notes, and snippets.

@ElectricImpSampleCode
Last active August 10, 2017 23:30
Show Gist options
  • Save ElectricImpSampleCode/94ec295cd1a71d9b8061add9565ce494 to your computer and use it in GitHub Desktop.
Save ElectricImpSampleCode/94ec295cd1a71d9b8061add9565ce494 to your computer and use it in GitHub Desktop.
Squirrel Class to control Adafruit SSD1306-based 0.96in 128 x 32/64 OLED
// Constants for the OLED and the SSD1306
const SSD1306_SETCONTRAST = "\x81";
const SSD1306_DISPLAYALLON_RESUME = "\xA4";
const SSD1306_DISPLAYALLON = "\xA5";
const SSD1306_NORMALDISPLAY = "\xA6";
const SSD1306_INVERTDISPLAY = "\xA7";
const SSD1306_DISPLAYOFF = "\xAE";
const SSD1306_DISPLAYON = "\xAF";
const SSD1306_SETDISPLAYOFFSET = "\xD3";
const SSD1306_SETCOMPINS = "\xDA";
const SSD1306_SETVCOMDETECT = "\xDB";
const SSD1306_SETDISPLAYCLOCKDIV = "\xD5";
const SSD1306_SETPRECHARGE = "\xD9";
const SSD1306_SETMULTIPLEX = "\xA8";
const SSD1306_SETLOWCOLUMN = "\x00";
const SSD1306_SETHIGHCOLUMN = "\x10";
const SSD1306_SETSTARTLINE = "\x40";
const SSD1306_ADDRESSMODE = "\x20";
const SSD1306_COLUMNADDR = "\x21";
const SSD1306_PAGEADDR = "\x22";
const SSD1306_COMSCANINC = "\xC0";
const SSD1306_COMSCANDEC = "\xC8";
const SSD1306_SEGREMAP = "\xA1";
const SSD1306_CHARGEPUMP = "\x8D";
const SSD1306_EXTERNALVCC = "\x01";
const SSD1306_SWITCHCAPVCC = "\x02";
// Constants for scolling, as yet unimplemented
const SSD1306_ACTIVATE_SCROLL = 0x2F;
const SSD1306_DEACTIVATE_SCROLL = 0x2E;
const SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3;
const SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26;
const SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27;
const SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29;
const SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A;
const HIGH = 1;
const LOW = 0;
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
// Constants for the alphanumeric character set
// Each character is an 8 x 8 bitmap rendered
// as eight 8-bit values
static 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
_i2c.write(_i2cAddress, "\x00" + (setting ? SSD1306_INVERTDISPLAY : 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