ESP32-CAM AI-Thinker Board Overview: Pinout, OV2640, PSRAM and Limits
The AI-Thinker ESP32-CAM is a small, low-cost board that combines an ESP32 Wi-Fi and Bluetooth chip, an OV2640 camera, extra PSRAM, a microSD slot and a bright white LED. It is the cheapest way to build a Wi-Fi camera, but it is also a board with very few free pins, no USB port and a reputation for strange resets. Most of those problems disappear once you know what is on the board and which pins are already taken.
This article walks through the hardware block by block, gives the exact camera pin map used by the esp_camera driver, lists the pins you can actually use for your own projects, and ends with a self-test sketch that confirms your board, PSRAM and camera are working before you start a bigger project.
What Is on the Board
| Part | Details | Why it matters |
|---|---|---|
| ESP32 module (ESP32-S) | Dual-core Xtensa LX6, Wi-Fi 2.4 GHz, Bluetooth, 520 KB internal SRAM | Runs your Arduino sketch and the camera driver |
| Flash memory | 4 MB SPI flash | Holds the program; sets which partition schemes fit |
| PSRAM | External SPI RAM, 4 MB usable | Needed for large frame buffers and high resolutions |
| OV2640 camera | 2 MP sensor, outputs JPEG directly, max 1600 x 1200 (UXGA) | Hardware JPEG keeps images small enough for Wi-Fi |
| microSD slot | Uses the SD_MMC peripheral on GPIO 2, 4, 12, 13, 14, 15 | Storage for photos, but it uses most free pins |
| Flash LED | Bright white LED on GPIO 4, driven through a transistor | Lighting for photos; draws significant current |
| Red status LED | Small LED on GPIO 33, active LOW | Handy for status blinks without extra parts |
| Regulator | AMS1117 3.3 V linear regulator fed from the 5V pin | Explains why a weak 5 V supply causes brownouts |
| Antenna | PCB antenna plus an IPEX connector selected by a small resistor | External antenna improves range in metal enclosures |
The board has no USB-to-serial chip. To upload code you need an external USB-to-TTL adapter or the ESP32-CAM-MB plug-in board, which is covered in a separate tutorial. The RESET button on the board is on the underside, next to the camera connector.
Camera Pin Map (AI-Thinker)
The OV2640 is connected through an 8-bit parallel data bus plus clock, sync and SCCB (I2C-like) control lines. These pins are fixed by the PCB, so every ESP32-CAM sketch must use exactly this map. Other camera boards such as the ESP-EYE or M5Stack use different maps, which is a common reason for the error Camera probe failed.
Signal GPIO Signal GPIO PWDN 32 Y5 21 RESET -1 (n/c) Y4 19 XCLK 0 Y3 18 SIOD 26 Y2 5 SIOC 27 VSYNC 25 Y9 35 HREF 23 Y8 34 PCLK 22 Y7 39 Flash 4 Y6 36 Red LED 33
Note that XCLK is on GPIO 0. The ESP32 generates a 20 MHz clock on this pin for the sensor using the LEDC peripheral, and GPIO 0 is also the boot-mode strapping pin. That is why GPIO 0 must be pulled to GND only during upload and left free during normal running.
Which GPIOs Can You Use?
The header pins exposed on the board are 5V, GND, 3V3 (marked VCC or 3.3V), GPIO 0, 1, 3, 2, 4, 12, 13, 14, 15 and 16. Most of them have a second job, so choose carefully:
- GPIO 1 and 3 are U0TXD and U0RXD, the serial port used for uploading and
Serial.print. Use them only if you give up serial debugging. - GPIO 2, 4, 12, 13, 14, 15 are the microSD lines. If you do not use the SD card they are free. In 1-bit SD mode, GPIO 4, 12 and 13 are released.
- GPIO 12 is a strapping pin that selects flash voltage. It must be LOW at boot; a pull-up on this pin can stop the board from starting.
- GPIO 0 is XCLK and the boot pin. Do not connect anything to it.
- GPIO 16 is used as the PSRAM chip-select on this module. Avoid it if you use PSRAM.
- GPIO 4 drives the flash LED, so anything connected there will also light the LED.
- GPIO 33 is only the on-board red LED and is not on the header.
Board Self-Test Sketch
In the Arduino IDE, install the esp32 board package by Espressif, select the board AI Thinker ESP32-CAM, and upload the sketch below. It prints chip, flash and PSRAM information, starts the camera, reads the sensor ID, takes one test frame and blinks the red LED if everything passed.
#include "esp_camera.h"
// AI-Thinker ESP32-CAM camera pins
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
#define RED_LED 33 // on-board red LED, active LOW
bool initCamera() {
camera_config_t config = {}; // zero all fields first
config.ledc_channel = LEDC_CHANNEL_0; // LEDC makes the 20 MHz XCLK
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_VGA; // 640 x 480 for the test
config.jpeg_quality = 12;
config.fb_count = 1;
config.fb_location = psramFound() ? CAMERA_FB_IN_PSRAM : CAMERA_FB_IN_DRAM;
config.grab_mode = CAMERA_GRAB_LATEST;
return esp_camera_init(&config) == ESP_OK;
}
void setup() {
Serial.begin(115200);
delay(500);
pinMode(RED_LED, OUTPUT);
digitalWrite(RED_LED, HIGH); // LED off (active LOW)
Serial.printf("Chip: %s rev %d, %d cores\n", ESP.getChipModel(), ESP.getChipRevision(), ESP.getChipCores());
Serial.printf("Flash size: %u bytes\n", ESP.getFlashChipSize());
Serial.printf("PSRAM found: %s, size %u bytes\n", psramFound() ? "yes" : "no", ESP.getPsramSize());
if (!initCamera()) {
Serial.println("Camera init FAILED - check ribbon cable and board selection");
return;
}
sensor_t *s = esp_camera_sensor_get();
Serial.printf("Sensor PID: 0x%02X (OV2640 is 0x%02X)\n", s->id.PID, OV2640_PID);
camera_fb_t *fb = esp_camera_fb_get(); // grab one frame
if (!fb) {
Serial.println("Frame capture FAILED");
return;
}
Serial.printf("Test frame: %ux%u, %u bytes JPEG\n", fb->width, fb->height, fb->len);
esp_camera_fb_return(fb); // always give the buffer back
Serial.println("All tests passed");
for (int i = 0; i < 5; i++) { // blink red LED as a pass signal
digitalWrite(RED_LED, LOW);
delay(200);
digitalWrite(RED_LED, HIGH);
delay(200);
}
}
void loop() {
}
A healthy board reports PSRAM as found with a size of roughly 4 MB, a sensor PID of 0x26 and a VGA test frame of a few tens of kilobytes, depending on the scene. If PSRAM shows as not found, check that the board selection is AI Thinker ESP32-CAM, or that PSRAM is enabled when you use the generic ESP32 Dev Module.
Practical Limits and Common Problems
- Brownout detector was triggered: the supply cannot handle Wi-Fi and camera current peaks. Use a solid 5 V supply rated for at least 1 A to 2 A, with short, thick wires, rather than the 3.3 V pin of a USB-TTL adapter.
- Camera probe failed (0x105 / 0x20004): ribbon cable not fully latched, wrong board pin map, or a damaged sensor. Reseat the flat cable with the contacts facing the board.
- Weak Wi-Fi: the PCB antenna is small and partly shadowed by the camera. Some boards ship with the resistor set to the IPEX connector, in which case the PCB antenna is not connected at all.
- Heat: the ESP32 and regulator run warm during continuous streaming. Leave airflow around the board in an enclosure.
- Frame rate: expect smooth video only at VGA or smaller sizes. UXGA works for stills, but streaming at full size is slow over Wi-Fi.
Summary
The AI-Thinker ESP32-CAM packs an ESP32, an OV2640 with hardware JPEG, PSRAM and a microSD slot onto one board. The camera pin map is fixed, GPIO 0 doubles as XCLK and boot pin, GPIO 12 must stay low at boot, and the SD card uses most of the remaining pins. Run the self-test sketch on every new board: it confirms PSRAM, the sensor ID and frame capture, so later problems can be traced to your code rather than the hardware.