Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

SSD1306 OLED with ESP32 38-pin DevKitC — I2C wiring and board ID screen

Wire a 0.96″ SSD1306 OLED to a 38-pin ESP32 DevKitC on pins 21/22, auto-detect its address, and show the chip, silicon version, flash size and MAC of your clone.

Arduino C++Verified — core esp32 3.3.12, Adafruit SSD1306 2.5.17, 3 Nov 2026Compile-only

Community draft — report a mistake

A 0.96-inch SSD1306 OLED is the standard little status screen for ESP32 projects. On the 38-pin DevKitC it uses the same default I2C pins as the 30-pin DevKit V1 — GPIO21 (SDA) and GPIO22 (SCL) — but they are printed as plain 21 and 22, and the header around them is laid out differently.

The sketch is more than a “hello world”: it scans the bus, finds the OLED at 0x3C or 0x3D on its own, and shows what is actually on your board — chip model and silicon version, cores, clock, real flash-chip size and MAC address. With several unbranded 38-pin clones in a drawer, that tells you which is which.

Wiring

ESP32 DevKitC (38-pin)Only the pins used are shown · 3.3 V logicSSD1306 OLED 0.96″ (128×64, I2C)3V3GND22 · 2221 · 21GNDVCCSCLSDA
Every connection
ESP32 DevKitC (38-pin)SSD1306 OLED 0.96″ (128×64, I2C)WireNotes
GNDGNDblackThe GND at the top of the right header (J3 pin 1), right next to 22 and 21.
3V3VCCred3V3 is only on the left header (top). Not 5V — the OLED’s pull-ups would put 5 V on SDA/SCL.
22SCLyellowDefault I2C clock (GPIO22).
21SDAblueDefault I2C data (GPIO21). TX and RX sit between 22 and 21 — don’t shift a wire by one.

3.3–5 V (on-board regulator) part, 3.3 V board. Most modules have a regulator and run from 3.3 V or 5 V. Their I2C pull-ups go to VCC, so on ESP32 and Pico power the OLED from 3.3 V to keep SDA/SCL at a safe level.

Board quirks

  • On the 38-pin board the right header reads GND, 23, 22, TX, RX, 21, GND from the top. SDA and SCL are three holes apart with the USB-serial pins TX and RX in between; a wire one hole off lands on TX and garbles uploads.
  • The board has three GND pins: one on the left header (between 12 and 13) and two on the right (top, and below 21). Any of them works; the top-right one keeps the OLED wires short.
  • There is no 3V3 on the right header. The VCC wire has to cross from the top-left pin — route it around the antenna end, not over the module’s metal can.
  • Check the order printed on your OLED. Some clones are VCC, GND, SCL, SDA instead of GND, VCC…; reversed power kills the display.
  • The board-ID screen is useful with clones: the silicon version (v3.x is the newer chip found in WROOM-32E modules, v1.x the older one) and the size of the flash chip itself, read from the chip rather than from the Arduino IDE’s “Flash Size” setting. The exact model string differs between chip packages.

Code

ssd1306-esp32-38pin.inoArduino C++Open in PlaygroundOpen in PlaygroundFlash
// SSD1306 0.96" I2C OLED as a "which board is this?" screen on an ESP32 DevKitC (38-pin).
// GND → GND (right header, top), VCC → 3V3 (left header, top), SCL → 22, SDA → 21.
// Scans the I2C bus, then shows the chip model, revision, real flash-chip size and MAC —
// handy when a drawer holds several look-alike 38-pin clones.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <esp_flash.h>

Adafruit_SSD1306 display(128, 64, &Wire, -1);
uint8_t oledAddr = 0;

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);  // SDA, SCL — printed 21 and 22 on the right header

  Serial.print("I2C devices:");
  for (uint8_t a = 1; a < 127; a++) {
    Wire.beginTransmission(a);
    if (Wire.endTransmission() == 0) {
      Serial.printf(" 0x%02X", a);
      if (a == 0x3C || a == 0x3D) oledAddr = a;
    }
  }
  Serial.println();
  if (!oledAddr || !display.begin(SSD1306_SWITCHCAPVCC, oledAddr)) {
    Serial.println("No SSD1306 at 0x3C/0x3D — check GND/VCC order on your module and SDA=21, SCL=22");
    while (true) delay(1000);
  }

  uint32_t flashBytes = 0;
  esp_flash_get_physical_size(esp_flash_default_chip, &flashBytes);  // the chip itself, not the build setting
  uint16_t rev = ESP.getChipRevision();  // e.g. 301 = silicon v3.1
  uint8_t mac[6];
  uint64_t efuse = ESP.getEfuseMac();
  memcpy(mac, &efuse, 6);

  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.printf("%s\n", ESP.getChipModel());
  display.printf("Silicon v%d.%d\n", rev / 100, rev % 100);
  display.printf("%d cores, %lu MHz\n", ESP.getChipCores(), (unsigned long)ESP.getCpuFreqMHz());
  display.printf("Flash chip %lu MB\n", (unsigned long)(flashBytes / (1024 * 1024)));
  display.printf("MAC %02X%02X%02X%02X%02X%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  display.printf("OLED at 0x%02X\n", oledAddr);
  display.display();

  Serial.printf("%s, silicon v%d.%d, %d cores, %lu MHz, flash chip %lu MB, MAC %02X:%02X:%02X:%02X:%02X:%02X\n",
                ESP.getChipModel(), rev / 100, rev % 100, ESP.getChipCores(), (unsigned long)ESP.getCpuFreqMHz(),
                (unsigned long)(flashBytes / (1024 * 1024)), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

void loop() {
  // Bottom line: uptime, redrawn once a second.
  static uint32_t last = 0;
  if (millis() - last < 1000) return;
  last = millis();
  display.fillRect(0, 48, 128, 16, SSD1306_BLACK);
  display.setCursor(0, 52);
  display.printf("Up %lus", (unsigned long)(millis() / 1000));
  display.display();
}

Library: Adafruit SSD1306 (Adafruit (also install Adafruit GFX Library)) — compiled with Adafruit SSD1306 2.5.17.

Expected serial output

Serial Monitor · 115200Output
I2C devices: 0x3C
ESP32-D0WDQ6-V3, silicon v3.0, 2 cores, 240 MHz, flash chip 4 MB, MAC 24:6F:28:A1:B2:C3

Common errors

“No SSD1306 at 0x3C/0x3D” and no I2C devices listed

Nothing answers on 21/22. Check SDA is on the pin printed 21 and SCL on 22, VCC on 3V3, and that power isn’t reversed. Pins 21/22 on clones may be printed G21/G22 or IO21/IO22.

Upload fails after wiring the OLED

A wire is on TX or RX (between 22 and 21). Those are the USB-serial lines; move it one hole over.

Display shows only the top half

It is a 128×32 module. Change the constructor to Adafruit_SSD1306 display(128, 32, &Wire, -1).

↑↓ to move · Enter to open