kpower
Published © GPL3+

OV7670 Camera and Image Sensor with Nano 33 BLE

Use the OV7670 Camera and Image sensor with an Arduino Nano 33 BLE to take pictures and convert them to.PNG format.

IntermediateProtip2 hours1,053
OV7670 Camera and Image Sensor with Nano 33 BLE

Things used in this project

Hardware components

Nano 33 BLE Sense
Arduino Nano 33 BLE Sense
×1
OV7670 Camera and Image Sensor
×1
Male/Female Jumper Wires
Male/Female Jumper Wires
×1

Software apps and online services

Arduino IDE
Arduino IDE
Python 3

Story

Read more

Schematics

Datasheet

Datasheet for OV7670

Code

Nano 33 BLE program to read OV7670 data

Arduino
Program to read image from the OV7670 camera and send the data over serial port to computer for further processing
/*
  OV767X - Camera Capture Raw Bytes

  This sketch reads a frame from the OmniVision OV7670 camera
  and writes the bytes to the Serial port.

  This sketch waits for the letter 'c' on the Serial Monitor,
  it then reads a frame from the OmniVision OV7670 camera and 
  prints the data to the Serial Monitor as a series of bytes.

  The website https://rawpixels.net - can be used the visualize the data:
    width: 176
    height: 144
    offset: 0
    Predefined Format: RGB565
    Pixel Format: RGBA
    Ignore Alpha checked
    Little Endian not checked

  Circuit:
    - Arduino Nano 33 BLE board
    - OV7670 camera module:
      - 3.3 connected to 3.3
      - GND connected GND
      - SIOC connected to A5
      - SIOD connected to A4
      - VSYNC connected to 8
      - HREF connected to A1
      - PCLK connected to A0
      - XCLK connected to 9
      - D7 connected to 4
      - D6 connected to 6
      - D5 connected to 5
      - D4 connected to 3
      - D3 connected to 2
      - D2 connected to 0 / RX
      - D1 connected to 1 / TX
      - D0 connected to 10

  This example code is in the public domain.
*/

#include <Arduino_OV767X.h>

long bytesPerFrame; // variable to hold total number of bytes in image
long numPixels;

// Declare a byte array to hold the raw pixels recieved from the OV7670
// Array size is set for QCIF; if other format requied, change size
// QCIF: 176x144 X 2 bytes per pixel (RGB565)
byte data[176 * 144 * 2]; 

void setup() {
  Serial.begin(115200);
  while (!Serial);

  // Begin the OV7670 specifing resoultion (QCIF, Pixel format RGB565 and frames per second)
  if (!Camera.begin(QCIF, RGB565, 1)) {
    Serial.println("Failed to initialize camera!");
    while (1);
  }
  
  bytesPerFrame = Camera.width() * Camera.height() * Camera.bytesPerPixel();
  numPixels = Camera.width() * Camera.height();

  // Optionally, enable the test pattern for testing
  // If the next line is uncommented, the OV7670 will output a test pattern
  //Camera.testPattern();
}

void loop() {
  // Wait for a 'c' from Serial port before taking frame
  if (Serial.read() == 'c') {
    
    // Read frame from OV7670 into byte array
    Camera.readFrame(data);

    // Write out each byte of the array to the serial port
    // Probaly a quicker way to do this
    for (int i = 0; i < bytesPerFrame; i++){
      Serial.write(data[i]);
    }

    // Write out a FF byte to tell receiving program that data is finished
    // Somewhat dangerous - but has worked so far
    delay(100);
    Serial.write(0xFF);
  }
}

Read Camera data from Nano 33 BLE via serial

Python
Python program to read image data from Nano 33 BLE over serial
import serial
import os

# Delete the existing camera file if it already exists
if os.path.exists("camera"):
  os.remove("camera")
  print("deleted file camera")
else:
  print("camera does not exist") 

# Open a raw file and set it up to receive camera data
image = open('camera',mode='wb')
stopChar = bytes.fromhex('ff')

# Open a serial port that is connected to an Arduino (below is Linux, Windows and Mac would be "COM4" or similar)
# No timeout specified; program will wait until all serial data is received from Arduino
# Port description will vary according to operating system. Linux will be in the form /dev/ttyXXXX
# Windows and MAC will be COMX. Use Arduino IDE to find out name 'Tools -> Port'
ser = serial.Serial('/dev/ttyACM0')
ser.flushInput()

# Write out a single character encoded in utf-8; this is defalt encoding for Arduino serial comms
# This character tells the Arduino to start sending data
ser.write(bytes('c', 'utf-8'))

# Loop through and read data received from camera
while True:
    #Read in data from Serial a byte at a time
    ser_byte = ser.read()
    #print(ser_byte)
        
    #If Arduino has sent a byte FF, exit loop
    if (ser_byte == stopChar):
         break
    
    #Write received data to file
    image.write(ser_byte)
            
# Close port and image file to exit
ser.close()
image.close()
print("image transfer complete")

Convert raw to RGB888 and then save as .png

Python
from PIL import Image
import os

# Delete the temp file 'imagefile' if it exists
if os.path.exists("imagefile"):
  os.remove("imagefile")
  print("Deleted temp image file")
else:
  print("Temp image file does not exist")

# Open file that contains the RGB565 raw data
image = open('camera',mode='rb')

# Open a temp file that will contain RGB888 raw data
result = open('imagefile', mode = 'wb')

# If using other picture dimensions, change here
Width = 176
Height = 144

# Binary masks used in RGB565 to RGB888 conversion 
MASK5 = 0b00011111
MASK6 = 0b00111111

print("Starting conversion")

# Loop through the raw file and convert to RGB888
# Note that the raw pixel data needs to be converted to interger
# in order for the bit shifting and masking to compute
# Result has to converted back
# This is done with commands .from_bytes and .to_bytes

for x in range (Width * Height):
  p = image.read(2) #read two bytes
  im = int.from_bytes(p, byteorder='big')
  #print(p)
  
  #Conversion of RGB565 to RGB888
  red = ((im >> 11) & MASK5) << 3
  green = ((im >> 5) & MASK6) << 2
  blue = (im >> 0 & MASK5) << 3

  r = red.to_bytes(1, 'big')
  g = green.to_bytes(1, 'big')
  b = blue.to_bytes(1, 'big')

  #print(red, green , blue)
  result.write(r)
  result.write(g)
  result.write(b)

result.close()
image.close()

rawData = open("imagefile", 'rb').read()

# the image size
imgSize = (Width,Height)

# create the image file ready for saving 
img = Image.frombytes('RGB', imgSize, rawData)

# can give any format you like .png, jpg etc.
img.save("finalpic.png")
print ("Finished conversion")

Credits

kpower

kpower

19 projects • 6 followers
Qualified Electrical Engineer with experience in software and hardware development

Comments