Project

General

Profile

Using an I2C OLED display

Introduction

I2C OLED displays are a family of easily available modules generally offering a monochrome array of 128x32 or 128x64 pixels.
This family of displays is defined by the controller chip providing the digital interface: the SSD1306.
A library is provided as part of the Dooba SDK to use these devices: ssd1306.

This page will present some usage examples for such OLED displays.

Wiring

Most devices only require connecting the I2C bus.
A few devices might expose a RESET line.

Note: It is usually a good idea to add 10k pull-up resistors on the I2C bus (SCL & SDA lines).

Using the library

Before we can actually access the library from our code, we need to add it as a dependency.
Unless this is already the case, we're also going to be initializing the I2C bus in our application. This means we need to include the i2c library in our dependency list.
This is done in the project definition file - dfe.conf.

# Firmware Element Configuration
# ...
deps:
  - ssd1306
  - i2c

Initialization

Before we try to display anything, we need to initialize the OLED.
Also, if this is not already done, we need to initialize the I2C bus.

#include <i2c/i2c.h>
#include <ssd1306/ssd1306.h>

// Main Entry Point
void main()
{
    // Initialize I2C bus
    i2c_init();

    // Initialize OLED display
    ssd1306_init();

    // ...

    // Never return from main
    for(;;);
}

Drawing

Everything is drawn to a framebuffer, which can be written to the display at any time.
Like many graphics libraries, the main use case is usually to integrate the drawing / refreshing in a main loop.

The code example below assumes the eloop library has been included (to provide a main loop), and performs the following at each iteration:
  • Clears the framebuffer
  • Draws some text
  • Writes the framebuffer to the display
#include <i2c/i2c.h>
#include <ssd1306/ssd1306.h>
#include <ssd1306/fb.h>
#include <ssd1306/txt.h>

// Called once at initialization
void init()
{
    // Initialize I2C Bus and Display
    i2c_init();
    ssd1306_init();
}

// Called repeatedly until power-off
void loop()
{
    // Clear Framebuffer
    ssd1306_fb_clear();

    // Draw some text
    ssd1306_txt((uint8_t *)"", 0, 0);

    // Write Framebuffer to display
    ssd1306_draw();
}

The sections below will present the methods used in the example above.

Clearing the framebuffer

Clearing the framebuffer can be done with the ssd1306_fb_clear method (from ssd1306/fb.h):

void ssd1306_fb_clear()

// Clear the framebuffer
ssd1306_fb_clear();

Writing the framebuffer to the display

This is accomplished using the ssd1306_draw method (from ssd1306/ssd1306.h):

void ssd1306_draw()

// Draw the framebuffer to the display
ssd1306_draw();

Drawing a single pixel

Drawing a single pixel can be done using the ssd1306_fb_plot method (from ssd1306/fb.h):

void ssd1306_fb_plot(uint8_t x, uint8_t y, uint8_t v)
Arguments:

  • x -> X Position in pixels (from the left)
  • y -> Y Position in pixels (from the top)
  • v -> Pixel value (0 = dark / 1 = light)

// Draw a square
ssd1306_fb_plot(32, 8, 1);
ssd1306_fb_plot(33, 8, 1);
ssd1306_fb_plot(34, 8, 1);
ssd1306_fb_plot(32, 10, 1);
ssd1306_fb_plot(33, 10, 1);
ssd1306_fb_plot(34, 10, 1);
ssd1306_fb_plot(32, 9, 1);
ssd1306_fb_plot(34, 9, 1);

Note: x and y arguments MUST be within the framebuffer dimensions, or unexpected things may happen.
This rule only applies to single-pixel plotting (using ssd1306_fb_plot), the other methods presented below can be given negative or off-screen coordinates as arguments.

Drawing a line

Drawing a line can be done using the ssd1306_gfx_line method (from ssd1306/gfx.h):

void ssd1306_gfx_line(int from_x, int from_y, int to_x, int to_y, uint8_t v)
Arguments:

  • from_x -> Start X Position in pixels (from the left)
  • from_y -> Start Y Position in pixels (from the top)
  • to_x -> End X Position in pixels (from the left)
  • to_y -> End Y Position in pixels (from the top)
  • v -> Pixel value (0 = dark / 1 = light)

Drawing a rectangle

Drawing a rectangle can be done using the ssd1306_gfx_rect method (from ssd1306/gfx.h):

void ssd1306_gfx_rect(int x, int y, int w, int h, uint8_t v)
Arguments:

  • x -> Start X Position in pixels (from the left)
  • y -> Start Y Position in pixels (from the top)
  • w -> Width in pixels (from the left)
  • h -> Height in pixels (from the top)
  • v -> Pixel value (0 = dark / 1 = light)

Drawing a polygon

Drawing a polygon can be done using the ssd1306_gfx_ngon method (from ssd1306/gfx.h):

void ssd1306_gfx_ngon(uint8_t v, uint8_t n, ...)
Arguments:

  • v -> Pixel value (0 = dark / 1 = light)
  • n -> Number of lines to draw (determines number of additional variable arguments).

Coordinates should be passed as x0, y0, x1, y1, x2, y2, ...
Lines will be drawn:
  • x0;y0 -> x1;y1
  • x1;y1 -> x2;y2

Drawing a circle

Drawing a circle can be done using the ssd1306_gfx_circle method (from ssd1306/gfx.h):

void ssd1306_gfx_circle(int x, int y, uint8_t r, uint8_t v)
Arguments:

  • x -> Center X Position in pixels (from the left)
  • y -> Center Y Position in pixels (from the top)
  • r -> Radius in pixels
  • v -> Pixel value (0 = dark / 1 = light)

Drawing text

ASCII Text can be drawn to the framebuffer using the ssd1306_txt methods (from ssd1306/txt.h).
The ssd1306_txt_n method is a safe alternative to ssd1306_txt, requiring an additional argument: the length of the string.
The ssd1306_txt_f method allows printing a format string similar to the classic "printf".

void ssd1306_txt(void *s, int x, int y)
Arguments:

  • s -> Pointer to NULL-terminated string
  • x -> X Position in pixels (from the left, can be negative)
  • y -> Y Position in pixels (from the top, can be negative)

void ssd1306_txt_n(void *s, uint8_t l, int x, int y)
Arguments:

  • s -> Pointer to string
  • l -> Length of string s
  • x -> X Position in pixels (from the left, can be negative)
  • y -> Y Position in pixels (from the top, can be negative)

void ssd1306_txt_f(int x, int y, void *fmt, ...)
Arguments:

  • x -> X Position in pixels (from the left, can be negative)
  • y -> Y Position in pixels (from the top, can be negative)
  • fmt -> Format String (refer to Format Strings for details)

// Draw some text
ssd1306_txt("Hello World!", 0, 0);
ssd1306_txt_f(0, 8, "Number: %i", 42);
ssd1306_txt_f(0, 16, "Hex: %h", 42);
ssd1306_txt_f(0, 24, "Binary: %b", 42);

Drawing progress bars

Simple progress bars can be drawn using the ssd1306_pbar method (from ssd1306/pbar.h):

void ssd1306_pbar(uint8_t pct, uint8_t w, int x, int y)
Arguments:

  • pct -> Progress percentage (0 - 100)
  • w -> Total width in pixels
  • x -> X Position in pixels (from the left, can be negative)
  • y -> Y Position in pixels (from the top, can be negative)

// Draw a half-full progress bar 32 pixels wide
ssd1306_pbar(50, 32, 10, 10);

Drawing images

Please refer to (the bottom of this page) for instructions on how to export images in the proper format.
Place your images along with your source code, and change their extensions to .WIDTH.HEIGHT.ssd1306_img (replacing WIDTH and HEIGHT with the dimensions of your image in pixels, as described in SSD1306 Images).

These images will then be packaged into your application's binary file and stored in the flash of your microcontroller. This means they'll be available as XXXXX_data (actual data) and XXXXX_DATA_SIZE (byte size) from the code, but you'll need to load the image data into RAM to use it.

Then, images can be drawn using the ssd1306_img method (from ssd1306/img.h):

void ssd1306_img(uint8_t *img, int x, int y)
Arguments:

  • img -> Image binary data
  • x -> X Position in pixels (from the left, can be negative)
  • y -> Y Position in pixels (from the top, can be negative)

The example below assumes a 32x24 image has been exported from gimp into the source directory as foobar.32.24.ssd1306_img:

// RAM buffer to hold image data
uint8_t foobar_img[FOOBAR_DATA_SIZE];

// Main
void main()
{
    // ...

    // Load image into RAM
    memcpy_P(foobar_img, &foobar_data, FOOBAR_DATA_SIZE);

    // Draw image
    ssd1306_img(foobar_img, 20, 4);

    // ...
    for(;;);
}

Text terminal

The display can also be used as a very basic text terminal using the methods provided in ssd1306/term.h:

void ssd1306_term_init()

void ssd1306_term_printf(void *fmt, ...)
Arguments:

// Main
void main()
{
    // Initialize I2C Bus and OLED display
    i2c_init();
    ssd1306_init();

    // Initialize Text Terminal
    ssd1306_term_init();

    // Print a few things
    ssd1306_term_printf("Hello world!\n");
    ssd1306_term_printf("Hex: %h\n", 15);
    ssd1306_term_printf("Bin: %b\n", 15);
    ssd1306_term_printf("Dec: %i\n", 15);
}

Exporting from GIMP

To export images in a format that can be used by dbuild, a simple option is to use GIMP, which is a free image manipulation tool.

Work in RGB image mode.
Draw the pixels you want active as black (#000000), leave the rest as white.
When ready to export, do "Color to Alpha" -> set white as alpha.
Convert to Grayscale image mode.
Export as 'Raw image data' (leave default settings).