Show text on a 0.96″ 128×64 I²C screen: a building block for local, phone‑free output.
Project_10_ESP32_OLED_Display.ino, then open it in the Arduino IDE (setup: Foundation 3). Needs the Adafruit SSD1306 + GFX libraries.
By the end of this project, you will:
The SSD1306 is a crisp 128×64 monochrome OLED: self‑lit pixels, great contrast, low power. It talks over I²C, a two‑wire bus (SDA + SCL). ESP32 defaults: GPIO 21 (SDA), GPIO 22 (SCL). This module's I²C address is 0x3C.
| OLED pin | ESP32 |
|---|---|
| VCC | 3.3 V |
| GND | GND |
| SCL | GPIO 22 |
| SDA | GPIO 21 |

Two Adafruit libraries (both in the Library Manager):
Restart the IDE after installing.

Clear → set style → position → print → push to the display. Nothing appears until display():
display.clearDisplay(); // erase the buffer display.setTextSize(2); // 1 = small ... bigger = larger display.setTextColor(WHITE); // white text on black display.setCursor(0, 30); // x, y start display.println("LAFVIN"); // write into the buffer display.display(); // <-- actually show it
Scrolling is built in: startscrollright(), startscrollleft(), stopscroll().
Short, but every line is worth understanding: this is the first project that talks to another chip rather than to a plain component.
#include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <Wire.h> #define SCREEN_WIDTH 128 // OLED width, in pixels #define SCREEN_HEIGHT 64 // OLED height, in pixels // I2C OLED, no reset pin (-1). Default I2C address 0x3C. Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Three includes, three jobs. Wire.h is the ESP32's I²C driver: it knows how to wiggle SDA and SCL. Adafruit_SSD1306 knows the commands this particular chip understands. Adafruit_GFX is the generic drawing engine: shapes, fonts, cursor handling. That layering is why the println() you already know works on a screen.
The four arguments are width, height, which I²C bus to use (&Wire, the default on GPIO 21 and 22), and a reset pin. This module has none, so that argument is -1.
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println("[OLED] ERROR: display not found!"); Serial.println(" Check SDA->21, SCL->22, VCC->3V3, GND->GND,"); Serial.println(" and that the I2C address is 0x3C."); for(;;); // stop here - nothing else to do without a display } Serial.println("[OLED] Display initialized OK.");
display.begin() returns false if nothing answers at address 0x3C, which is what a loose wire looks like from the sketch's point of view. Reporting it is the whole difference between "my screen is broken" and "my SDA wire is in the wrong hole".
SSD1306_SWITCHCAPVCC tells the module to generate its own display voltage from the 3.3 V you supplied, using an internal charge pump. for(;;); is an intentional infinite loop: with no display there is nothing useful left to do.
display.clearDisplay(); // erase the buffer display.setTextSize(2); // 1 = small ... bigger numbers = larger display.setTextColor(WHITE); // white text on black display.setCursor(0, 30); // x, y start position display.println("LAFVIN"); // write into the buffer display.display(); // <-- actually show it on screen
The first five calls change nothing on the screen. The library keeps a 128×64 copy of the image in the ESP32's RAM and every drawing call edits that copy. Only display.display() sends it over I²C. Forgetting that last line is the most common OLED mistake, and it looks exactly like a dead screen.
Coordinates start at (0, 0) in the top-left corner, with y growing downward. At text size 2 each character is about 12×16 pixels, so a cursor at (0, 30) lands roughly in the middle of a 64-pixel-tall screen.
loop() scrolls, and the display chip does the workvoid loop() { display.startscrollright(0x00, 0x0F); delay(7000); display.stopscroll(); delay(1000); display.startscrollleft(0x00, 0x0F); delay(7000); display.stopscroll(); delay(1000); }
startscrollright() is one command sent once. The SSD1306 then scrolls by itself, in hardware, until told to stop, which is why the delay() calls do no damage here: the animation is not being driven by the ESP32 at all. The two arguments are the first and last page to scroll, a page being an 8-pixel-tall band. 0x00 to 0x0F covers the whole screen.
| Code | What it does |
|---|---|
#include <Wire.h> | The I²C driver. It owns SDA (GPIO 21) and SCL (GPIO 22). |
Adafruit_SSD1306 display(W, H, &Wire, -1) | Creates the display object: size, which I²C bus, and no reset pin. |
display.begin(SSD1306_SWITCHCAPVCC, 0x3C) | Wakes the module at I²C address 0x3C and returns false if nothing answers. |
for(;;); | Deliberate infinite loop. Halts the sketch when there is no display to talk to. |
display.clearDisplay() | Blanks the buffer in RAM, not the screen. |
display.setTextSize(2) | Scales the built-in font. 1 is roughly 6×8 pixels per character. |
display.setCursor(0, 30) | Sets where the next text goes. (0, 0) is the top-left corner. |
display.println("LAFVIN") | Writes into the buffer, exactly like Serial.println() writes to the port. |
display.display() | Sends the whole buffer over I²C. Nothing appears without this. |
startscrollright(0x00, 0x0F) | Tells the display chip to scroll pages 0 to 15 on its own, until stopscroll(). |
============================================== Project 10: ESP32 OLED Display (SSD1306) ============================================== I2C: SDA = GPIO 21, SCL = GPIO 22, address 0x3C [OLED] Display initialized OK. [OLED] Showing text: "LAFVIN" (will scroll).
The improved sketch confirms the display started and prints a wiring/address hint if it can't find the screen (the original just froze silently on failure).

You now have local output: a device that can report for itself, with no phone, no browser, and no network. The key takeaways:
0x3C, and adding a second I²C part costs no extra pins.Wire moves bytes, Adafruit_SSD1306 speaks the chip's command set, Adafruit_GFX provides text and shapes.display() pushes the frame, which is why a forgotten display() looks identical to broken hardware.if(!display.begin(...)) costs three lines and turns a silent freeze into a message naming the wires to inspect.| Symptom | Likely cause | Fix |
|---|---|---|
| "display not found" | Wiring or wrong address | SDA→21, SCL→22, VCC→3.3 V, GND→GND; try 0x3D |
| Blank, no serial error | VCC floating | Confirm VCC is 3.3 V |
| Compile error (SSD1306/GFX) | Libraries missing | Install Adafruit SSD1306 and GFX |
| Garbled pixels | Loose I²C wires | Reseat SDA/SCL |