OLED Animation Maker OLED Maker
🍓 Pico + OLED

Raspberry Pi Pico + SSD1306 OLED in Arduino IDE (RP2040 and RP2350)

How I wire a Raspberry Pi Pico OLED display and program it in the Arduino IDE: the right core, the I2C pins you can use, working Adafruit and U8g2 code, and smooth animations on Pico, Pico W, Pico 2 and RP2040-Zero.

By Ashish Oct 29, 2026 9 min read Hardware
🧩

Four boards, one sketch

Pico, Pico W, Pico 2 and RP2040-Zero all run the same code.

📍

Pin map you can trust

Every valid I2C0 and I2C1 pin pair, and how to pick one in code.

📚

Two libraries

Adafruit SSD1306 and U8g2 examples, side by side.

🎞️

Room for animations

2 MB of flash fits hundreds of 128x64 frames.

Raspberry Pi Pico and an SSD1306 OLED module programmed with the Arduino IDE

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.

  1. 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
  2. Open Boards Manager, search pico, and install Raspberry Pi Pico/RP2040/RP2350 by Earle F. Philhower, III.
  3. Pick your board under Tools › Board: Raspberry Pi Pico, Pico W, Pico 2, or Waveshare RP2040 Zero.
  4. 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 pinPico pinPhysical pin number
GNDGND38 (or any GND)
VCC3V3(OUT)36
SDAGP46
SCLGP57

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.

BusSDA optionsSCL options
I2C0 (Wire)GP0, GP4, GP8, GP12, GP16, GP20GP1, GP5, GP9, GP13, GP17, GP21
I2C1 (Wire1)GP2, GP6, GP10, GP14, GP18, GP26GP3, 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.

  1. Open the OLED Animation Maker, pick a template, draw frames or import a GIF or MP4.
  2. Under board, choose Raspberry Pi Pico (or Generic Arduino) with SSD1306, 128x64, I2C, address 0x3C.
  3. Click Get the Code, paste the sketch into the Arduino IDE, and upload.
  4. If you used other pins, add Wire.setSDA() and Wire.setSCL() at the top of setup().

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

BoardChipRAMFlashGood for
Pico / Pico HRP2040, 133 MHz dual-core264 KB2 MBMost OLED animations
Pico WRP2040 + Wi-Fi264 KB2 MBClocks and weather screens
Pico 2 / Pico 2 WRP2350, 150 MHz520 KB4 MBLong clips, two screens
RP2040-ZeroRP2040264 KB2 MBTiny 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 called setSDA() after begin(). 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

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.