Hello World on SSD1306 using Arduino
After looking through numerous libraries and examples to control the SSD1306 with Arduino, I’ve found nothing simple. There are some really powerful libraries with lots of support. In this post I’ll post the simplest possible “Hello World” example on an SSD1306 using an Arduino Due. Note, if you need to draw shapes, lines, movement you’ll have to extend this example.
Code
Used in Arduino IDE.
Code is simple.
- Hardware Reset
- Initialize the SSD1306
- Write text on each of the rows
Note, the large char[]
used for ASCII text.
/*
Simplest possible program to display text on the SSD1306.
*/
#include <SPI.h>
#define SCREEN_WIDTH 130 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define SSD1306_DC 35
#define SSD1306_CS 33
#define SSD1306_RESET 31
/* Standard ASCII 6x8 font */
const char font6x8[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Space
0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // !
0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // "
0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // #
0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $
0x00, 0x23, 0x13, 0x08, 0x64, 0x62, // %
0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // &
0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // '
0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // (
0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // )
0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // *
0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // +
0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // ,
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // -
0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // .
0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // /
0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0
0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1
0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2
0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3
0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4
0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5
0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6
0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7
0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8
0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9
0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // :
0x00, 0x00, 0x56, 0x36, 0x00, 0x00, // ;
0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // <
0x00, 0x14, 0x14, 0x14, 0x14, 0x14, // =
0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // >
0x00, 0x02, 0x01, 0x51, 0x09, 0x06, // ?
0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, // @
0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A
0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // B
0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C
0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D
0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E
0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F
0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G
0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H
0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I
0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J
0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K
0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L
0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M
0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N
0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O
0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P
0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q
0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R
0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S
0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T
0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U
0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // V
0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W
0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X
0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y
0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z
0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [
0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55, // "\"
0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // ]
0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^
0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _
0x00, 0x00, 0x01, 0x02, 0x04, 0x00, // '
0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // a
0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b
0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c
0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d
0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e
0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f
0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g
0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h
0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i
0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j
0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k
0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l
0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m
0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n
0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o
0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p
0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q
0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r
0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s
0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t
0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u
0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v
0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w
0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x
0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, // y
0x00, 0x44, 0x64, 0x54, 0x4C, 0x44, // z
0x00, 0x00, 0x08, 0x77, 0x00, 0x00, // {
0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, // |
0x00, 0x00, 0x77, 0x08, 0x00, 0x00, // }
0x00, 0x10, 0x08, 0x10, 0x08, 0x00, // ~
0x14, 0x14, 0x14, 0x14, 0x14, 0x14, // DEL
};
uint8_t SSD1306_INIT0[] = {
0xae, // entire display on ignoring ram content, 0xa4 follows
0x20, 0x02, // Horizontal Addressing Mode
0xa6, // set normal display, 0xa7 for inverted
0xa8, 63, // mux to 64
0xd5, 0x80, // clock
0x40, // start line 0
0x2e, // de-activate scroll, activate with 0x2f
0x8d, 0x14, // Enable Charge Pump
0xa4, // resume ram content display following ram content
0xaf // display on in normal mode
};
size_t SSD1306_INIT0_SIZE = sizeof(SSD1306_INIT0);
void ssd1306_reset()
{
digitalWrite(SSD1306_RESET, 1);
delayMicroseconds(4);
digitalWrite(SSD1306_RESET, 0);
delayMicroseconds(4);
digitalWrite(SSD1306_RESET, 1);
delayMicroseconds(4);
}
void spi_send(uint8_t *data, size_t len)
{
Serial.print(len);
digitalWrite(SSD1306_CS, 0);
SPI.transfer(data, len);
digitalWrite(SSD1306_CS, 1);
}
void ssd1306_commands(uint8_t *commands, size_t len)
{
digitalWrite(SSD1306_DC, 0);
spi_send(commands, len);
}
void ssd1306_data(uint8_t *data, size_t len)
{
digitalWrite(SSD1306_DC, 1);
spi_send(data, len);
}
void
ssd1306_select_page(int page, int col)
{
uint8_t page_command[3] = { 0xB0, 0x00, 0x10 };
// row
page &= 0b111; // rows only 0-7
page_command[0] |= page;
// lower column
page_command[1] = col & 0x0f;
// upper column
page_command[2] = (col & 0xf0) >> 4;
page_command[2] |= 0x10;
ssd1306_commands(page_command, 3);
}
void
ssd1306_clear_screen(uint8_t val)
{
uint8_t clear[SCREEN_WIDTH];
for(int i=0; i<SCREEN_HEIGHT/8; i++)
{
memset(clear, val, SCREEN_WIDTH);
ssd1306_select_page(i, 0);
ssd1306_data(clear, SCREEN_WIDTH);
}
}
void
ssd1306_text(int page, int col, char *text)
{
ssd1306_select_page(page,col);
int len = strlen(text);
for(int i=0; i<len; i++)
{
int index = text[i]-32;
index *= 6;
uint8_t *data = (uint8_t *) &font6x8[index];
ssd1306_data(data, 6);
}
}
void setup() {
pinMode(SSD1306_DC, OUTPUT);
pinMode(SSD1306_CS, OUTPUT);
pinMode(SSD1306_RESET, OUTPUT);
SPI.begin();
SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
ssd1306_reset();
ssd1306_commands(SSD1306_INIT0, SSD1306_INIT0_SIZE);
ssd1306_clear_screen(0x00);
ssd1306_text(0,3,"Row 0");
ssd1306_text(1,3,"Row 1");
ssd1306_text(2,3,"Row 2");
ssd1306_text(3,3,"Row 3 - Hello World!");
ssd1306_text(4,3,"Row 4");
ssd1306_text(5,3,"Row 5");
ssd1306_text(6,3,"Row 6");
ssd1306_text(7,3,"Row 7");
}
void loop() { }
Wiring
I didn’t bother with using the chip selects for SPI as we could control the DC# Input with them. I found it simpler to just have a dedicated output pin.
Note, when a pin has #
that means it’s active low. Thus, logic high will activate/enable it not normally with logic high.
SSD1306 | Arduino | Note |
---|---|---|
GND | GND | Ground Pin |
VCC | 5V | +5V Input |
SCK | SPI SCK | SPI Clock |
SDA | COPI | SPI Controller Out / Peripheral In |
RES# | GPIO 31 | SSD1306 Reset |
DC# | GPIO 35 | SSD1306 Data (1) or Control (0) |
CS# | GPIO 33 | SSD1306 Chip Select |