NAME

RPi::OLED::SSD1306::128_64 - Interface to the SSD1306-esque 128x64 OLED displays

SYNOPSIS

use RPi::OLED::SSD1306::128_64;

# The panel defaults to I2C address 0x3C
my $oled = RPi::OLED::SSD1306::128_64->new;

$oled->text_size(1);

# Draw text and push it to the screen. A trailing 1 on string() calls
# display() for you; "\n" starts a new line, and long lines wrap.
$oled->string("Hello, OLED!\nline two", 1);

# Drawing primitives fill the in-memory buffer; call display() to show them
$oled->clear;
$oled->rect(0, 0, 40, 20, 1);           # filled rectangle
$oled->horizontal_line(0, 32, 128, 1);  # a horizontal line
$oled->pixel(64, 40, 1);                # a single pixel
$oled->display;

# Flicker-free continuous refresh: rebuild the whole frame in the buffer
# each pass, then push it once (no blank flash between frames)
while (1) {
    $oled->clear_buffer;                     # zero buffer + home cursor
    $oled->string(sprintf("tick %d", time)); # draw (no auto-display)
    $oled->display;                          # single push to the panel
    select(undef, undef, undef, 0.2);
}

$oled->clear;   # blank the panel when done

DESCRIPTION

Provides the ability to use the 128x64 SSD1306 type OLED displays.

This distribution requires wiringPi version 3.18+ to be installed (the canonical minimum for the whole RPi:: family is published as WIRINGPI_MIN_VERSION in RPi::Const, which this distribution's Makefile.PL consumes).

METHODS

new([$i2c_addr])

Instantiates and returns a new RPi::OLED::SSD1306::128x64 object.

Note that this module is a singleton; if you've already instantiated a new OLED device object, it will be returned if new() is called again.

Parameters:

$i2c_addr

Optional, Integer. The I2C address of your OLED screen. Defaults to 0x3C, which is extremely common.

clear

Wipes the display clean, and sets the cursor to the top-left position on the screen.

Returns 1 on success.

clear_buffer

Zeroes the in-memory framebuffer and homes the cursor, but does not push the result to the panel (unlike "clear", which also refreshes the screen). Pair it with a single "display" to rebuild and draw a whole frame in one write - handy for a continuously-updating screen, where clearing straight to the panel each frame would flicker.

Returns 1 on success.

text_size($size)

By default, we use the smallest text size (1) when displaying characters to the OLED. You can increase or decrease the text size with this call.

Parameters:

$size

Mandatory, Integer: A number to increase or decrease the font size to. Any number is valid, but be realistic... this screen is only 128x64 pixels.

Returns 1 on success.

display

Draws whatever you've put into the buffer to the screen. All calls that add to the buffer (eg: rect(), string(), pixel(), char() etc) require a call to this method after you've filled the buffer.

string($str, [$display])

Send a string to the display for printing.

Parameters:

$str

Mandatory, String: The string you want put into the buffer for display.

$display

Optional, Bool: All calls for displaying something to the screen require an additional call to display(). Send in a positive value (1) as the second parameter to this call and we'll call display automatically for you.

Returns 1 on success.

rect($x, $y, $w, $h, $colour)

Prepares a rectangle for display on the screen. To actually display the rectangle, a subsequent call to display() is required.

Parameters:

$x

Mandatory, Integer: The X-axis (horizontal) position from the left of the screen to begin drawing the rectangle.

$y

Mandatory, Integer: The Y-axis (vertical) position from the top of the screen to begin drawing the rectangle.

$w

Mandatory, Integer: How many pixels wide to draw the rect.

$h

Mandatory, Integer: How many pixels tall (from the top) to draw the rect.

$colour

Optional, Bool: By default, we use 1 which is standard colour (white). Send in 0 and we'll use black, which will effectively wipe out whatever was on the display in the area of the rectangle.

Returns 1 on success.

char($x, $y, $char, $size, $colour)

Creates a buffer with a single ASCII char. As with other buffer calls, a call to display() is required once you're ready to display the buffer.

Parameters:

$x

Mandatory, Integer: The X-axis (horizontal) position from the left of the screen to begin drawing the char.

$y

Mandatory, Integer: The Y-axis (vertical) position from the top of the screen to begin drawing the char.

$char

Mandatory, Integer: The integer representation of the ASCII char to draw. Valid values are 0-255.

$size

Optional, Integer: The size of the char on the screen. Defaults to 2.

$colour

Optional, Bool: By default, we use 1 which is standard colour (white). Send in 0 and we'll use black, which will effectively wipe out whatever was on the display in the area of the char.

Returns 1 on success.

pixel($x, $y, $colour)

Draw a single pixel to the screen.

Parameters:

$x

Mandatory, Integer: The X-axis (horizontal) position from the left of the screen to place the pixel.

$y

Mandatory, Integer: The Y-axis (vertical) position from the top of the screen to place the pixel.

$colour

Optional, Bool: By default, we use 1 which is standard colour (white). Send in 0 and we'll use black, which will effectively wipe out whatever was on the display in the area of the pixel.

Returns 1 on success.

horizontal_line($x, $y, $w, $colour)

Draw a single pixel wide horizontal line.

Parameters:

$x

Mandatory, Integer: The X-axis (horizontal) position from the left of the screen to begin drawing the horizontal line.

$y

Mandatory, Integer: The Y-axis (vertical) position from the top of the screen to begin drawing the horizontal line.

$w

Mandatory, Integer: How many pixels wide to draw the horizontal line.

$colour

Optional, Bool: By default, we use 1 which is standard colour (white). Send in 0 and we'll use black, which will effectively wipe out whatever was on the display in the area of the rectangle.

Returns 1 on success.

vertical_line($x, $y, $h, $colour)

Draw a single-pixel wide vertical line.

Parameters:

$x

Mandatory, Integer: The X-axis (horizontal) position from the left of the screen to begin drawing the vertical line.

$y

Mandatory, Integer: The Y-axis (vertical) position from the top of the screen to begin drawing the vertical line.

$w

Mandatory, Integer: How many pixels tall to draw the vertical line.

$colour

Optional, Bool: By default, we use 1 which is standard colour (white). Send in 0 and we'll use black, which will effectively wipe out whatever was on the display in the area of the rectangle.

Returns 1 on success.

dim($bool)

The screen has two brightness levels, dim and full.

Parameters:

$bool

Optional, Bool: Send in 1 to dim the display, and 0 to turn it to its maximum brightness. Defaults to 0 if not sent in.

Returns 1 on success.

invert_display($bool)

By default, the screen background is black, and anything you draw will be white. Inverting the screen will reverse those two colours.

Parameters:

$bool

Optional, Bool: 1 will invert the screen (black on white background), and 0 will set it back to normal (white on black background). Defaults to 0 if not sent in.

Returns 1 on success.

TECHNICAL INFORMATION

The bus work lives in the bundled C layer (ssd1306_i2c.c, a port of the Adafruit SSD1306 library); the Perl methods draw into a 1KB framebuffer on the Pi and push it to the panel over I2C.

DEVICE SPECIFICS

- SSD1306: 128x64 monochrome OLED controller/driver
- Graphics RAM is 1KB, arranged as 8 pages of 128 bytes; each byte
  is a vertical strip of 8 pixels
- This driver mirrors that RAM in a Pi-side buffer: the draw calls
  only touch the buffer, and display() pushes all 1024 bytes out
- I2C address 0x3C (the module default), or 0x3D with the SA0 line
  strapped high
- I2C clocks up to 400kHz (t_cycle 2.5us minimum); the Pi's default
  100kHz works fine
- Logic supply 1.65-3.3V; the higher OLED panel voltage is made by
  the chip's internal charge pump, switched on during init
- The bare chip also speaks SPI and parallel buses; I2C breakout
  boards hardwire the interface-select pins

Wiring a typical 4-pin I2C breakout: VCC to 3.3V, GND to ground, SDA to GPIO 2 (pin 3), SCL to GPIO 3 (pin 5). i2cdetect -y 1 shows the panel at 0x3C.

COMMAND SET

Every byte sent to the chip is framed by a control byte (see "ON THE WIRE") marking it as either a command or display data - there are no addressable registers. The commands this driver uses:

0xAE / 0xAF   Display off / on
0x81 xx       Contrast (init sets 0xCF; dim() sends 0x00)
0xA4          Resume displaying the RAM contents
0xA6 / 0xA7   Normal / inverted video (invert_display())
0x20 00       Memory addressing mode: horizontal, auto-wrapping
0x21 s e      Column address window (display() uses 0-127)
0x22 s e      Page address window (display() uses 0-7)
0x40+n        Display start line (init: line 0)
0xA1          Segment remap - X flip (init)
0xC8          COM scan direction - Y flip (init)
0xA8 3F       Multiplex ratio: 64 rows
0xD3 00       Display offset: none
0xD5 80       Display clock divide ratio / oscillator
0xD9 F1       Precharge periods
0xDA 12       COM pins layout for 128x64
0xDB 40       VCOMH deselect level
0x8D 14       Charge pump on
0x2E          Deactivate scroll

new() runs the whole bring-up sequence above; after that the only traffic is the odd contrast/invert command and display() pushes.

ON THE WIRE

The C layer writes through the kernel's /dev/i2c-1, and every frame is three bytes: the chip address, a control byte, then one payload byte. The control byte is 0x00 for a command and 0x40 for display data (bit 6 is D/C#, bit 7 is Co):

S = START    P = STOP    A = ACK (receiver pulls SDA low)

A command - here 0xAF (display on) at address 0x3C, which is 0x78 on the wire:

+---+------+---+------+---+------+---+---+
| S | 0x78 | A | 0x00 | A | 0xAF | A | P |
+---+------+---+------+---+------+---+---+
     addr+W     Control    Command
     (0x3C)     = command

The framebuffer streams out in a single transaction - one 0x40 control byte (Co = 0, so every byte after it is display data), then all 1024 buffer bytes, before the STOP:

+---+------+---+------+---+------+-- --+------+---+---+
| S | 0x78 | A | 0x40 | A | 0xFF | ... | 0x00 | A | P |
+---+------+---+------+---+------+-- --+------+---+---+
     addr+W     Control    1024 data bytes, eight
                = data     vertical pixels each

A full display() is six command frames (resetting the column and page windows to 0-127 / 0-7) followed by that one 1025-byte data transaction - about 0.09s per refresh at the Pi's default 100kHz, which is bus-bound (raise dtparam=i2c_arm_baudrate to go faster). Earlier releases sent a control+data frame per byte (~1024 transactions, ~0.3s); the driver now streams the whole buffer after a single control byte, falling back to the byte-by-byte path only if an adapter caps the single transfer.

DATASHEET

The Solomon Systech SSD1306 datasheet (rev 1.1) is distributed with this software as docs/datasheet/SSD1306.pdf. It covers the command set, the GDDRAM layout, the I2C control byte framing, and the charge pump this driver enables.

AUTHOR

Steve Bertrand, <steveb at cpan.org>

LICENSE AND COPYRIGHT

Copyright 2018-2026 Steve Bertrand.

BSD License