---
title: "PicoCamera can see 1600×1200. Its RAM sees rather less"
locale: "en"
url: "https://irz.fr/en/articles/picocamera-memory-wall-en"
markdown_url: "https://irz.fr/en/articles/picocamera-memory-wall-en.md"
category: "tech"
tags: ["RP2040", "RP2350", "camera", "Arduino", "PIO", "DMA"]
published_at: "2026-08-20T09:01:00.000Z"
author: "Hugo Marchal"
translation: "https://irz.fr/fr/articles/picocamera-memory-wall-fr.md"
---

# PicoCamera can see 1600×1200. Its RAM sees rather less

PicoCamera brings OV2640, OV3660 and OV7670 capture to RP2040/RP2350. For raw images, though, the real ceiling is not the sensor but the framebuffer.

An OV2640 can produce a 1600 × 1200 image, and PicoCamera knows how to request it, but the interesting part begins when that frame reaches the microcontroller and has to fit in its memory.

I checked PicoCamera at commit `6c558bd`, the source tagged as version 0.4.0 on August 18, because that release adds PSRAM-backed frame buffers for RP2350 boards.[1](https://github.com/umeiko/PicoCamera/tree/6c558bdbc6c73d2f17f99eaa2452e26d400f3405) The change matters precisely because **the camera can offer images much larger than the internal memory available to hold them**.

The RP2040/RP2350 compilation claim is well supported. In practice, the useful dividing line is the amount of image data that can exist in memory at one time, first in the larger SRAM of RP2350 and then in external PSRAM when a board actually provides it.

## Three sensors

PicoCamera deliberately mirrors part of the `esp32-camera` API: the `esp_` prefix becomes `pico_`, and a sketch continues to configure a `camera_config_t`, borrow a frame through `pico_camera_fb_get()` and obtain a runtime sensor object for controls.[1](https://github.com/umeiko/PicoCamera/tree/6c558bdbc6c73d2f17f99eaa2452e26d400f3405)

Three sensors are listed as hardware-verified: **OV2640**, **OV3660** and **OV7670**. The first two can emit JPEG in the sensor itself, whereas the OV7670 supports RGB565 here and has no onboard JPEG encoder.[1](https://github.com/umeiko/PicoCamera/tree/6c558bdbc6c73d2f17f99eaa2452e26d400f3405)

Capture uses Raspberry Pi's PIO and DMA hardware, with the eight-bit camera bus occupying consecutive GPIOs and VSYNC, HREF and PCLK selected at runtime; sensor configuration travels over SCCB through the hardware I2C peripheral, so the normal pin-routing rules of the chip remain part of the design.[2](https://github.com/umeiko/PicoCamera/blob/6c558bdbc6c73d2f17f99eaa2452e26d400f3405/docs/user_guide.md)

> Illustration: Python window displaying a JPEG frame sent by PicoCamera. One example sends captured frames over USB serial to a small Python viewer. Acquisition still happens on the microcontroller. Credit: [umeiko / PicoCamera](https://github.com/umeiko/PicoCamera/tree/6c558bdbc6c73d2f17f99eaa2452e26d400f3405/examples/push_image_to_python).

Somebody arriving from an ESP32 camera project gets a familiar programming surface that reduces porting work, but the memory and pin constraints underneath it remain those of a Pico-class microcontroller.

## The RGB wall

The cost of one RGB565 frame is easy to calculate because every pixel takes **two bytes**.[2](https://github.com/umeiko/PicoCamera/blob/6c558bdbc6c73d2f17f99eaa2452e26d400f3405/docs/user_guide.md) At 320 × 240, that is 153,600 bytes, or 150 KiB. Moving to 640 × 480 raises that to 614,400 bytes, exactly 600 KiB, and a 1600 × 1200 framebuffer reaches 3,840,000 bytes, about 3.66 MiB.

Raspberry Pi specifies **264 KiB of SRAM** for RP2040 and **520 KiB** for RP2350.[5](https://www.raspberrypi.com/documentation/microcontrollers/pico-series.html) Even if every byte were available to the camera, a 600 KiB VGA RGB565 frame would fit in neither chip's internal SRAM.

PicoCamera's own guide therefore calls **QVGA 320 × 240 the practical RGB565 ceiling on RP2040**.[2](https://github.com/umeiko/PicoCamera/blob/6c558bdbc6c73d2f17f99eaa2452e26d400f3405/docs/user_guide.md) Real programs also need memory for their stack, code data, driver structures and whatever work happens after capture.

> One RGB565 frame
> **The sensor grows faster than SRAM**
> - QVGA 320×240: practical on RP2040.: 150 KiB
> - VGA 640×480: larger than RP2040 and RP2350 SRAM.: 600 KiB
> - UXGA 1600×1200: requires external memory when raw.: 3.66 MiB
> - Total RP2040 / RP2350 on-chip SRAM.: 264 / 520 KiB
> IRZ calculation: width × height × two bytes. Total chip SRAM is obviously not all available to a framebuffer.

This is why moving from RP2040 to RP2350 does not solve raw capture by itself. The newer chip nearly doubles internal SRAM, yet VGA RGB565 still exceeds it by about 80 KiB before any application overhead is counted.

## PSRAM changes the map

PicoCamera 0.4.0 adds three buffer-location policies: SRAM, PSRAM or automatic. On an RP2350 board whose PSRAM support is enabled in the Arduino core, automatic allocation tries `pmalloc()` first and falls back to SRAM, whereas an explicit PSRAM request fails when external memory is unavailable.[3](https://github.com/umeiko/PicoCamera/blob/6c558bdbc6c73d2f17f99eaa2452e26d400f3405/src/pico_camera.cpp)

RP2040 has no equivalent path in the library, so its frame buffers remain in SRAM.[2](https://github.com/umeiko/PicoCamera/blob/6c558bdbc6c73d2f17f99eaa2452e26d400f3405/docs/user_guide.md) External memory becomes a functional boundary: several megabytes of PSRAM make VGA, SVGA or UXGA RGB565 buffers plausible, while a board lacking that memory cannot make an oversized raw buffer fit.

Buffer count matters as well: two QVGA RGB565 buffers already require 300 KiB before allocation overhead, so `fb_count = 2` exceeds the RP2040's entire SRAM although one QVGA frame by itself fits comfortably enough.[3](https://github.com/umeiko/PicoCamera/blob/6c558bdbc6c73d2f17f99eaa2452e26d400f3405/src/pico_camera.cpp)

## JPEG bends the rule

OV2640 and OV3660 contain JPEG encoders, allowing PicoCamera to receive compressed data instead of reserving two bytes for every pixel.[1](https://github.com/umeiko/PicoCamera/tree/6c558bdbc6c73d2f17f99eaa2452e26d400f3405)

The library cannot know the compressed size beforehand. It allocates an estimated `width × height / 4 + 8 KiB` buffer and captures the variable-length JPEG into that space.[3](https://github.com/umeiko/PicoCamera/blob/6c558bdbc6c73d2f17f99eaa2452e26d400f3405/src/pico_camera.cpp)

Under that formula, UXGA needs about **477 KiB** of buffer. It falls below the RP2350's 520 KiB total on paper, although the margin for everything else would be tiny, and remains far beyond RP2040's 264 KiB.

To be sure, 477 KiB is only an allocation estimate for JPEG output. The project's own documentation warns that it covers typical scenes and **pathological noise can overflow the allocation**.[2](https://github.com/umeiko/PicoCamera/blob/6c558bdbc6c73d2f17f99eaa2452e26d400f3405/docs/user_guide.md) Compression makes larger captures possible with much less RAM, but variable-size output remains variable.

## What “compatible” means

The other part of the audit was simpler: check whether RP2040/RP2350 compatibility exists somewhere beyond the README.

At commit `6c558bd`, upstream CI compiles the four main examples across **RP2040, RP2350 Arm, RP2350 RISC-V and an RP2350 environment with the PSRAM code path enabled**, using warnings-as-errors. A separate workflow also builds the bare Pico SDK CMake example at the same commit.[4](https://github.com/umeiko/PicoCamera/actions/runs/32113283023)

Those jobs verify compilation. They do not put a physical camera on every entry in the matrix. Hardware support is documented separately: the repository marks OV2640, OV3660 and OV7670 as tested, but publishes no comprehensive throughput or frame-rate benchmark across every resolution.[1](https://github.com/umeiko/PicoCamera/tree/6c558bdbc6c73d2f17f99eaa2452e26d400f3405)

Capture is synchronous as well: `pico_camera_fb_get()` performs a blocking frame capture, while the `grab_mode` behaviour available in `esp32-camera` has not been implemented and the guide says so explicitly.[2](https://github.com/umeiko/PicoCamera/blob/6c558bdbc6c73d2f17f99eaa2452e26d400f3405/docs/user_guide.md)

## An API cannot create RAM

Adafruit highlighted PicoCamera as an Arduino-compatible camera library for RP2040 and RP2350.[6](https://blog.adafruit.com/2026/08/19/picocamera-an-arduino-compatible-camera-library-for-the-rp2040-rp2350/) That description is accurate, but the project's most useful quality may be the constraints it leaves visible.

The constraints remain visible all the way down: eight consecutive GPIOs for the camera bus, SCCB pins constrained by hardware I2C routing, JPEG rejected on a sensor lacking an encoder, allocation failure when a frame will not fit, and PSRAM only where the board really carries it.

Those details make the compatibility claim credible because PicoCamera brings the programming model closer to `esp32-camera` while keeping the very different memory architecture of an RP2040 visible.

A maker therefore has to make the useful decision earlier: **decide what must happen to the image before deciding how large it should be**. Local processing on a raw QVGA frame, a larger JPEG sent elsewhere and a VGA RGB565 stream are three very different memory problems.

The sensor can see big, while the framebuffer keeps the bill for every pixel.

## References

1. [umeiko, PicoCamera 0.4.0 at commit 6c558bd](https://github.com/umeiko/PicoCamera/tree/6c558bdbc6c73d2f17f99eaa2452e26d400f3405)
2. [PicoCamera user guide](https://github.com/umeiko/PicoCamera/blob/6c558bdbc6c73d2f17f99eaa2452e26d400f3405/docs/user_guide.md)
3. [PicoCamera framebuffer allocation code](https://github.com/umeiko/PicoCamera/blob/6c558bdbc6c73d2f17f99eaa2452e26d400f3405/src/pico_camera.cpp)
4. [PicoCamera multi-architecture CI](https://github.com/umeiko/PicoCamera/actions/runs/32113283023)
5. [Raspberry Pi, Pico microcontroller boards](https://www.raspberrypi.com/documentation/microcontrollers/pico-series.html)
6. [Adafruit, PicoCamera: an Arduino-compatible camera library for the RP2040 / RP2350](https://blog.adafruit.com/2026/08/19/picocamera-an-arduino-compatible-camera-library-for-the-rp2040-rp2350/)
