Lloyd Rochester's Geek Blog

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.

Hello World SSD1306 Arduino


Used in Arduino IDE.

Code is simple.

  1. Hardware Reset
  2. Initialize the SSD1306
  3. 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);
  digitalWrite(SSD1306_RESET, 0);
  digitalWrite(SSD1306_RESET, 1);

void spi_send(uint8_t *data, size_t 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);

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);

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);

ssd1306_text(int page, int col, char *text)
  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.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));

  ssd1306_commands(SSD1306_INIT0, SSD1306_INIT0_SIZE);

  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() { }


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
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

#Arduino #Ssd1306