A Raspberry Pi Pico OLED setup is my favourite cheap combo for animations. The RP2040 has far more RAM than an Uno, the board costs about the same, and a 0.96 inch SSD1306 plugs straight into its I2C pins. This guide is for people who want to stay in the Arduino IDE: install the right core, choose pins, run Adafruit SSD1306 or U8g2 code, and play animations exported from the OLED Animation Maker.
If you prefer Python, I have a separate MicroPython SSD1306 guide for the Pico. Everything below is C++ and Arduino libraries.
What you need
- A Raspberry Pi Pico, Pico H, Pico W, Pico 2, Pico 2 W, or an RP2040 board such as the Waveshare RP2040-Zero
- A 4-pin I2C SSD1306 OLED, 128x64 (0.96 inch) or 128x32 (0.91 inch)
- 4 jumper wires and a micro-USB or USB-C data cable (some cables are charge-only and the board never shows up)
- Arduino IDE 2.x
Step 1: install the arduino-pico core
There are two Arduino cores for the Pico. I use the community core by Earle Philhower, called arduino-pico. It compiles fast, supports RP2350 boards like the Pico 2, and lets you choose your I2C pins. The official "Arduino Mbed OS RP2040" core also exists, but it fixes the pins and builds slowly.
- Open File › Preferences and add this URL to Additional boards manager URLs:
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json - Open Boards Manager, search pico, and install Raspberry Pi Pico/RP2040/RP2350 by Earle F. Philhower, III.
- Pick your board under Tools › Board: Raspberry Pi Pico, Pico W, Pico 2, or Waveshare RP2040 Zero.
- In Library Manager, install Adafruit SSD1306 (with Adafruit GFX) and, if you want the second example, U8g2.
The first upload is the only awkward one. Hold the BOOTSEL button while you plug the USB cable in, release it, and the board appears as a drive. Click Upload. After that the IDE resets the board by itself and you can pick its COM port normally.
Step 2: wiring the OLED to the Pico
| OLED pin | Pico pin | Physical pin number |
|---|---|---|
| GND | GND | 38 (or any GND) |
| VCC | 3V3(OUT) | 36 |
| SDA | GP4 | 6 |
| SCL | GP5 | 7 |
Use 3V3(OUT), not VBUS. The Pico's GPIO pins are 3.3V only, and the pull-up resistors on the OLED module pull to whatever voltage you feed it. At 3.3V everything is safe. The Pico 2 and Pico W use the same pinout, so this table works for them too.
Which RP2040 pins can be I2C?
The RP2040 and RP2350 have two I2C blocks, and each can use several pin pairs. In arduino-pico, Wire is I2C0 and
Wire1 is I2C1. The defaults are GP4/GP5 for Wire and GP26/GP27 for Wire1.
| Bus | SDA options | SCL options |
|---|---|---|
I2C0 (Wire) | GP0, GP4, GP8, GP12, GP16, GP20 | GP1, GP5, GP9, GP13, GP17, GP21 |
I2C1 (Wire1) | GP2, GP6, GP10, GP14, GP18, GP26 | GP3, GP7, GP11, GP15, GP19, GP27 |
To use other pins, call Wire.setSDA() and Wire.setSCL() before Wire.begin(),
and pick both pins from the same bus row. Mixing a GP4 SDA with a GP7 SCL does not work, because those belong to different
I2C blocks.
Wire.setSDA(16); // GP16, I2C0
Wire.setSCL(17); // GP17, I2C0
Wire.begin();
On the tiny RP2040-Zero I usually wire to GP4 and GP5 too, since they sit next to each other on the edge. Check your board's silkscreen, because the physical pin numbers differ from the Pico.
Example 1: Adafruit SSD1306 hello world on the Pico
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Adafruit_SSD1306 display(128, 64, &Wire, -1);
void setup() {
Serial.begin(115200);
Wire.setSDA(4); // defaults, shown so you can change them
Wire.setSCL(5);
Wire.begin();
Wire.setClock(400000); // 400 kHz fast mode
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("SSD1306 not found");
while (true) delay(100);
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(2);
display.setCursor(0, 0);
display.println("Pico +");
display.println("SSD1306");
display.setTextSize(1);
display.print("CPU MHz: ");
display.println(rp2040.f_cpu() / 1000000);
display.display();
}
void loop() {}
rp2040.f_cpu() is an arduino-pico helper that returns the clock speed, so you can see the board is really running
your code. If the Serial Monitor prints "SSD1306 not found", run an I2C scanner (the one in my
Arduino OLED example code works on the Pico unchanged) and use the
address it reports.
Example 2: the same screen with U8g2
U8g2 supports far more controllers and fonts. On the Pico I use the full-buffer hardware I2C constructor.
#include <Wire.h>
#include <U8g2lib.h>
// _F_ = full 1 KB buffer, HW_I2C = uses Wire (GP4/GP5)
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
void setup() {
Wire.setSDA(4);
Wire.setSCL(5);
u8g2.begin();
}
void loop() {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawStr(0, 20, "RP2040");
u8g2.setFont(u8g2_font_6x10_tf);
u8g2.setCursor(0, 40);
u8g2.print("Uptime: ");
u8g2.print(millis() / 1000);
u8g2.print(" s");
u8g2.sendBuffer();
delay(200);
}
For a module on Wire1 use the U8G2_SSD1306_128X64_NONAME_F_2ND_HW_I2C constructor. For a 1.3 inch
SH1106 module, swap in U8G2_SH1106_128X64_NONAME_F_HW_I2C. Not sure which library to pick? I compared them in
U8g2 vs Adafruit SSD1306.
Example 3: a smooth bouncing ball (frame timing)
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Adafruit_SSD1306 display(128, 64, &Wire, -1);
float x = 10, y = 10, vx = 2.2, vy = 1.6;
void setup() {
Wire.begin();
Wire.setClock(1000000); // the RP2040 copes with 1 MHz; drop to 400000 if you see glitches
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
}
void loop() {
unsigned long t0 = millis();
x += vx; y += vy;
if (x < 4 || x > 123) vx = -vx;
if (y < 4 || y > 59) vy = -vy;
display.clearDisplay();
display.drawRect(0, 0, 128, 64, SSD1306_WHITE);
display.fillCircle((int)x, (int)y, 4, SSD1306_WHITE);
display.display();
while (millis() - t0 < 16) {} // about 60 fps if the bus keeps up
}
At 400 kHz a full frame takes about 25 ms to send, which caps you near 40 fps. Many SSD1306 modules accept 1 MHz on short wires, and then the Pico easily reaches 60 fps. If the picture tears or the board stops answering, go back to 400 kHz.
Playing an animation from OLED Animation Maker
This is where the Pico shines. An Uno runs out of room after about 20 full frames. The Pico's 2 MB of flash holds hundreds.
- Open the OLED Animation Maker, pick a template, draw frames or import a GIF or MP4.
- Under board, choose Raspberry Pi Pico (or Generic Arduino) with SSD1306, 128x64, I2C, address 0x3C.
- Click Get the Code, paste the sketch into the Arduino IDE, and upload.
- If you used other pins, add
Wire.setSDA()andWire.setSCL()at the top ofsetup().
The exported arrays are plain const unsigned char ... PROGMEM. On the RP2040 PROGMEM is just
ignored and const data already lives in flash, so the same sketch compiles on an Uno and on a Pico. Want a
reactive face instead of a fixed clip? The OLED Robot Eyes generator exports a sketch
that draws blinking, mood-changing eyes in code, which suits the Pico's speed well.
Pico vs Pico W vs Pico 2 for OLED projects
| Board | Chip | RAM | Flash | Good for |
|---|---|---|---|---|
| Pico / Pico H | RP2040, 133 MHz dual-core | 264 KB | 2 MB | Most OLED animations |
| Pico W | RP2040 + Wi-Fi | 264 KB | 2 MB | Clocks and weather screens |
| Pico 2 / Pico 2 W | RP2350, 150 MHz | 520 KB | 4 MB | Long clips, two screens |
| RP2040-Zero | RP2040 | 264 KB | 2 MB | Tiny wearables and badges |
On the Pico W the Wi-Fi chip uses a few internal pins, but GP4 and GP5 stay free, so the wiring above still works. For two
screens, put the second one on Wire1 or give it address 0x3D on the same bus, as in the
dual OLED guide.
Troubleshooting on the Pico
- No COM port after the first upload. Use a data cable, then hold BOOTSEL and plug in once more.
- "SSD1306 not found". SDA and SCL swapped, or pins from two different I2C buses. Re-check the table.
- Code freezes in
Wire.begin(). You calledsetSDA()afterbegin(). Move it above. - Picture shifted two pixels with noise on the right. The module is SH1106. See SH1106 vs SSD1306.
- Square 128x128 module shows garbage. It is probably SH1107. My SH1107 and SSD1315 guide covers the right library.
Animations for your Pico
Draw or import frames, preview them on a virtual 128x64 screen in the OLED Simulator, and export a sketch that runs on Pico, Pico 2, ESP32 or Uno.
Open OLED Animation Maker →Popular searches: raspberry pi pico oled · rp2040 ssd1306 · pico oled display · pico 2 rp2350 oled · pico w oled · rp2040 zero oled · u8g2 pico · arduino-pico i2c pins
Frequently asked questions
Which pins do I use for an OLED on the Raspberry Pi Pico in Arduino IDE?
With the Earle Philhower arduino-pico core, Wire defaults to GP4 (SDA) and GP5 (SCL), which are physical pins 6 and 7. You can move it to any valid I2C0 pair with Wire.setSDA() and Wire.setSCL() before Wire.begin().
Which Arduino core should I use for the Pico?
The community core by Earle Philhower (arduino-pico). It supports Pico, Pico W, Pico 2, Pico 2 W and most RP2040 and RP2350 boards, and lets you choose the I2C pins. The official Arduino Mbed OS RP2040 core works too but is slower to compile and less flexible with pins.
Does the Raspberry Pi Pico 2 (RP2350) work with SSD1306?
Yes. Select Raspberry Pi Pico 2 as the board in the arduino-pico core. The wiring, the default pins GP4 and GP5, and the Adafruit SSD1306 and U8g2 code are the same as on the original Pico.
Is the Pico 3.3V or 5V for the OLED?
The Pico GPIO is 3.3V only. Power the OLED from the 3V3(OUT) pin, physical pin 36. Most SSD1306 modules run happily at 3.3V, and their pull-ups then pull to 3.3V too, which is safe for the Pico.
How many animation frames fit on a Pico?
A 128x64 frame is 1,024 bytes. The Pico has 2 MB of flash, so even several hundred frames fit. RAM is not a problem either: the RP2040 has 264 KB and the RP2350 has 520 KB.
Should I use MicroPython or Arduino on the Pico for an OLED?
Use Arduino if you already know C++ or want to reuse Arduino libraries and sketches exported from OLED Animation Maker. Use MicroPython if you want to edit code live without compiling. Both drive an SSD1306 well.
Related guides you might like
- MicroPython SSD1306 animation on the Pico
- Arduino OLED example code (10 sketches)
- U8g2 vs Adafruit SSD1306
- Two OLED displays on one board
- SH1107 and SSD1315 OLED guide
- Simulate an SSD1306 in Wokwi
Free tools: OLED Animation Maker for your SSD1306 and SH1106 animations, OLED Robot Eyes for animated robot faces, Image to Byte Array for your logos, the OLED Simulator to preview before you flash, and Assemble Studio for your name and logo animations. See all tools.