OV7670 Camera โ Basic image capture
The OV7670 is a low-cost VGA (640ร480) CMOS camera sensor from OmniVision that outputs 8-bit parallel data with pixel, line, and frame synchronisation signals. It supports many output formats including YUV, RGB565, and grayscale via SCCB (IยฒC-like) register configuration. Capturing images with an Arduino Uno is challenging due to limited RAM (2 KB) and clock speed โ practical use requires a FIFO buffer module (AL422B) or restricting output to low-resolution grayscale (QQVGA 160ร120). This tutorial covers the FIFO-based OV7670 module.
For this interfacing you need the following components:
- Arduino board (Uno, Nano, Mega โ preferably Mega for more RAM)
- OV7670 camera module with FIFO (AL422B) buffer
- Breadboard and jumper wires
- USB cable to connect Arduino to your computer
- (Optional) SD card module for storing captured images
Schematic
Connect the OV7670 FIFO module to the Arduino as follows:
OV7670 FIFO Module Arduino Mega (recommended)
------------------ --------------------------
VCC --> 5V
GND --> GND
SIOC (SCL) --> Digital Pin 21 (SCL on Mega)
SIOD (SDA) --> Digital Pin 20 (SDA on Mega)
VSYNC --> Digital Pin 2 (interrupt)
FIFO_RCLK --> Digital Pin 6
FIFO_RRST --> Digital Pin 7
FIFO_OE --> Digital Pin 5
FIFO_WRST --> Digital Pin 8
FIFO_WR --> Digital Pin 4
D0โD7 --> Digital Pins 22โ29 (PORT A on Mega)
XCLK --> Digital Pin 3 (PWM โ generates clock)
The FIFO buffer stores a full frame so the Arduino can read it out at its own pace. The OV7670 writes continuously to the FIFO, and the Arduino reads the FIFO when a frame is ready.
Arduino Uno limitations: Due to the 8-bit parallel data bus requiring 8 GPIOs plus control pins, and only 2 KB RAM (insufficient for a VGA frame buffer), the Uno is not practical for OV7670 capture without external storage. A Mega or Due is strongly recommended.
Pin Map
| OV7670 Pin | Name | Mega Connection |
|---|---|---|
| VCC | Power | 5V |
| GND | Ground | GND |
| SIOC | SCCB clock | 21 (SCL) |
| SIOD | SCCB data | 20 (SDA) |
| VSYNC | Frame sync | 2 (interrupt) |
| XCLK | Master clock | 3 (PWM ~8 MHz) |
| D0โD7 | Pixel data (8-bit) | 22โ29 |
| FIFO_RCLK | FIFO read clock | 6 |
| FIFO_RRST | FIFO read reset | 7 |
| FIFO_OE | FIFO output enable | 5 |
| FIFO_WRST | FIFO write reset | 8 |
| FIFO_WR | FIFO write enable | 4 |
| PWDN | Power down | GND (normal mode) |
| RESET | Reset | 5V (inactive) |
Install necessary Library
Install the Arduino_OV767X library by Arduino via the Library Manager (Tools > Manage Libraries). This library supports OV7670 with FIFO on Mega and Due.
arduino-cli lib install "Arduino_OV767X"
For direct register-level control, install the OV7670 library by Ricardo Andrade.
Code with complete explanation
This sketch captures a QQVGA (160ร120) grayscale image from the OV7670 and outputs it as a PGM (portable graymap) over the Serial Monitor, which can be saved and viewed in any image viewer.
#include <Arduino_OV767X.h>
const int cameraWidth = 160;
const int cameraHeight = 120;
byte imageBuffer[cameraWidth * cameraHeight];
void setup()
{
Serial.begin(115200); // High baud rate for image transfer
// Initialise the camera
if (!Camera.begin(QQVGA, GRAYSCALE, cameraWidth, cameraHeight))
{
Serial.println("Camera init failed!");
while (1) {}
}
// Custom register settings (optional)
// Camera.writeRegister(0x11, 0x00); // Clock divider
// Camera.writeRegister(0x3D, 0x00); // Reserved
Serial.println("Camera ready. Type 'c' to capture.");
Serial.println();
}
void loop()
{
if (Serial.available() && Serial.read() == 'c')
{
captureAndSendPGM();
}
}
void captureAndSendPGM()
{
// Read a frame from the camera
Camera.readFrame(imageBuffer);
// Send PGM header
Serial.println("P5");
Serial.print(cameraWidth);
Serial.print(" ");
Serial.println(cameraHeight);
Serial.println(255);
// Send pixel data (raw bytes)
Serial.write(imageBuffer, cameraWidth * cameraHeight);
}
Code breakdown
#include <Arduino_OV767X.h>โ includes the Arduino OV767X camera library.Camera.begin(QQVGA, GRAYSCALE, width, height)โ initialises the camera in QQVGA (160ร120) grayscale mode. Other resolutions includeVGA(640ร480) andQVGA(320ร240). Other pixel formats includeRGB565andYUV.Camera.readFrame(buffer)โ reads one complete frame into the provided byte array. The array must be at leastwidth ร heightbytes for grayscale orwidth ร height ร 2bytes for RGB565.Camera.writeRegister(reg, value)โ sets a register on the OV7670 via SCCB. Useful for adjusting gain, exposure, white balance, and output format.- The PGM format:
P5header, width and height in ASCII, max pixel value (255), then raw binary pixel data.
Capturing RGB565 frames
For colour images:
const int cameraWidth = 320;
const int cameraHeight = 240;
byte imageBuffer[cameraWidth * cameraHeight * 2]; // 2 bytes per pixel
void setup()
{
Camera.begin(QVGA, RGB565, cameraWidth, cameraHeight);
}
void captureRGB()
{
Camera.readFrame(imageBuffer);
// Send as PPM (portable pixmap)
Serial.println("P6");
Serial.print(cameraWidth);
Serial.print(" ");
Serial.println(cameraHeight);
Serial.println(255);
// Convert RGB565 to RGB888 and send
for (int i = 0; i < cameraWidth * cameraHeight; i++)
{
uint16_t pixel = (imageBuffer[i * 2 + 1] << 8) | imageBuffer[i * 2];
uint8_t r = (pixel >> 11) & 0x1F; r = (r << 3) | (r >> 2);
uint8_t g = (pixel >> 5) & 0x3F; g = (g << 2) | (g >> 4);
uint8_t b = pixel & 0x1F; b = (b << 3) | (b >> 2);
Serial.write(r);
Serial.write(g);
Serial.write(b);
}
}
Using an SD card for storage
To save images to an SD card instead of sending them over serial:
#include <SD.h>
const int chipSelect = 53; // Mega SS pin
void setup()
{
SD.begin(chipSelect);
}
void captureToSD()
{
Camera.readFrame(imageBuffer);
File imageFile = SD.open("image.pgm", FILE_WRITE);
if (imageFile)
{
imageFile.println("P5");
imageFile.println("160 120");
imageFile.println(255);
imageFile.write(imageBuffer, 160 * 120);
imageFile.close();
Serial.println("Image saved to SD card");
}
}
Steps to perform this interfacing
- Connect the OV7670 FIFO module to the Arduino Mega as shown in the schematic. The data pins (D0โD7) must be connected to pins 22โ29 for the Arduino_OV767X library defaults.
- Install the Arduino_OV767X library via the Library Manager.
- Copy the code into the Arduino IDE.
- Select Arduino Mega or Due as the board (
Tools > Board). - Upload the sketch.
- Open the Serial Monitor (
Tools > Serial Monitor, set baud rate to 115200). - Type
cand press Enter โ the Serial Monitor will display raw PGM binary data. - Save the Serial Monitor output to a file with a
.pgmextension and open it in an image viewer (GIMP, IrfanView, etc.) to see the captured image.
Viewing the PGM image
- On Linux: Save the serial output to a file:
cat /dev/ttyACM0 > capture.pgm(after theccommand is sent). - On Windows: Use a serial terminal that supports binary logging (e.g., Tera Term, RealTerm).
- Convert to JPEG/PNG: Use ImageMagick:
convert capture.pgm capture.jpg.
Caution
- Arduino Uno is not suitable: The OV7670 requires 8โ12 GPIOs for the parallel data bus, which leaves few pins for other functions on a Uno. More importantly, a full VGA frame (307 KB) far exceeds the Unoโs 2 KB RAM. Even QQVGA (19 KB) exceeds it. The FIFO buffer helps with timing, but the RAM constraint remains โ use a Mega (8 KB) for QQVGA grayscale or a Due (96 KB) for larger images.
- FIFO module required: Without the AL422B FIFO buffer, the OV7670 must be read in real time (pixel-by-pixel as they arrive). This is nearly impossible on an Arduino because the pixel clock runs at 8โ24 MHz. Always use the FIFO version of the module for any Arduino-based capture.
- XCLK generation: The OV7670 requires an external master clock (XCLK) of 8โ24 MHz. The Arduino_OV767X library generates this via PWM on pin 3 (~8 MHz). The frequency must be stable โ toggle rate variations cause corrupted images.
- Register configuration: The default OV7670 registers produce VGA YUV output. The library configures the sensor for the requested resolution and format via SCCB during
Camera.begin(). If the image appears garbled, the register settings may need adjustment for your specific module variant (OV7670 vs OV7675, or different clock configurations). - Serial bandwidth: Sending a full image over serial is slow. At 115200 baud (~11.5 KB/s), a QQVGA grayscale frame (19 KB) takes about 1.7 seconds. A VGA grayscale frame (307 KB) would take 27 seconds โ faster interfaces (SPI to SD card) or hardware flow control are recommended for larger images.
- Light sensitivity: The OV7670 requires good lighting. Without adequate illumination, images will be dark and noisy even at maximum gain. The default automatic gain control (AGC) and automatic white balance (AWB) can be adjusted via registers to improve low-light performance.
- Lens focus: The OV7670 lens is fixed-focus, set for distances of about 30 cm to infinity. For close-up shots (macro), add an external M12x0.5 lens mount with adjustable focus.