Arduino UNO R4 - SSD1309 OLED Display

Organic Light-Emitting Diode (OLED) displays feature self-illuminating pixels that produce true blacks, superior contrast ratios, and excellent viewing angles from all directions — providing significant advantages over conventional LCD technology. The SSD1309 driver chip powers the 2.42-inch (also marketed as 2.4-inch) 128×64 monochrome OLED modules using I2C communication.

Arduino Uno R4 SSD1309 OLED 128x64

This hands-on guide walks you through interfacing the SSD1309 OLED 128×64 with your Arduino Uno R4 board via the DIYables_OLED_SSD1309 library. You will discover how to:

Overview of the SSD1309 2.42-Inch OLED Display

The SSD1309 serves as a dedicated CMOS driver IC engineered for 128×64 pixel OLED matrices. Its register set maintains compatibility with the popular SSD1306, enabling code reuse with minor adjustments. Notable hardware distinctions include:

  • External power requirement — unlike the SSD1306's integrated charge pump, the SSD1309 relies on an external VCC supply. However, commercially available breakout boards (including 2.42-inch and 2.4-inch variants) incorporate onboard boost converters, making this difference transparent during use.
  • Extended voltage range — the SSD1309 handles up to 16 V VCC input, whereas the SSD1306 caps at approximately 4.2 V.

The 2.42 inch (2.4 inch) OLED module typically employs the SSD1309 controller and provides a 128×64 resolution display communicating via I2C protocol. Display color (white, blue, yellow, green, or dual-color zones) depends on the physical OLED substrate and cannot be modified through software commands.

This tutorial interfaces with the display through the I2C bus, requiring only two signal lines (SDA and SCL) and allowing bus sharing with additional I2C peripherals.

SSD1309 OLED Pinout (I2C Module)

Standard 2.42-inch SSD1309 I2C OLED modules feature four connection pins:

  • GND — Connect to Arduino Uno R4 ground reference.
  • VCC — Power supply input. Wire to Arduino Uno R4 5 V output (or 3.3 V if module supports it).
  • SCL — I2C clock signal line.
  • SDA — I2C data signal line.
SSD1309 OLED Pinout

※ NOTE THAT:

  • Pin arrangement differs between manufacturers. Always verify the silkscreen labels on your specific module before making connections.
  • This guide has been validated with the 2.42 inch SSD1309 OLED display from DIYables. Other SSD1309-based 2.4/2.42-inch modules should function identically.

Wiring Diagram — Arduino Uno R4 & SSD1309 OLED 128×64

  • Schematic connections between Arduino Uno R4 and the 2.42 inch SSD1309 OLED 128×64
The wiring diagram between Arduino Uno R4 SSD1309 OLED 128x64

This image is created using Fritzing. Click to enlarge image

  • Physical wiring photograph between Arduino Uno R4 and SSD1309 OLED 128×64
Arduino Uno R4 SSD1309 OLED 128x64 real wiring

This image is created using Fritzing. Click to enlarge image

See The best way to supply power to the Arduino Uno R4 and other components.

The Arduino Uno R4 uses the same I2C pin locations as the classic Uno. Connection details:

OLED ModuleArduino Uno R4
Vin5V
GNDGND
SDAA4 (or SDA pin)
SCLA5 (or SCL pin)

Getting Started — SSD1309 OLED with Arduino Uno R4

Step 1: Install the DIYables_OLED_SSD1309 Library

  • Launch the Arduino IDE and click the Libraries icon on the left sidebar.
  • Enter "DIYables_OLED_SSD1309" in the search field and find the library by DIYables.
  • Click the Install button.
Arduino SSD1309 OLED library
  • The IDE will prompt you to install the required dependency (Adafruit GFX Library). Click Install All.
Arduino Adafruit GFX library

Step 2: Basic Programming Structure

All SSD1309 sketches follow this consistent structure: include headers, instantiate a display object, initialize it in setup(), draw content into the frame buffer, then transfer the buffer to the screen using display().

  • Include necessary headers:
#include <Wire.h> #include <Adafruit_GFX.h> #include <DIYables_OLED_SSD1309.h>
  • Define the screen dimensions (128×64 for the 2.42-inch module):
#define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels
  • Instantiate the display object (use -1 when no reset pin is connected):
// Create an SSD1309 display instance on the default I2C bus DIYables_OLED_SSD1309 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
  • Initialize the display inside setup():
// Initialize the SSD1309 — use address 0x3C (default for most modules) if (!oled.begin(SSD1309_SWITCHCAPVCC, 0x3C)) { Serial.println(F("SSD1309 allocation failed")); while (true); }
  • After initialization, call drawing functions (clearDisplay(), drawPixel(), print(), etc.) followed by oled.display() to refresh the screen.

※ NOTE THAT:

All code examples in this guide target the SSD1309 OLED 128×64 (2.42 inch) and use the DIYables_OLED_SSD1309 library with Arduino Uno R4.

Arduino Uno R4 Code — Hello World on SSD1309 OLED

The simplest demonstration: display text at multiple sizes.

/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-ssd1309-oled-display */ /* * DIYables OLED SSD1309 – Hello World * Prints text in two sizes on the 2.42 inch 128x64 I2C OLED. * Compatible with Arduino Uno R4 WiFi and Arduino Uno R4 Minima. */ #include <Wire.h> #include <Adafruit_GFX.h> #include <DIYables_OLED_SSD1309.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C DIYables_OLED_SSD1309 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); void setup() { Serial.begin(9600); if (!display.begin(SSD1309_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1309 allocation failed")); for (;;); } display.clearDisplay(); display.setTextSize(1); // 6x8 pixels per character display.setTextColor(SSD1309_PIXEL_ON); // turn pixels on display.setCursor(0, 0); display.println(F("Hello, World!")); display.println(); display.setTextSize(2); // 12x16 pixels per character display.println(F("DIYables")); display.setTextSize(1); display.println(); display.println(F("SSD1309 OLED 128x64")); display.display(); // push buffer to screen } void loop() { }

Arduino Uno R4 Code — Display Text on SSD1309 OLED

This example showcases advanced text features — variable sizes, numeric formatting, and the F() macro for RAM conservation.

/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-ssd1309-oled-display */ /* * DIYables OLED SSD1309 – Display Text Demo for Arduino Uno R4 * Demonstrates text display features on 2.42" SSD1309 128x64 I2C OLED. */ #include <Wire.h> #include <Adafruit_GFX.h> #include <DIYables_OLED_SSD1309.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C DIYables_OLED_SSD1309 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); void setup() { Serial.begin(9600); if (!display.begin(SSD1309_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1309 allocation failed")); for (;;); } delay(2000); // wait for initializing display.clearDisplay(); // clear display display.setTextSize(1); // text size = 1 display.setTextColor(SSD1309_PIXEL_ON); // set text color display.setCursor(0, 10); // set position to display display.println(F("Text size = 1")); // display text display.display(); // update display delay(2000); display.setTextSize(2); // text size = 2 display.setCursor(0, 30); // set position to display display.println(F("Size = 2")); // display text display.display(); // update display delay(2000); display.clearDisplay(); // clear display display.setTextSize(1); display.setCursor(0, 0); display.println(F("Display integer:")); display.println(12345); display.println(); display.println(F("Display float:")); display.println(1.2345); display.println(); display.println(F("Display HEX:")); display.println(0xABCD, HEX); display.display(); // update display } void loop() { }

Useful Display Functions Reference

Quick reference for commonly-used SSD1309 OLED functions via the DIYables library:

  • oled.clearDisplay() — erase the frame buffer (all pixels off).
  • oled.display() — transfer the buffer to the OLED to make changes visible.
  • oled.drawPixel(x, y, color) — set or clear a single pixel.
  • oled.setTextSize(n) — scale the font by factor *n* (1 = 6×8, 2 = 12×16, …, up to 8).
  • oled.setCursor(x, y) — position the text cursor at pixel coordinates *(x, y)*.
  • oled.setTextColor(SSD1309_PIXEL_ON) — text foreground only (transparent background).
  • oled.setTextColor(SSD1309_PIXEL_OFF, SSD1309_PIXEL_ON) — text with explicit background color.
  • oled.println("message") — print a string and move to the next line.
  • oled.println(number) — print an integer in decimal format.
  • oled.println(number, HEX) — print an integer in hexadecimal format.
  • oled.startscrollright(start, stop) — hardware-scroll right between page *start* and page *stop*.
  • oled.startscrollleft(start, stop) — hardware-scroll left.
  • oled.startscrolldiagright(start, stop) — hardware-scroll diagonally right.
  • oled.startscrolldiagleft(start, stop) — hardware-scroll diagonally left.
  • oled.stopscroll() — halt any active hardware scroll.
  • oled.setContrast(value) — adjust display brightness (0–255).
  • oled.dim(true/false) — quickly dim the display to minimum or restore previous contrast.
  • oled.invertDisplay(true/false) — hardware-level color inversion (on pixels ↔ off pixels).

How to Vertically and Horizontally Center Text on the SSD1309 OLED

See How to vertical/horizontal center on OLED

Arduino Uno R4 Code — Draw Shapes on SSD1309 OLED

The DIYables_OLED_SSD1309 library inherits from Adafruit_GFX, providing complete shape-drawing capabilities: pixels, lines, rectangles, filled rectangles, circles, filled circles, triangles, filled triangles, and rounded rectangles. The following sketch demonstrates all of them with animated sequences.

/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-ssd1309-oled-display */ /* * DIYables OLED SSD1309 – Draw Shapes for Arduino Uno R4 * Cycles through every shape primitive on the 2.42" SSD1309 128x64 OLED. */ #include <Wire.h> #include <Adafruit_GFX.h> #include <DIYables_OLED_SSD1309.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C DIYables_OLED_SSD1309 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); void setup() { Serial.begin(9600); if (!display.begin(SSD1309_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1309 allocation failed")); for (;;); } display.clearDisplay(); } void loop() { // --- Pixels --- display.clearDisplay(); for (int16_t i = 0; i < display.width(); i += 4) { display.drawPixel(i, i * display.height() / display.width(), SSD1309_PIXEL_ON); } display.display(); delay(1500); // --- Lines --- display.clearDisplay(); for (int16_t i = 0; i < display.width(); i += 8) { display.drawLine(0, 0, i, display.height() - 1, SSD1309_PIXEL_ON); } for (int16_t i = 0; i < display.height(); i += 8) { display.drawLine(0, 0, display.width() - 1, i, SSD1309_PIXEL_ON); } display.display(); delay(1500); // --- Rectangles --- display.clearDisplay(); for (int16_t i = 0; i < display.height() / 2; i += 4) { display.drawRect(i, i, display.width() - 2 * i, display.height() - 2 * i, SSD1309_PIXEL_ON); } display.display(); delay(1500); // --- Filled rectangles (inverse) --- display.clearDisplay(); for (int16_t i = 0; i < display.height() / 2; i += 6) { display.fillRect(i, i, display.width() - 2 * i, display.height() - 2 * i, SSD1309_PIXEL_INVERSE); } display.display(); delay(1500); // --- Circles --- display.clearDisplay(); for (int16_t i = max(display.width(), display.height()) / 2; i > 0; i -= 5) { display.drawCircle(display.width() / 2, display.height() / 2, i, SSD1309_PIXEL_ON); } display.display(); delay(1500); // --- Filled circles (inverse) --- display.clearDisplay(); for (int16_t i = max(display.width(), display.height()) / 2; i > 0; i -= 6) { display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1309_PIXEL_INVERSE); } display.display(); delay(1500); // --- Rounded rectangles --- display.clearDisplay(); for (int16_t i = 0; i < display.height() / 2 - 2; i += 4) { display.drawRoundRect(i, i, display.width() - 2 * i, display.height() - 2 * i, display.height() / 4, SSD1309_PIXEL_ON); } display.display(); delay(1500); // --- Filled rounded rectangles (inverse) --- display.clearDisplay(); for (int16_t i = 0; i < display.height() / 2 - 2; i += 4) { display.fillRoundRect(i, i, display.width() - 2 * i, display.height() - 2 * i, display.height() / 4, SSD1309_PIXEL_INVERSE); } display.display(); delay(1500); // --- Triangles --- display.clearDisplay(); for (int16_t i = 0; i < max(display.width(), display.height()) / 2; i += 5) { display.drawTriangle( display.width() / 2, display.height() / 2 - i, display.width() / 2 - i, display.height() / 2 + i, display.width() / 2 + i, display.height() / 2 + i, SSD1309_PIXEL_ON); } display.display(); delay(1500); // --- Filled triangles (inverse) --- display.clearDisplay(); for (int16_t i = max(display.width(), display.height()) / 2; i > 0; i -= 6) { display.fillTriangle( display.width() / 2, display.height() / 2 - i, display.width() / 2 - i, display.height() / 2 + i, display.width() / 2 + i, display.height() / 2 + i, SSD1309_PIXEL_INVERSE); } display.display(); delay(1500); }

Arduino Uno R4 Code — Hardware Scrolling on SSD1309 OLED

The SSD1309 includes a hardware scrolling engine that moves display content without CPU intervention. The DIYables library exposes four scroll modes: right, left, diagonal-right, and diagonal-left. Each accepts a start page and stop page parameter (pages are 8-pixel-tall horizontal bands numbered 0–7 on a 64-pixel-tall display).

※ NOTE THAT:

Always call display() to transfer your content to the OLED before initiating a scroll. Avoid rendering new content while scrolling is active — call stopscroll() first.

/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-ssd1309-oled-display */ /* * DIYables OLED SSD1309 – Scroll Text * Demonstrates all four hardware scroll directions on the 2.42" OLED. * Compatible with Arduino Uno R4 WiFi and Arduino Uno R4 Minima. */ #include <Wire.h> #include <Adafruit_GFX.h> #include <DIYables_OLED_SSD1309.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C DIYables_OLED_SSD1309 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); void setup() { Serial.begin(9600); if (!display.begin(SSD1309_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1309 allocation failed")); for (;;); } display.clearDisplay(); display.setTextSize(2); display.setTextColor(SSD1309_PIXEL_ON); display.setCursor(10, 24); display.println(F("DIYables")); display.display(); delay(2000); } void loop() { // Scroll right across all pages display.startscrollright(0x00, 0x07); delay(3000); display.stopscroll(); delay(500); // Scroll left display.startscrollleft(0x00, 0x07); delay(3000); display.stopscroll(); delay(500); // Diagonal scroll right display.startscrolldiagright(0x00, 0x07); delay(3000); display.stopscroll(); delay(500); // Diagonal scroll left display.startscrolldiagleft(0x00, 0x07); delay(3000); display.stopscroll(); delay(500); }

Arduino Uno R4 Code — Display Bitmap Image on SSD1309 OLED

To display a bitmap on the SSD1309 OLED you must first convert your image into a C byte array. Use the free image2cpp online tool for this conversion:

  1. Upload your image file (PNG, JPG, BMP, etc.).
  2. Configure the canvas size to 128×64 (or smaller).
  3. Select Arduino code as the output format.
  4. Copy the generated array into your sketch.
image to bitmap array

The example below alternates between a 16×16 heart icon and a full-width DIYables logo:

/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-ssd1309-oled-display */ /* * DIYables OLED SSD1309 – Display Image Demo for Arduino Uno R4 * Displays bitmap images on the 2.42" SSD1309 128x64 I2C OLED. */ #include <Wire.h> #include <Adafruit_GFX.h> #include <DIYables_OLED_SSD1309.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C DIYables_OLED_SSD1309 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // 16x16 heart bitmap const unsigned char heart16x16[] PROGMEM = { 0x00, 0x00, 0x03, 0xc0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x3f, 0xfc, 0x1f, 0xf8, 0x0f, 0xf0, 0x03, 0xc0, 0x00, 0x00 }; // 128x64 DIYables logo bitmap const unsigned char diyablesLogo[] PROGMEM = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x00, 0xff, 0xf0, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x03, 0xff, 0xfc, 0x00, 0x00, 0x7f, 0xf8, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xf0, 0x00, 0x03, 0xff, 0xff, 0xc0, 0x00, 0x7f, 0xf0, 0x00, 0x1f, 0xff, 0xff, 0xc0, 0x00, 0x1f, 0xe0, 0x00, 0x0f, 0xff, 0xff, 0xe0, 0x00, 0x7f, 0xf0, 0x00, 0x7f, 0xff, 0xff, 0xe0, 0x00, 0x0f, 0xc0, 0x00, 0x1f, 0xff, 0xff, 0xf8, 0x00, 0x3f, 0xe0, 0x00, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x07, 0x80, 0x00, 0x7f, 0xff, 0xff, 0xfc, 0x00, 0x1f, 0xe0, 0x00, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x03, 0x80, 0x00, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x1f, 0xc0, 0x01, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x03, 0x00, 0x01, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x1f, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xf0, 0x7f, 0xff, 0x00, 0x0f, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xf0, 0x7f, 0xff, 0x80, 0x0f, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x1f, 0xff, 0xf0, 0x7f, 0xff, 0x80, 0x07, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x3f, 0xff, 0xf0, 0x7f, 0xff, 0xc0, 0x07, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x7f, 0xff, 0xf0, 0x7f, 0xff, 0xc0, 0x07, 0x80, 0x0f, 0xfe, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x03, 0xff, 0xc0, 0x07, 0x80, 0x0f, 0xfe, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xc0, 0x07, 0x80, 0x0f, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x03, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xc0, 0x07, 0x80, 0x0f, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x03, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xc0, 0x07, 0x80, 0x0f, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x03, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xc0, 0x07, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x01, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xc0, 0x07, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xc0, 0x07, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xc0, 0x07, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x7f, 0xff, 0xf0, 0x7f, 0xff, 0xc0, 0x07, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x3f, 0xff, 0xf0, 0x7f, 0xff, 0x80, 0x07, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x1f, 0xff, 0xf0, 0x7f, 0xff, 0x80, 0x07, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x80, 0x0f, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x03, 0x00, 0x01, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x1f, 0xe0, 0x00, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x07, 0x80, 0x00, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x1f, 0xe0, 0x00, 0x7f, 0xff, 0xff, 0xf0, 0x00, 0x0f, 0xc0, 0x00, 0x3f, 0xff, 0xff, 0xf8, 0x00, 0x3f, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xe0, 0x00, 0x1f, 0xe0, 0x00, 0x1f, 0xff, 0xff, 0xf0, 0x00, 0x3f, 0xf8, 0x00, 0x1f, 0xff, 0xff, 0x80, 0x00, 0x3f, 0xe0, 0x00, 0x07, 0xff, 0xff, 0xe0, 0x00, 0x7f, 0xf8, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xf8, 0x00, 0x03, 0xff, 0xff, 0xc0, 0x00, 0x7f, 0xfc, 0x00, 0x03, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x01, 0xff, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x01, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; void setup() { Serial.begin(9600); if (!display.begin(SSD1309_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1309 allocation failed")); for (;;); } delay(2000); display.setCursor(0, 0); } void loop() { // Display 16x16 heart icon display.clearDisplay(); display.drawBitmap((SCREEN_WIDTH - 16) / 2, (SCREEN_HEIGHT - 16) / 2, heart16x16, 16, 16, SSD1309_PIXEL_ON); display.display(); delay(2000); // Display full-screen DIYables logo display.clearDisplay(); display.drawBitmap(0, 0, diyablesLogo, 128, 64, SSD1309_PIXEL_ON); display.display(); delay(2000); // Invert display display.invertDisplay(true); delay(2000); display.invertDisplay(false); delay(500); }

※ NOTE THAT:

  • Bitmap dimensions must not exceed the screen resolution (128×64 for the 2.42 inch module).

Arduino Uno R4 Code — Contrast and Dim on SSD1309 OLED

The SSD1309 offers 256 contrast levels (0–255). The DIYables library provides setContrast() for precise control and dim() for quick toggling between minimum brightness and the previously-set level.

/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-ssd1309-oled-display */ /* * DIYables OLED SSD1309 – Contrast & Dim * Sweeps contrast from 0→255→0 then toggles dim mode on the 2.42" OLED. * Compatible with Arduino Uno R4 WiFi and Arduino Uno R4 Minima. */ #include <Wire.h> #include <Adafruit_GFX.h> #include <DIYables_OLED_SSD1309.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C DIYables_OLED_SSD1309 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); void setup() { Serial.begin(9600); if (!display.begin(SSD1309_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1309 allocation failed")); for (;;); } // Draw a test pattern so the contrast changes are visible display.clearDisplay(); display.fillRect(0, 0, 64, 32, SSD1309_PIXEL_ON); display.fillRect(64, 32, 64, 32, SSD1309_PIXEL_ON); display.setTextSize(1); display.setTextColor(SSD1309_PIXEL_INVERSE); display.setCursor(16, 28); display.println(F("Contrast Demo")); display.display(); delay(2000); } void loop() { // Ramp up for (int c = 0; c <= 255; c += 5) { display.setContrast((uint8_t)c); delay(30); } delay(1000); // Ramp down for (int c = 255; c >= 0; c -= 5) { display.setContrast((uint8_t)c); delay(30); } delay(1000); // Quick dim toggle display.dim(true); // minimum brightness delay(2000); display.dim(false); // restore delay(2000); }

Arduino Uno R4 Code — Custom External Fonts on SSD1309 OLED

The Adafruit GFX library includes dozens of scalable FreeFont typefaces (Serif, Sans, Mono — each in Regular, Bold, Italic, and multiple sizes). You can use any of them on the SSD1309 display by including the relevant header and calling setFont().

※ NOTE THAT:

  • When an external font is active, the cursor Y coordinate refers to the text baseline, not the top-left corner. This differs from the built-in 5×7 font behavior.
  • External fonts are stored in flash (PROGMEM). On memory-constrained boards like the classic Uno (32 KB flash), use them judiciously. The Arduino Uno R4 has more memory, allowing more flexibility.
/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-ssd1309-oled-display */ /* * DIYables OLED SSD1309 – External Fonts * Cycles through three FreeFont typefaces on the 2.42" SSD1309 OLED. * Compatible with Arduino Uno R4 WiFi and Arduino Uno R4 Minima. */ #include <Wire.h> #include <Adafruit_GFX.h> #include <DIYables_OLED_SSD1309.h> #include <Fonts/FreeSerif9pt7b.h> #include <Fonts/FreeSansBold12pt7b.h> #include <Fonts/FreeMono9pt7b.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C DIYables_OLED_SSD1309 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); void setup() { Serial.begin(9600); if (!display.begin(SSD1309_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1309 allocation failed")); for (;;); } display.clearDisplay(); display.display(); } void loop() { // ── Built-in 5×7 font ── display.clearDisplay(); display.setFont(NULL); display.setTextSize(1); display.setTextColor(SSD1309_PIXEL_ON); display.setCursor(0, 0); display.println(F("Built-in 5x7 font")); display.println(); display.setTextSize(2); display.println(F("DIYables")); display.display(); delay(3000); // ── FreeSerif 9pt ── display.clearDisplay(); display.setFont(&FreeSerif9pt7b); display.setTextSize(1); display.setTextColor(SSD1309_PIXEL_ON); display.setCursor(0, 14); // Y = baseline display.println(F("FreeSerif 9pt")); display.setCursor(0, 38); display.println(F("DIYables OLED")); display.setCursor(0, 58); display.println(F("Hello World!")); display.display(); delay(3000); // ── FreeSansBold 12pt ── display.clearDisplay(); display.setFont(&FreeSansBold12pt7b); display.setTextSize(1); display.setTextColor(SSD1309_PIXEL_ON); display.setCursor(0, 20); display.println(F("SansBold")); display.setCursor(0, 52); display.println(F("DIYables")); display.display(); delay(3000); // ── FreeMono 9pt ── display.clearDisplay(); display.setFont(&FreeMono9pt7b); display.setTextSize(1); display.setTextColor(SSD1309_PIXEL_ON); display.setCursor(0, 14); display.println(F("FreeMono 9pt")); display.setCursor(0, 34); display.println(F("0123456789")); display.setCursor(0, 54); display.println(F("!@#$%^&*()")); display.display(); delay(3000); }

Video Tutorial

The below video demo uses the below code:

/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-ssd1309-oled-display */ /* * Robot Eyes v4 - Arduino UNO R4 + OLED SSD1309 2.42" (I2C) + TTP223 * Libraries: DIYables_OLED_SSD1309 and Adafruit GFX * * Design: white elliptical sclera + black pupils with highlights + angled eyelids. * All eye elements are drawn in code for smooth animation. * * Wiring: * OLED VCC -> 5V, GND -> GND, SDA -> A4/SDA, SCL -> A5/SCL * TTP223 VCC -> 5V (or 3.3V), GND -> GND, I/O -> D13 * * Behavior: * - Each tap triggers a quick blink and switches to the next expression: * NORMAL -> HAPPY -> WINK -> ANGRY -> SAD -> CRY -> STAR -> DIZZY * -> LOVE -> SURPRISED -> SUSPICIOUS -> ROLL -> SLEEPY -> (back to NORMAL) * - After RETURN_NORMAL_MS (10s) of inactivity, return to NORMAL. * - After SLEEP_MS (30s) of inactivity, enter SLEEP mode. * - Tap while sleeping to wake up and return to NORMAL. */ #include <Wire.h> #include <Adafruit_GFX.h> #include <DIYables_OLED_SSD1309.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C #define TOUCH_PIN 13 #define ON SSD1309_PIXEL_ON #define OFF SSD1309_PIXEL_OFF // Adjustable parameters const int EYE_RX = 22; // horizontal eye radius const int EYE_RY = 25; // vertical eye radius const int EYE_CY = 32; // eye center on the Y axis const int CX_L = 32; // left eye center const int CX_R = 96; // right eye center const unsigned long RETURN_NORMAL_MS = 10000; const unsigned long SLEEP_MS = 30000; //must be greater than RETURN_NORMAL_MS const unsigned long DEBOUNCE_MS = 60; const unsigned long TRANSITION_MS = 110; //transition blink duration const unsigned long FRAME_MS = 33; // ~30 FPS DIYables_OLED_SSD1309 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); enum Mood { NORMAL, HAPPY, WINK, ANGRY, SAD, CRY, STAR, DIZZY, LOVE, SURPRISED, SUSPICIOUS, ROLL, SLEEPY, SLEEP }; const Mood ORDER[] = { NORMAL, HAPPY, WINK, ANGRY, SAD, CRY, STAR, DIZZY, LOVE, SURPRISED, SUSPICIOUS, ROLL, SLEEPY }; const uint8_t ORDER_N = sizeof(ORDER) / sizeof(ORDER[0]); Mood mood = NORMAL; uint8_t moodIndex = 0; unsigned long moodStart = 0, transUntil = 0; // Look direction (-1..1) float curX = 0, curY = 0, tgtX = 0, tgtY = 0; unsigned long nextLook = 0; // Blinking bool blinking = false; unsigned long blinkStart = 0, nextBlink = 0; const unsigned long BLINK_MS = 170; // Touch sensor bool lastReading = false, stableState = false; unsigned long lastChange = 0, lastActivity = 0, lastFrame = 0; // Eye model struct Style { // expression parameters float lx, ly; // look direction -1..1 float pr; // pupil radius (0 = do not draw) float topIn, topOut; // upper eyelid coverage in pixels (inner / outer) float bot; // lower eyelid coverage in pixels int rx, ry, cy; // eye size and position int shake; // horizontal shake (px) }; struct Eye { // calculated geometry for each eye int cx, cy, rx, ry; float topIn, topOut, bot; int inner; // +1: left eye (inner side is right), -1: right eye }; Style baseStyle() { Style s = { 0, 0, 13, 0, 0, 0, EYE_RX, EYE_RY, EYE_CY, 0 }; return s; } Eye makeEye(int side, const Style& s) { Eye e; e.cx = (side == 0 ? CX_L : CX_R) + s.shake + (int)(s.lx * 3); // slightly offset eye toward look direction e.cy = s.cy + (int)(s.ly * 2); e.rx = s.rx; e.ry = s.ry; e.topIn = s.topIn; e.topOut = s.topOut; e.bot = s.bot; e.inner = (side == 0) ? 1 : -1; return e; } float lidTopAt(const Eye& e, float u) { // u: -1..1 across the eye width float t = (e.inner * u + 1.0) * 0.5; // 0 = outer side, 1 = inner side return e.cy - e.ry + e.topOut + (e.topIn - e.topOut) * t; } // Draw the eyeball column by column, clipped by the eyelids. void drawEyeball(const Eye& e) { for (int dx = -e.rx; dx <= e.rx; dx++) { float u = (float)dx / e.rx; float hy = e.ry * sqrt(1.0 - u * u); float y0 = e.cy - hy, y1 = e.cy + hy; float lt = lidTopAt(e, u); float lb = e.cy + e.ry - e.bot; if (y0 < lt) y0 = lt; if (y1 > lb) y1 = lb; if (y1 >= y0) display.drawFastVLine(e.cx + dx, (int)y0, (int)(y1 - y0) + 1, ON); } } // Draw the pupil and highlights. Highlights are skipped when covered by the eyelids. void drawPupil(const Eye& e, const Style& s) { int pr = (int)(s.pr + 0.5); float mx = e.rx - pr - 2; if (mx < 0) mx = 0; float my = e.ry - pr - 2; if (my < 0) my = 0; int px = e.cx + (int)(s.lx * mx); int py = e.cy + (int)(s.ly * my); display.fillCircle(px, py, pr, OFF); float lb = e.cy + e.ry - e.bot; int hr = max(2, pr / 4); int hx = px - pr / 3, hy = py - pr / 3; if (hy - hr > lidTopAt(e, (float)(hx - e.cx) / e.rx) + 1 && hy + hr < lb - 1) display.fillCircle(hx, hy, hr, ON); int h2x = px + pr / 3, h2y = py + pr / 3; if (pr >= 8 && h2y + 1 < lb - 1) display.fillCircle(h2x, h2y, 1, ON); } void drawEyeSide(int side, const Style& s) { Eye e = makeEye(side, s); drawEyeball(e); if (s.pr > 0) drawPupil(e, s); } void drawPair(const Style& s) { drawEyeSide(0, s); drawEyeSide(1, s); } // Auxiliary shapes drawn inside the eyeball void drawHeart(int cx, int cy, int r, uint16_t col) { display.fillCircle(cx - r, cy - r / 2, r, col); display.fillCircle(cx + r, cy - r / 2, r, col); display.fillTriangle(cx - 2 * r + 1, cy - r / 2 + 2, cx + 2 * r - 1, cy - r / 2 + 2, cx, cy + 2 * r + 2, col); } void drawStar(int cx, int cy, float R, float rot, uint16_t col) { float px[10], py[10]; for (int i = 0; i < 10; i++) { float ang = rot + i * PI / 5.0 - PI / 2.0; float rad = (i % 2 == 0) ? R : R * 0.45; px[i] = cx + cos(ang) * rad; py[i] = cy + sin(ang) * rad; } for (int i = 0; i < 10; i++) { int j = (i + 1) % 10; display.fillTriangle(cx, cy, (int)px[i], (int)py[i], (int)px[j], (int)py[j], col); } } void drawSpiral(int cx, int cy, float phase, int dir, uint16_t col) { float px = cx, py = cy; for (float a = 0.4; a <= 11.5; a += 0.4) { float rad = a * 1.3; float ang = dir * (a + phase); float x = cx + cos(ang) * rad, y = cy + sin(ang) * rad; display.drawLine((int)px, (int)py, (int)x, (int)y, col); display.drawLine((int)px + 1, (int)py, (int)x + 1, (int)y, col); px = x; py = y; } } void drawHappyEye(int cx, int cy) { // smiling arch eye "^" display.fillCircle(cx, cy + 10, 17, ON); display.fillCircle(cx, cy + 18, 17, OFF); } void drawClosedEye(int cx, int cy) { // Closed eye display.fillRoundRect(cx - EYE_RX, cy - 1, 2 * EYE_RX, 3, 1, ON); } // Expressions void drawNormal(unsigned long now) { float cover = 0; if (blinking) { float p = (float)(now - blinkStart) / BLINK_MS; if (p >= 1.0) { blinking = false; nextBlink = now + (random(5) == 0 ? 180 : random(2000, 5500)); // occasional double blink } else { cover = sin(p * PI) * EYE_RY * 0.96; } } else if (now >= nextBlink) { blinking = true; blinkStart = now; } Style s = baseStyle(); s.lx = curX; s.ly = curY; s.pr = 13 + sin(now / 700.0) * 0.7; // subtle pupil breathing animation s.topIn = s.topOut = s.bot = cover; drawPair(s); } void drawHappy(unsigned long now) { int bob = (int)(sin(now / 120.0) * 3); drawHappyEye(CX_L, EYE_CY - 4 + bob); drawHappyEye(CX_R, EYE_CY - 4 + bob); } void drawWink(unsigned long now) { Style s = baseStyle(); s.pr = 13 + sin(now / 700.0) * 0.7; drawEyeSide(0, s); drawHappyEye(CX_R, EYE_CY - 4); } void drawAngry() { Style s = baseStyle(); s.pr = 10; s.ly = 0.15; s.topIn = 26; s.topOut = 1; s.bot = 3; s.shake = random(-1, 2); drawPair(s); } void drawSad() { Style s = baseStyle(); s.ly = 0.6; s.topIn = 0; s.topOut = 18; drawPair(s); } void drawCry(unsigned long now) { Style s = baseStyle(); s.rx = 20; s.ry = 22; s.cy = 26; s.pr = 12; s.ly = 0.6; s.topIn = 0; s.topOut = 16; drawPair(s); int tx[2] = { CX_L - 8, CX_R + 8 }; for (int i = 0; i < 2; i++) { int ty = 44 + (int)((now / 55 + i * 9) % 19); display.fillCircle(tx[i], ty, 2, ON); display.fillTriangle(tx[i] - 2, ty, tx[i] + 2, ty, tx[i], ty - 5, ON); } } void drawStarEyes(unsigned long now) { float R = 15 + sin(now / 150.0) * 1.5; float rot = sin(now / 500.0) * 0.25; Style s = baseStyle(); for (int side = 0; side < 2; side++) { Eye e = makeEye(side, s); drawEyeball(e); drawStar(e.cx, e.cy, R, rot, OFF); } } void drawDizzy(unsigned long now) { float phase = now / 120.0; Style s = baseStyle(); for (int side = 0; side < 2; side++) { Eye e = makeEye(side, s); drawEyeball(e); drawSpiral(e.cx, e.cy, phase, side == 0 ? 1 : -1, OFF); } } void drawLove(unsigned long now) { int r = 7 + (int)(sin(now / 150.0) * 1.5); Style s = baseStyle(); for (int side = 0; side < 2; side++) { Eye e = makeEye(side, s); drawEyeball(e); drawHeart(e.cx, e.cy - 3, r, OFF); } } void drawSurprised() { Style s = baseStyle(); s.rx = 24; s.ry = 28; s.pr = 5; drawPair(s); } void drawSuspicious(unsigned long now) { Style s = baseStyle(); s.pr = 11; s.lx = sin(now / 700.0); s.ly = 0.1; s.topIn = s.topOut = 22; s.bot = 8; drawPair(s); } void drawRoll(unsigned long now) { float t = (float)((now - moodStart) % 1300) / 1300.0 * 2.0 * PI; Style s = baseStyle(); s.lx = cos(t); s.ly = sin(t) * 0.9; drawPair(s); } void drawSleepy(unsigned long now) { float k = (sin(now / 450.0) + 1.0) / 2.0; // drowsy motion Style s = baseStyle(); s.lx = curX * 0.3; s.ly = 0.5; s.topIn = s.topOut = 10 + k * 24; s.bot = 3; drawPair(s); } void drawSleep(unsigned long now) { int cx[2] = { CX_L, CX_R }; for (int i = 0; i < 2; i++) { // Closed eye cong "︶" display.fillCircle(cx[i], EYE_CY - 2, 18, ON); display.fillCircle(cx[i], EYE_CY - 5, 18, OFF); } int phase = (now / 600) % 3; display.setTextColor(ON); display.setTextSize(1 + phase); display.setCursor(104 - phase * 6, 12 - phase * 4); display.print(phase == 0 ? F("z") : F("Z")); } void updateLook(unsigned long now) { if (now >= nextLook) { if (random(4) == 0) { tgtX = 0; tgtY = 0; } else { tgtX = random(-100, 101) / 100.0; tgtY = random(-70, 71) / 100.0; } nextLook = now + random(1200, 3500); } curX += (tgtX - curX) * 0.3; curY += (tgtY - curY) * 0.3; } void setMood(Mood m) { unsigned long now = millis(); if (mood == SLEEP && m != SLEEP) display.dim(false); if (m == SLEEP && mood != SLEEP) display.dim(true); if (m == NORMAL) { blinking = false; nextBlink = now + random(1500, 4000); } mood = m; moodStart = now; } bool touchPressed(unsigned long now) { bool reading = (digitalRead(TOUCH_PIN) == HIGH); if (reading != lastReading) { lastChange = now; lastReading = reading; } if ((now - lastChange) >= DEBOUNCE_MS && reading != stableState) { stableState = reading; if (stableState) return true; } return false; } void onTap(unsigned long now) { lastActivity = now; transUntil = now + TRANSITION_MS; // quick blink during expression transition if (mood == SLEEP) { moodIndex = 0; setMood(NORMAL); return; } moodIndex = (moodIndex + 1) % ORDER_N; setMood(ORDER[moodIndex]); } void setup() { Serial.begin(9600); pinMode(TOUCH_PIN, INPUT); randomSeed(analogRead(A0)); if (!display.begin(SSD1309_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1309 allocation failed")); for (;;); } Wire.setClock(400000); display.clearDisplay(); display.display(); unsigned long now = millis(); lastActivity = now; nextBlink = now + 2500; nextLook = now + 1500; } void loop() { unsigned long now = millis(); if (touchPressed(now)) onTap(now); if (stableState) lastActivity = now; unsigned long idle = now - lastActivity; if (mood != SLEEP) { if (idle >= SLEEP_MS) { moodIndex = 0; setMood(SLEEP); } else if (idle >= RETURN_NORMAL_MS && mood != NORMAL) { moodIndex = 0; setMood(NORMAL); } } if (now - lastFrame < FRAME_MS) return; lastFrame = now; if (mood == NORMAL || mood == SLEEPY) updateLook(now); display.clearDisplay(); if (now < transUntil) { drawClosedEye(CX_L, EYE_CY); drawClosedEye(CX_R, EYE_CY); } else { switch (mood) { case NORMAL: drawNormal(now); break; case HAPPY: drawHappy(now); break; case WINK: drawWink(now); break; case ANGRY: drawAngry(); break; case SAD: drawSad(); break; case CRY: drawCry(now); break; case STAR: drawStarEyes(now); break; case DIZZY: drawDizzy(now); break; case LOVE: drawLove(now); break; case SURPRISED: drawSurprised(); break; case SUSPICIOUS: drawSuspicious(now); break; case ROLL: drawRoll(now); break; case SLEEPY: drawSleepy(now); break; case SLEEP: drawSleep(now); break; } } display.display(); }

SSD1309 OLED Troubleshooting with Arduino Uno R4

If the 2.42 inch SSD1309 OLED remains blank after uploading your sketch, work through these diagnostic steps:

  • Verify connections — confirm SDA, SCL, VCC and GND are wired to the correct Arduino Uno R4 pins.
  • Confirm the driver chip — this library is designed for the SSD1309. If your module uses a different controller (e.g. SH1106), it won't respond correctly.
  • Check the I2C address — most SSD1309 modules default to 0x3C, but some use 0x3D. Run the I2C scanner sketch below to detect the actual address:
// I2C address scanner — prints every detected device to the Serial Monitor #include <Wire.h> void setup() { Wire.begin(); Serial.begin(9600); Serial.println("I2C Scanner"); } void loop() { byte error, address; int nDevices = 0; Serial.println("Scanning..."); for (address = 1; address < 127; address++) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address < 16) Serial.print("0"); Serial.print(address, HEX); Serial.println(" !"); nDevices++; } else if (error == 4) { Serial.print("Unknown error at address 0x"); if (address < 16) Serial.print("0"); Serial.println(address, HEX); } } if (nDevices == 0) Serial.println("No I2C devices found"); else Serial.println("done"); delay(5000); }

Expected Serial Monitor output when the SSD1309 is detected:

∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
Arduino Uno R4 WiFi
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'Arduino Uno R4 WiFi' on 'COM15')
New Line
9600 baud
Scanning... I2C device found at address 0x3C ! done
Ln 11, Col 1
Arduino Uno R4 WiFi on COM15
2
  • Ensure display() is called — the SSD1309 uses a frame buffer. Drawing functions only modify the buffer in RAM; nothing appears on screen until you call oled.display().
  • Check power supply — the 2.42 inch module draws more current than smaller OLEDs. Ensure your power source can deliver sufficient current (typically 20–40 mA at full brightness).

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!