The OV7670 camera module with Arduino is a popular project for learning embedded image capture. While the OV7670 is a VGA (640×480) CMOS sensor, the Arduino Uno’s limited RAM restricts it to 320×240 (QVGA) resolution. This tutorial covers the OV7670 module with built-in AL422 FIFO buffer (the easier variant), complete wiring, I2C configuration, and image capture code for Arduino.
Table of Contents
- OV7670 Module Variants
- Wiring OV7670 to Arduino
- I2C (SCCB) Configuration
- Image Capture Code
- Displaying the Image
- Troubleshooting
- Better Alternatives for Image Capture
- Frequently Asked Questions
OV7670 Module Variants
Two main variants exist in India:
- OV7670 without FIFO (AL422): Requires capturing data at camera pixel clock speed — very difficult on Arduino Uno. Not recommended for beginners.
- OV7670 with FIFO AL422 buffer (OV7670 + FIFO board): The AL422 buffers a full frame, then Arduino reads at its own pace. This is the easier, recommended variant. Look for “OV7670 FIFO” when buying. Available in India at ₹300–600.
Wiring OV7670 to Arduino
OV7670 FIFO Module to Arduino Mega (Mega recommended over Uno for enough pins):
OV7670 Pin | Arduino Mega Pin | Notes
----------- ---------------- -----
3.3V | 3.3V | Camera power (NOT 5V!)
GND | GND |
SIOC | 21 (SCL) | I2C clock (SCCB)
SIOD | 20 (SDA) | I2C data
VSYNC | 25 | Frame sync
HREF | 23 | Line sync
PCLK | 22 | Pixel clock
XCLK | Digital 3 (PWM) | Generate ~12MHz clock
D0-D7 | 37-30 | 8-bit pixel data
RCLK | 26 | FIFO read clock
RRST | 27 | FIFO read reset
OE | GND | Output enable (always enable)
WEN | 28 | FIFO write enable
WRST | 29 | FIFO write reset
IMPORTANT: OV7670 is a 3.3V device. The I2C lines (SIOC/SIOD)
need 4.7kΩ pull-up resistors to 3.3V, NOT 5V.
Use a logic level shifter for data pins when using 5V Arduino.
I2C (SCCB) Configuration
OV7670 uses SCCB (Serial Camera Control Bus) which is I2C-compatible. The I2C address is 0x21 (write) / 0x42 (write including register):
#include
#define OV7670_ADDR 0x21
bool ov7670_write(uint8_t reg, uint8_t val) {
Wire.beginTransmission(OV7670_ADDR);
Wire.write(reg);
Wire.write(val);
return Wire.endTransmission() == 0;
}
uint8_t ov7670_read(uint8_t reg) {
Wire.beginTransmission(OV7670_ADDR);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom(OV7670_ADDR, 1);
return Wire.read();
}
void configureQVGA() {
ov7670_write(0x12, 0x80); // Reset all registers
delay(200);
ov7670_write(0x12, 0x14); // QVGA, RGB565
ov7670_write(0x40, 0xD0); // RGB565 format
ov7670_write(0x3A, 0x04); // UYVY format
// Add more register settings for colour correction
}
Image Capture Code
#define FIFO_RCLK 26
#define FIFO_RRST 27
#define FIFO_WEN 28
#define FIFO_WRST 29
#define FIFO_OE 30 // Pull low in hardware
// Data pins D0-D7 on PORTC of Mega (pins 37-30)
#define DATA_PORT PORTC
#define DATA_DDR DDRC
#define DATA_PIN PINC
void captureImage() {
// Reset FIFO write pointer
digitalWrite(FIFO_WRST, LOW);
delay(1);
digitalWrite(FIFO_WRST, HIGH);
// Enable FIFO write - camera writes one frame
digitalWrite(FIFO_WEN, HIGH);
// Wait for frame to complete (VSYNC pulse)
while (digitalRead(25) == LOW); // Wait for VSYNC high
while (digitalRead(25) == HIGH); // Wait for VSYNC low (frame done)
digitalWrite(FIFO_WEN, LOW);
// Reset FIFO read pointer
digitalWrite(FIFO_RRST, LOW);
delay(1);
digitalWrite(FIFO_RRST, HIGH);
// Read pixels: QVGA = 320*240*2 bytes (RGB565)
Serial.write(0xFF); // Frame start marker
for (int row = 0; row < 240; row++) {
for (int col = 0; col < 320; col++) {
// Read 2 bytes per pixel (RGB565)
digitalWrite(FIFO_RCLK, LOW);
uint8_t high = PINC; // Read high byte
digitalWrite(FIFO_RCLK, HIGH);
digitalWrite(FIFO_RCLK, LOW);
uint8_t low = PINC; // Read low byte
digitalWrite(FIFO_RCLK, HIGH);
Serial.write(high);
Serial.write(low);
}
}
}
Displaying the Image
Receive the RGB565 data on a PC via serial and convert to an image:
# Python script to receive and display OV7670 image
import serial
import numpy as np
from PIL import Image
ser = serial.Serial('COM3', 115200, timeout=5) # or /dev/ttyACM0
width, height = 320, 240
# Wait for frame start marker
while True:
if ser.read(1) == b'ÿ':
break
# Read raw RGB565 data
data = ser.read(width * height * 2)
# Convert RGB565 to RGB888
img_array = np.frombuffer(data, dtype=np.uint16).reshape(height, width)
r = ((img_array >> 11) & 0x1F) <> 5) & 0x3F) << 2
b = (img_array & 0x1F) << 3
rgb = np.stack([r, g, b], axis=-1).astype(np.uint8)
img = Image.fromarray(rgb)
img.save('ov7670_capture.png')
print("Image saved!")
Better Alternatives for Image Capture
The OV7670 with Arduino project is educational but challenging. For practical projects, consider:
- Arducam OV2640/OV5642 SPI shield: Direct SPI connection to Arduino, hardware JPEG compression, much simpler code. 2–5MP. Available at Zbotic.
- ESP32-CAM: Built-in OV2640 with Wi-Fi streaming. Much more capable than OV7670+Arduino for less cost.
- Raspberry Pi with CSI camera: For full computer vision applications with Python/OpenCV.
Frequently Asked Questions
Can I use OV7670 with Arduino Uno instead of Mega?
The Uno has insufficient pins for the 8-bit parallel data bus of OV7670. You can use tricks (multiplexing, shift registers) but it’s complex and unreliable. The Arduino Mega (or Due/SAMD21 boards) with dedicated 8-bit port registers is strongly recommended for OV7670 projects. Alternatively, use an SPI camera module (Arducam) with the Uno which requires only 4 SPI pins.
Why do I get colour-shifted or noise-filled images from OV7670?
OV7670 image quality issues usually stem from: (1) 5V logic driving 3.3V I2C lines — always use 3.3V pull-ups and a logic level shifter; (2) wrong register configuration — use a known-good register table from OV7670 community resources; (3) timing issues with the FIFO read — ensure proper clock generation for XCLK; (4) power supply noise — add 100nF bypass capacitors on the 3.3V supply near the module.
Add comment