Make Your Own Camera

2,165

3

39

About: 55+ years in electronics, computers, and teaching ... now retired.

This instructable explains how to make a monochrome camera using an Omnivision OV7670 image sensor, an Arduino microcontroller, a few jumper wires, and Processing 3 software.

Experimental software for obtaining a color image is also presented.

Press the “c” key to capture a 640*480 pixel image ... press the “s” key to save the image to file. Successive images are sequentially numbered should you wish to create a short time-lapse movie.

The camera is not fast (each scan takes 6.4 seconds) and is only suitable for use in fixed lighting.

The cost, excluding your Arduino and PC, is less than a cup of coffee.

Images

The component parts, without jumper wiring, are shown in the opening photo.

The second photo is a screen-shot showing the Arduino camera software and the Processing 3 frame-grabber. The inset shows how the camera is connected.

The video demonstrates the camera in action. When the “c” capture key is pressed there is a brief flash followed by a burst of activity as the image is scanned. The image automatically appears in the display window once the scan is complete. The images are then seen to appear in the Processing folder following each press of the “s” key. The video concludes by cycling rapidly through each of the three saved images.

Step 1: Circuit Diagram

The circuit diagram, for all versions of this camera, is shown in photo 1.

Photos 2, 3 show how the jumpers-wires and components are connected.

Without the aluminium bracket the images are lying on their side.

Warning

Program your Arduino BEFORE attaching any jumper wires to the OV7670 camera chip. This will prevent 5 volt output pins from a previous program from destroying the 3v3 volt OV7670 camera chip.

Step 2: Parts List

The following parts were obtained from https://www.aliexpress.com/

  • 1 only OV7670 300KP VGA Camera Module for arduino DIY KIT
  • 1 only camera bracket complete with nuts and bolts
  • 1 only UNO R3 for arduino MEGA328P 100% original ATMEGA16U2 with USB Cable

The following parts were obtained locally

  • 18 anly Arduino male-female jumper cables
  • 3 only Arduinin female-female jumper cables
  • 1 only mini bread-board
  • 4 only 4K7 ohm 1/2 watt resistors
  • 1 only scrap aluminium stand.

You will also need the following datasheets:

Step 3: Theory

OV7670 camera chip

The default output from the OV7670 camera chip comprises a YUV (4:2:2) video signal and 3 timing waveforms. Other output formats are possible by programming the internal registers via an I2C compatible bus.

The YUV (4:2:2) video signal (photo 1) is a continuous sequence of monochrome (black & white) pixels separated by U (blue color difference) and V (red color difference) color information.

This output format is known as YUV (4:2:2) since each group of 4 bytes contains 2 monochrome bytes and and 2 color bytes.

Monochrome

To obtain a monochrome image we must sample every second data byte.

An Arduino only has 2K of random access memory but each frame comprises 640*2*480 = 307,200 data bytes. Unless we add a frame-grabber to the OV7670 all data must sent to the PC line-by-line for processing.

There are two possibilities:

For each of 480 successive frames, we can capture one line to the Arduino at high speed before sending it to the PC at 1Mbps. Such an approach would see the OV7670 working at full speed but would take a long time (well over a minute).

The approach that I have taken is to slow the PCLK down to 8uS and send each sample as it comes. This approach is significantly faster (6.4 seconds).

Step 4: Design Notes

Compatibility

The OV7670 camera chip is a 3v3 volt device. The data sheet indicates that voltages above 3.5 volts will damage the chip.

To prevent your 5 volt Arduino from destroying the OV7670 camera chip:

  • The external clock (XCLK) signal from the Arduino must be reduced to a safe level by means of a voltage divider.
  • The internal Arduino I2C pull-up resistors to 5 volts must be disabled and replaced with external pull-up resistors to the 3v3 volt supply.
  • Program your Arduino BEFORE attaching any jumper-wires as some of the pins may still be programmed as an output from an earlier project !!! (I learnt this the hard way ... fortunately I bought two as they were so cheap).

External clock

The OV7670 camera chip requires an external clock in the frequency range 10Mhz to 24MHz.

The highest frequency we can generate from a 16MHz Arduino is 8MHz but this seems to work.

Serial link

It takes at least 10 uS (microseconds) to send 1 data byte across a 1Mbps (million bits per second) serial link . This time is made up as follows:

  • 8 data bits (8us)
  • 1 start-bit (1uS)
  • 1 stop-bit (1uS)

Internal clock

The internal pixel clock (PCLK) frequency within the OV7670 is set by bits[5:0] within register CLKRC (see photo 1). [1]

If we set bits[5:0] = B111111 = 63 and apply it to the above formula then:

  • F(internal clock) = F (input clock)/(Bit[5:0}+1)
  • = 8000000/(63+1)
  • = 125000 Hz or
  • = 8uS

Since we are only sampling every second data byte, a PCLK interval of 8uS results in a 16uS sample which is sufficient time to transmit 1 data byte (10uS) leaving 6uS for processing.

Frame rate

Each VGA video frame comprises 784*510 pixels (picture elements) of which 640*480 pixels are displayed. Since the YUV (4:2:2) output format has an average of 2 data bytes per pixel, each frame will take 784*2*510*8 uS = 6.4 seconds.

This camera is NOT fast !!!

Horizontal positioning

The image may be moved horizontally if we change the HSTART and HSTOP values while maintaining a 640 pixel difference.

When moving your image left, it is possible for your HSTOP value to be less than the HSTART value!

Don’t be alarmed ... it is all to do with counter overflows as explained in photo 2.

Registers

The OV7670 has 201 eight-bit registers for controlling things such as gain, white balance, and exposure.

One data byte only allows for 256 values in the range [0] to [255]. If we require more control then we must cascade several registers. Two bytes gives us 65536 possibilities ... three bytes give us 16,777,216.

The 16 bit AEC (Automatic Exposure Control) register shown in photo 3 is such an example and is created by combining portions of the following three registers.

  • AECHH[5:0] = AEC[15:10]
  • AECH[7:2 ] = AEC[9:2]
  • COM1[1:0] = AEC[1:0]

Be warned ... the register addresses are not grouped together !

Side effects

A slow frame rate introduces a number of unwanted side effects:

For correct exposure, the OV7670 expects to work at a frame rate of 30 fps (frames per second). Since each frame is taking 6.4 seconds the electronic shutter is open 180 times longer than normal which means all images will be over-exposed unless we alter some register values.

To prevent over-exposure I have set all of the AEC (auto exposure control) register bits to zero. Even so a neutral density filter is needed in front of the lens when the lighting is bright.

A long exposure also appears to affect the UV data. As I have yet to find register combinations that produce correct colours ... consider this to be work in progress.

Note

[1]

The formula shown in the data sheet (photo 1) is correct but the range only shows bits[4:0] ?

Step 5: Timing Waveforms

The note in the bottom left corner of the “VGA Frame Timing” diagram (photo 1) reads:

For YUV/RGB, tp = 2 x TPCLK

Figures 1, 2, & 3 verify the data sheet(s) and confirm that Omnivision treats every 2 data bytes as being the equivalent of 1 pixel.

The oscilloscope waveforms also verify that HREF remains LOW during the blanking intervals.

Fig.4 confirms that the XCLK output from the Arduino is 8MHz. The reason we see a sinewave, rather than a squarewave, is that all of the odd harmonics are invisible to my 20MHz sampling oscilloscope.

Step 6: Frame Grabber

The image sensor within an OV7670 camera chip comprises an array of 656*486 pixels of which a grid of 640*480 pixels are used for the photo.

The HSTART, HSTOP, HREF, and VSTRT, VSTOP, VREF register values are used to position the image over the sensor. If the image is not positioned correctly over the sensor you will see a black band over one or more edges as explained in the “Design Notes” section.

The OV7670 scans each line of the picture one pixel at a time starting from the top left corner until it reaches the bottom right pixel. The Arduino simply passes these pixels to the PC via the serial link as shown in photo 1.

The frame-grabbers’ task is to capture each of these 640*480=307200 pixels and display the contents in an “image” window

Processing 3 achieves this using the following four lines of code !!

Code line 1:

  • byte[] byteBuffer = new byte[maxBytes+1]; // where maxBytes=307200

The underlying code in this statement creates:

  • a 307201 byte array called “byteBuffer[307201]”
  • The extra byte is for a termination (linefeed) character.

Code line 2:

  • size(640,480);

The underlying code in this statement creates:

  • a variable called “width=640;”
  • a variable called “height=480”;
  • a 307200 pixel array called “pixels[307200]”
  • a 640*480 pixel “image” window in which the contents of pixels[] array are displayed. This “image” window is continuously refreshed at a frame rate of 60 fps.

Code line 3:

  • byteCount = myPort.readBytesUntil(lf, byteBuffer);

The underlying code in this statement:

  • buffers the incoming data locally until it sees a “lf” (linefeed) character.
  • after which it dumps the first 307200 bytes of local data into the byteBuffer[] array.
  • It also saves the number of bytes received (307201) into a variable called “byteCount”.

Code line 4:

  • pixels[i] = color(byteBuffer[i]);

When placed in a for-next-loop, the underlying code in this statement:

  • copies the contents of the “byteBuffer[]” array to the “pixels[]” array
  • the contents of which appear in the image window.

Key Strokes:

The frame-grabber recognises the following keystrokes:

  • ‘c’ = capture the image
  • ‘s’ = save the image to file.

Step 7: Software

Download and install each of the following software packages if not already installed:

  • “Arduino” from https://www.arduino.cc/en/main/software
  • “Java 8” from https://java.com/en/download/ [1]
  • "Processing 3” from https://processing.org/download/

Installing the Arduino sketch:

  • Remove all OV7670 jumper wires [2]
  • Connect a USB cable to your Arduino
  • Copy the contents of “OV7670_camera_mono_V2.ino“ (attached) into an Arduino “sketch” and save.
  • Upload the sketch to your Arduino.
  • Unplug the Arduino
  • You can now safely reconnect the OV7670 jumper wires
  • Reconnect the USB cable.

Installing and running the Processing sketch:

  • Copy the contents of “OV7670_camera_mono_V2.pde” (attached) into a Processing “sketch” and save.
  • Click the top-left “run” button ... a black image window will appear
  • Click the “black” image-window
  • Press the “c” key to capture an image. (approx 6.4 seconds).
  • Press the “s” key to save the image in your processing folder
  • Repeat steps 4 & 5
  • Click the “stop” button to exit the program.

Notes

[1]

Processing 3 requires Java 8

[2]

This is a “once only” safety step to avoid damaging your OV7670 camera chip.

Until the sketch “OV7670_camera_mono.ini” has been uploaded to your Arduino the internal pull-up resistors are connected to 5 volts, plus there is the possiblity that some of the Arduino data lines may be 5 volt outputs ... all of which are fatal to the 3v3 volt OV7670 camera chip.

Once the Arduino has been programmed there is no need to repeat this step and the register values may be safely changed.

Step 8: Obtaining a Color Image

The following software is purely experimental and is posted in the hope that some of the techniques will prove useful. The colors appear to be inverted ... I have yet to find the correct register settings. If you find a solution please post your results.

If we are to obtain a color image, all data bytes must be captured and the following formulas applied.

The OV7670 uses the following formulas to convert RGB (red, green, blue) color information into YUV (4:2:2): [1]

  • Y = 0.31*R + 0.59*G + 0.11*B
  • U = B – Y
  • V = R – Y
  • Cb = 0.563*(B-Y)
  • Cr = 0.713*(R-Y)

The following formulas may be used to convert YUV (4:2:2) back to RGB color: [2]

  • R = Y + 1.402* (Cr – 128)
  • G = Y – 0.344136*(Cb -128) – 0.714136*(Cr -128)
  • B = Y + 1.772*(Cb -128)

The attached software is simply an extension of the monochrome software:

  • A “c” capture request is sent to the Arduino
  • The Arduino sends the even numbered (monochrome) bytes to the PC
  • The PC saves these bytes into an array
  • The Arduino next sends the odd numbered (chroma) bytes to the PC.
  • These bytes are saved into a second array ... we now have the entire image.
  • The above formulas are now applied to each group of four UYVY data bytes.
  • The resulting color pixels are then placed in the “pixels[]” array
  • The PC scans the “pixels[]” array and an image appears in the “image” window.

The Processing 3 software briefly displays each scan and the final results:

  • Photo 1 shows the U & V chroma data from scan 1
  • Photo 2 shows the Y1 & Y2 luminance data from scan 2
  • Photo 3 shows the color image ... only one thing is wrong ... the bag should be green !!

I will post new code once I have solved this program ...

References:

[1]

http://www.haoyuelectronics.com/Attachment/OV7670%... (page 33)

[2]

https://en.wikipedia.org/wiki/YCbCr (JPEG conversion)

  Click here   to view my other instructables.

Share

    Recommendations

    • Pocket Sized Contest

      Pocket Sized Contest
    • Weaving Challenge

      Weaving Challenge
    • Pie Contest

      Pie Contest

    39 Discussions

    0
    None
    Varunsimha123

    22 days ago

    I have changed the PCLK pin from 8 to 12, looking at the comments above. Also changed B00000001 to B00010000 everywhere. I was getting some horizontal lines previously on a blank background and now I am getting something like this - a completely blank image. Please help.

    I have checked thee connections and verified them. My camera is fixed too. I don't understand what else might be the problem.

    image_0.jpg
    4 replies
    0
    None
    lingibVarunsimha123

    Reply 20 days ago

    The following tests will pinpoint the problem:

    In screenshots 1,2,3, the word "data" in the "line Serial.write(data);" has been replaced with a hexadecimal value. Replacing the word "data" with each of these hexadecimal values will produce a different shade of gray in the image window. If you see these changes then your software is working correctly.

    Restore the code line to read Serial.write(data);

    Now make the code changes shown in screen shots 4,5. Do these tests in a darkened room (i.e. away from bright light).

    The image in screenshot 4 is the darkest image possible.

    Writing a single 1 into any position in the top two lines shown in yellow high-light will brighten the image as shown in image 5.

    In test 4 the hexadecimal values in tests 1,2,3 have been substituted with your OV7670 data.

    In test 5 we have increased the camera gain.

    At some point you should get an image ... if not then your OV7670 data is suspect.

    light_grey (Copy).jpgmedium_grey (Copy).jpgblack (Copy).jpgimage (Copy).jpgbright_image (Copy).jpg
    0
    None
    Varunsimha123lingib

    Reply 16 days ago

    This is another image I got when I changed the code to:
    write_register(0x07, B10000000);
    write_register(0x10, B00000000);
    write_register(0x04, B00000000);

    This was a new camera. I uploaded the code to my new Arduino before connecting it, as per your guidelines. Please suggest what other changes could be made to make this work. I am trying this out since I wasn't able to afford to get a monochrome camera as they are very costly. I think this is the only way to complete me project. Please suggest something so that this whole thing works.

    image_0.jpg
    0
    None
    lingibVarunsimha123

    Reply 16 days ago

    Your frame-grabber is working as intended :)

    Tests 1,2,3 indicate that the frame-grabber is displaying an image after receiving 307200 (640*480) data bytes followed by a linefeed making 307201 bytes in total.

    The reason the screen exhibits a constant shade of gray in tests 1,2,3 is that we are deliberately sending the same data value.

    These tests also proves that you are receiving horizontal and vertical sync pulses from your OV7670.

    But tests 1,2,3 will not detect if a data byte is being dropped occasionally. The effect of a missed/dropped data byte would be to switch the camera output from monochrome to color and back producing horizontal lines.

    If you look at the images posted in the comments by skywalker58 you will see that his initial symptoms were similar to yours. His images then progress from slanted images with lines, to rectangular images without lines. Non-rectangular images indicate a loss of sync. In skywalker58's case it appears that instability was the cause.

    Comparing your images to those of skywalker58 I notice a diagonal line running downwards from the top left-hand corner of the display when there is an image problem.. This diagonal line indicates a loss of sync or lost data bytes.

    If there is any instability (tendency to oscillate) you will not get an image.

    Sorry I cannot be more helpful ... I will contact you if I think of anything.

    0
    None
    Varunsimha123lingib

    Reply 16 days ago

    HI! I tried all the above tests. Even changed my Arduino and got a new OV7670 camera. I got this image from test 5. Image from test 4 is no different. Have tried tests 1, 2 and 3 - they all worked as you have mentioned. I think the code is running fine. I think there is some other problem.

    image_0.jpg
    0
    None
    skywalker58

    Question 6 weeks ago

    Hi again.
    I take this pictures. When i touching my cables, image is almost perfect but i dont touch the cable, have side lines.
    I think, probably, my problem is cables.
    Last one, How can i do light setting? i want more brighter image.

    image_2 (2).jpgimage_6.jpg
    1 answer
    0
    None
    lingibskywalker58

    Answer 6 weeks ago

    -------------------------------------
    In the "OV7670_camera_mono_v2.ino"
    -------------------------------------
    Your image brightness is controlled by registers 0x07, 0x10, and 0x04
    You are allowed to write a '1' into any of the positions below that are shown with an 'x'
    write_register(0x07, Bxxxxxxxx);
    write_register(0x10, Bxxxxxxxx);
    write_register(0x04, B000000xx);

    Maximum brightness occurs with the following register settings:
    write_register(0x07, B11111111);
    write_register(0x10, B11111111);
    write_register(0x04, B00000011);

    Minimum brightness occurs with the following register settings:
    write_register(0x07, B00000000);
    write_register(0x10, B00000000);
    write_register(0x04, B00000000);

    Medium brightness occurs with the following register settings:
    write_register(0x07, B00000000);
    write_register(0x10, B00000001);
    write_register(0x04, B00000011);

    0
    None
    skywalker58

    Question 8 weeks ago

    ben changed all B00000001, now B00010000. İ connect 12.pin to pclk. still there is side lines. Now what can i do?

    fffffff.png
    4 answers
    0
    None
    lingibskywalker58

    Answer 8 weeks ago

    Originally you never got an image. With your help I traced this to:
    - incorrect wiring
    - a faulty 0V7670

    The fact that you are now getting an image with this second 0V7670 indicates that your circuit is working.

    But the cause of the horizontal bands is a mystery.

    These horizontal bands appear to contain faint image information (especially your second curtain photo) which suggests that the Arduino is missing an occasional PCLK signal.

    If this were to happen then you would see the UV color components of the image which, if you look at photo 1 in step 8 of this instructable, only contains faint image information.

    The image would return to normal if another PCLK signal was missed.

    To test this PCLK theory we have tried:
    - swapping the PCLK input to Arduino pin 12 in the off-chance that pin 8 was level sensitive
    - a faster method of sending data

    The only thing I can think of now is some form of instability which can be caused by
    - lead length
    - proximity of critical leads to another

    Can you please send a photo of your camera. I need to see your construction layout before I can make any further suggestions.

    0
    None
    lingibskywalker58

    Reply 8 weeks ago

    Thank you for posting a photo of your wiring layout.

    I notice that your camera module is not fixed which means that your wiring moves with each photo. It is possible that an a poor connection on your breadboard could cause instability and horizontal banding. Hand capacity may also contribute to your problem.

    If you Google "OV7670 image" you will find that other people have also experienced your problem. Two examples:

    (1)
    https://www.instructables.com/id/OV7670-Without-FI...
    Step 7: Troubleshooting and Pictures shows similar image problems and suggests "try switching the USB of the arduino from a USB2.0 to a USB3.0 (blue socket) or vice versa."

    (2)
    https://www.instructables.com/id/OV7670-Arduino-Ca...
    Step 11 trouble-shooting: also says "try switching the USB of the arduino from a USB2.0 to a USB3.0 (blue socket) or vice versa." and in the Comments section: someone says "I met the same problem and I think it dues to bad connections in the circuit. After soldering the circuit, it worked."

    Unfortunately I am unable to offer any further suggestions as I am out of ideas ... I will contact you if I think of anything else.

    0
    None
    skywalker58lingib

    Reply 8 weeks ago

    firstly, I will make immobilize my camera. After, i'll change my blue usb cable.
    i will try to connect my jumper cables different hole in breadboard...


    0
    None
    skywalker58

    Question 2 months ago

    hello again. i buy a new ov7670.
    i take this photos. how can i do take a good image with ov7670?
    only working in high light.

    image_0.jpgimage_1.jpg
    7 answers
    0
    None
    lingibskywalker58

    Answer 2 months ago

    An image at last ... well done :)

    The fact that you are now getting an image indicates that both your camera module and frame grabber are working.

    I am a little puzzled about your images as I have not encountered this problem.

    Both images have a slanting edge which indicates a timing / synchronising issue. In particular the lower part of your first image appears to "wrap-around" the screen.

    The "bands" in each image also indicate a loss of sync. I suspect that you are occasionally loosing a sync pulse and what you are seeing are bands of "UV" (color) data then "Y" (image) data as you regain sync. If this problem occurs at start up then it may resolve after running for a while (I'm thinking temperature).

    Go back over my previous suggestions ... in particular the answer in which I post six images.

    0
    None
    skywalker58lingib

    Reply 2 months ago

    i dont made it. Why dont happened, i am not understand. what is the problem this time? i make your says but I couldn't reach the result.

    0
    None
    lingibskywalker58

    Reply 2 months ago

    I can understand your frustration.

    Keep in mind that I cannot see what you are doing and have no way of knowing what you have tried in an attempt to debug your latest problem ... you are my eyes and ears. Sounding off does not help.

    Currently you have progressed from no image to a (slightly) distorted image.

    Together we isolated your initial lack of an image to a faulty OV7670.

    I am assuming that you have confirmed that the framegrabber is working by performing the tests that I have previously sent you. I am attaching the three screen shots again.

    Your framegrabber screen should change from light-gray, to gray, and then to black when you change the Serial.print(data); line in yellow-high light. This test is probably unnecessary as you are now getting an image. Restore the line in yellow-highlight to read Serial.print(data); when you have finished.

    The number 307201 should be displayed following each test.

    Assuming that the framegrabber tests okay you need to figure out why your data is getting corrupted.

    For this project to work you need relatively short leads as we are dealing with microsecond pulse widths. Long leads can distort pulses which could cause pulses to be missed which will result in a distorted image.

    If you look at my wiring the earth and supply leads are extremely short.

    black.jpggrey.jpglight_grey.jpg
    0
    None
    skywalker58lingib

    Reply 8 weeks ago

    i make your says (in picture). but not improve. even I can't take the photos I took formerly . i shortened cables.

    BEYAZ.pngbozuk.pnggri.png
    0
    None
    skywalker58skywalker58

    Reply 8 weeks ago

    i took a picture of the window now. Actually camera is working but have a problem. Why occur side lines ?

    image_0.jpg
    0
    None
    lingibskywalker58

    Reply 8 weeks ago

    If you study the photo in Step 3 of this instructable you will see how it is broken up into three sets of information.

    The desired "Y" information, which appears every second data position, produces a detailed image. This us why we sample the data every second PCLK.

    The unwanted "U" and "V" information only contains faint images. These signals, if displayed on a monochrome screen, would appear as faint monochrome outlines.

    If you now examine each of your three "window" images you can see faint outlines in each of the unwanted horizontal bands.

    It would appear that your Arduino is not detecting the occasional PCLK signal from your 0V7670 chip. If this were to happen then every second sample would become "U" then "V" information and the image would appear as faint monochrome outlines until another PCLK signal is missed at which point the desired image will reappear.

    This would also explain the slanted edges to each of your images as the frame-grabber is a dumb device and simply captures data until a linefeed symbol is received at which point it will display the data it has received.

    It would appear that:
    (1) your frame-grabber is working correctly
    (2) your 0V7670 camera chip is working.
    (3) your 5 volt Arduino is not reliably detecting the PCLK signals from the 3 volt 0V7670 CMOS camera chip

    Things to try:
    (1) Swap your existing Arduino board for another. Make certain you PROGRAM IT FIRST before attaching the OV7670 wires.

    (2) If you don't have a spare Arduino board then you could try mapping the PCLK signal to another unused Arduino pin ... some pins may be more sensitive. If you do this then you will need to change the binary patterns, and possibly ports numbers, in each of the code lines associated with the PCLK signal. Let me know if you need help with this.