Have you ever wanted to build your own wearable spy camera, UAV or other small, camera-enabled gizmo, gadget or device? While Arduino provides a wonderful prototyping platform for creating all sorts of DIY electronic gadgets, experimentations in physical computing, robotic artworks, and the like, it’s slim pickins’ when it comes to finding a tiny, easy-to-use digital camera to pair it up with. Fortunately, a company out of Hong Kong, COMedia Ltd., makes the C328R, a relatively small cell-phone style camera module that includes built-in JPEG image compression and UART serial communication. Couple the camera module with an Arduino and external storage (EEPROM, microSD, etc.) and you can have an instant Arduino-powered digital still image or video capture solution. That is to say, “instant,” once you have a working software driver … Fortunately for you, I’ve already done that work.
While working on a pigeon-based aerial photography solution as part of PigeonBlog for Beatriz da Costa, I wrote a library in C++ that allows the Arduino to communicate with the C328R camera over UART using the camera’s built-in communications protocol. Here’s a sample picture to prove that it works:

Sample C328R picture
The camera can take pictures in a variety of color depths (2- to 8-bit grayscale, 12- to 16-bit color, JPEG) and resolutions (80×64 to 640×480) with serial communication up to 115,200 baud. It’s fairly versatile and not terribly expensive (~$50) given the fact that it does a lot of work for you (i.e. on-board JPEG compression).
The rest of this post assumes that you have the camera and an Arduino Duemilanove (or similar) in hand. If you don’t, I suggest that you get them. You’re also going to need some type of external storage for the pictures, because the Arduino doesn’t provide any sufficient storage on-board. The sample code that follows assumes a 256KB Atmel SPI serial EEPROM or similar SPI EEPROM.
How to Use the Camera
Using the C328R camera involves a fairly straightforward issuance of commands. After power on, you essentially just have to synchronize with the camera, tell it what size and color-depth you want your pictures to be, and optionally set the byte size of the data packets it will send back to you (default 64 bytes), light frequency to use, etc. It looks something like this, assuming you want a JPEG photo:
- Power on camera.
- Issue “sync” command.
- Issue “initial” command (tell the camera the desired color-depth, resolution, etc.).
- Wait about 2 seconds for the camera to settle.
- Issue “snapshot” command. This will tell the camera to take a snapshot and store it in its internal memory.
- Issue “getPicture” command. The camera will then begin sending the JPEG image over serial in a series of data packets (default 64 bytes).
- Take each data packet and store its image data sequentially in the EEPROM, until all image data has been retrieved. (A 640×480 JPEG can be upwards of 20KB. At 64 bytes a packet, this would be 320 individual data packets per photo.)
All of these commands are issued over a proprietary serial communications protocol. The C328R Arduino library takes care of all of this behind the scenes, and reduces the complexity to nothing more than a series of function calls.
Download the Library
You can download the camera library here. Standard Arduino library installation instructions apply.
Making the Connections
The camera should be powered at 3.3V, with the TX (yellow wire) connected to the RX pin on the Arduino, and the RX (green wire) connected to the TX pin on the Arduino. The wiring that comes with the camera is thin and stranded, so you may want to solder on some headers for a more robust electrical connection, especially if you plan on using a breadboard.
The SPI EEPROM, assuming you’re using an Atmel SPI EEPROM or another SPI EEPROM with the same pin layout, should be connected per the instructions found here.
An LED, with appropriate resistor, should be connected to pin 8. This will be used as an indicator LED to let you know when the Arduino is done writing the picture to the EEPROM.
Sample Code and Application
The following sample code operates in two parts: The first part captures a picture with the camera, stores it on an external EEPROM at address 0, then powers off the camera. The second part sends the JPEG image data from the EEPROM to the computer over USB, allowing a simple program written in Processing to read the data and save it to disk for viewing.
IMPORTANT NOTE: This sample code is designed primarily for testing and debugging purposes. Because the Arduino has only one hardware serial port, and the camera and computer (via USB) are both connected to that port, we use the indicator LED to notify us when the Arduino is done saving the data to the EEPROM and has powered off the camera. This way we know that any subsequent data being sent over serial is picture data that should be captured on the computer and saved to disk. By default, the code gives us 5 seconds to MANUALLY tell the Processing sketch to begin listening for and saving the serial data. You do this simply by pressing any key on the keyboard when the Processing sketch is running. After it is done reading the data, press any key again to write it to disk.
First up, the Arduino code. You will need to have the SPI library already installed. Save this as a sketch and upload it to your Arduino:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | #include "CameraC328R.h" #include "Spi.h" #define LED_PIN 8 #define PAGE_SIZE 64 #define BAUD 38400 // EEPROM Opcodes #define WREN 6 #define WRDI 4 #define RDSR 5 #define WRSR 1 #define READ 3 #define WRITE 2 // Buffer for EEPROM data byte pageBuffer[PAGE_SIZE]; CameraC328R camera; static uint16_t pictureSizeCount = 0; static uint16_t pageBufferIndex = 0; static uint16_t currentAddress = 0; bool dirtyBuffer = false; /** * Reads one byte from the EEPROM at the given address. */ byte readByte( uint16_t address ) { byte data; digitalWrite( SS_PIN, LOW ); Spi.transfer( READ ); Spi.transfer( (byte)(address >> 8) ); Spi.transfer( (byte)(address) ); data = Spi.transfer( 0xFF ); digitalWrite( SS_PIN, HIGH ); return data; } /** * Writes the data in the buffer to the EEPROM at the page * starting at the given address. */ void writeBuffer( uint16_t address, uint16_t bufferSize ) { // Write enable digitalWrite( SS_PIN, LOW ); Spi.transfer( WREN ); digitalWrite( SS_PIN, HIGH ); delay( 10 ); // Begin write digitalWrite( SS_PIN, LOW ); Spi.transfer( WRITE ); // Send the address Spi.transfer( (byte)(address>>8) ); // MSB Spi.transfer( (byte)(address) ); // LSB // Send the data for( uint16_t i = 0; i < bufferSize; i++ ) { Spi.transfer( pageBuffer[i] ); } digitalWrite( SS_PIN, HIGH ); } /** * Fills the page buffer for the EEPROM with data. */ void fillPageBuffer( byte* data, uint16_t dataSize ) { for( uint16_t i = 0; i < dataSize; i++ ) { pageBuffer[pageBufferIndex] = data[i]; dirtyBuffer = true; pageBufferIndex++; if( pageBufferIndex == PAGE_SIZE && dirtyBuffer ) { pageBufferIndex = 0; writeBuffer( currentAddress, PAGE_SIZE ); currentAddress += PAGE_SIZE; dirtyBuffer = false; delay( 50 ); } } } /** * Sends a picture to the computer. */ void transferPicture( uint16_t startAddress, uint16_t size ) { uint16_t endAddress = startAddress + size; for( uint16_t i = startAddress; i < endAddress; i++ ) { Serial.print( readByte( i ), BYTE ); } } /** * This callback is called EVERY time a JPEG data packet is received. */ void getJPEGPicture_callback( uint16_t pictureSize, uint16_t packageSize, uint16_t packageCount, byte* package ) { // packageSize is the size of the picture part of the package pictureSizeCount += packageSize; // package contains everything in the package fillPageBuffer( package, packageSize ); if( pictureSizeCount == pictureSize ) { // Is there still stuff in the buffer? if( dirtyBuffer ) { writeBuffer( currentAddress, pageBufferIndex ); } camera.powerOff(); digitalWrite( LED_PIN, HIGH ); // DONE! Serial.flush(); delay( 5000 ); // Give us 5 seconds to hit a key ... transferPicture( 0, pictureSize ); } } void setup() { Serial.begin( BAUD ); digitalWrite( SS_PIN, HIGH ); // disable device pinMode( LED_PIN, OUTPUT ); if( !camera.sync() ) { Serial.println( "Sync failed." ); return; } if( !camera.initial( CameraC328R::CT_JPEG, CameraC328R::PR_160x120, CameraC328R::JR_640x480 ) ) { Serial.println( "Initial failed." ); return; } if( !camera.setPackageSize( 64 ) ) { Serial.println( "Package size failed." ); return; } if( !camera.setLightFrequency( CameraC328R::FT_50Hz ) ) { Serial.println( "Light frequency failed." ); return; } // Let camera settle, per manual delay(2000); if( !camera.snapshot( CameraC328R::ST_COMPRESSED, 0 ) ) { Serial.println( "Snapshot failed." ); return; } if( !camera.getJPEGPicture( CameraC328R::PT_JPEG, PROCESS_DELAY, &getJPEGPicture_callback ) ) { Serial.println( "Get JPEG failed." ); return; } } void loop() { } |
Next up, the Processing code. This code doubles as a serial monitor, so you can run it as soon as you plug in the Arduino to USB, and ignore the built-in Arduino serial monitor. Again, note that you must hit a key within 5 seconds of the LED lighting up, and again once data is done reading to save it to disk. By default, it will be saved as a file called “photo.jpg” in the same folder as your Processing sketch.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | import processing.serial.*; Serial myPort; String filename = "photo.jpg"; byte[] photo = {}; Boolean readData = false; void setup() { println( Serial.list() ); myPort = new Serial( this, Serial.list()[0], 38400 ); } void draw() { byte[] buffer = new byte[64]; if( readData ) { while( myPort.available() > 0 ) { int readBytes = myPort.readBytes( buffer ); print( "Read " ); print( readBytes ); println( " bytes ..." ); for( int i = 0; i < readBytes; i++ ) { photo = append( photo, buffer[i] ); } } } else { while( myPort.available() > 0 ) { print( "COM Data: " ); println( myPort.readString() ); } } } void keyPressed() { if( photo.length > 0 ) { readData = false; print( "Writing to disk " ); print( photo.length ); println( " bytes ..." ); saveBytes( filename, photo ); println( "DONE!" ); } else { readData = true; myPort.clear(); println( "Waiting for data ..." ); } } |
That should be enough to get started. The sample code above does not cover using the camera for RAW image capture, which is considerably faster and more useful as a solution for video (5 frames per second), but the library includes this functionality.
Feel free to post questions, comments or ideas below.
Tags: arduino, c++, camera, photography, processing
-
A great addition to the Arduino world, I guess we can stop saying that it’s not possible to take photos with an Arduino now.
BTW did you mean to say “bytes” in the sentence “over serial in a series of data packets (default 64KB)” as it seems to contradict the next point.
Thanks for your work.
–Phil.
-
Hey,
my camera does not even sync. I have put the response time to be 500 and 60 mx sync attempts, but I get a fail in sync, in less than 5 seconds, instead of 30! Why is that happening?
Thanks,
Anthony -
Very wonderful tutor! Would it be possible to quote it in our C328 module product page?
http://www.seeedstudio.com/depot/uart-camera-module-with-jpeg-compression-c328-p-209.html
-
Sean, The link above goes to a camera without an R appended, do you think your library will work with it? Also, the C328R is out of stock from your link so do you think the C328-2225BW will work?
Thanks in advance for your advice.
-
Thanks for the camera library! I’m going to use it for a little project.
I’m only slightly confused about Processing’s Serial library. On their website it’s stated the library is used for RS-232 standard. Did you use a USB Serial Adaptor to connect your Arduino to PC? -
Hi, Sean:
Thanks for clearing it up for me. I haven’t purchased Duemilanove yet, I have an older version of Arduino that I bought 3 years ago. Right now I’m doing research on what parts I should get. And on Processing’s website, this is what is written about the Serial library:
“The Processing serial library allows for easily reading and writing data to and from external machines. It allows two computers to send and receive data and gives you the flexibility to communicate with custom microcontroller devices, using them as the input or output to Processing programs.
The serial port is a nine pin I/O port that exists on many PCs and can be emulated through USB with the Keyspan USB Serial Adaptor and other compliant devices.”
I originally thought that this library is only for serial connection which uses the R232 standard, and I would need a USB Serial adaptor for Duemilanove. But I guess there’s some confusion somewhere, Processing’s Serial library handles USB connections as well.
Thanks again!
-
Correction: It should be RS-232 standard, not R232 as I mentioned.
Sorry about that! -
Thanks for the informative post Sean, you’ve given me a leg up on my project
I had a somewhat odd question about the raw image capture; you load in the bytes in a while() loop polling Serial.available(), and I’m wondering what the consequence would be if you stuck some in-line computations between each byte read, say around line 510 in CameraC328R::waitForResponse.
Obviously, on a camera with unbuffered output you’d lose data, but since this has an onboard buffer I thought that perhaps there might be a way to do this? Or does the delivery as a single package preclude “waiting” for computations to finish before loading more of the raw data?
Sorry if this is a silly question, but I couldn’t sort it out from the data sheet and I haven’t yet received my camera (otherwise I would just test it myself
.Thanks again!
-
Thanks Sean I’ll let you know if I accomplish anything useful
-
Sean thank you thank you thank you…
I am trying to get geared and brained up for a robotics project I have had on the shelf for years. I have recently learned about the complexity of sending digital video wirelessly, that 99% of cameras out there are analog and need some kind of board to decode them before sending them digitally. This camera gave me some hope, but the frame rate listed in the documentation for “preview” mode was pretty abysmal (.75 fps).
Your claim of 5fps and a mention of an altogether new lower res to operate at (fine by me) is just what I am looking for. I will be taking apart your code for the next month or so once I acquire this little device!
You are the man, hooray for smart people!
-
Why does the data need to go via the SPI eeprom? can’t you just send the packets from the camera directly up to the computer? or is there timing issues?
-
Yes exactly, I wasn’t planning on keeping attached to a computer but rather stream it to the net with the ethernet shield board, an eeprom could be used as a little storage buffer, but then the camera has its own storage eeprom doesn’t it? There is a nice web server library that has been developed called Webuino that would act as the frame work for displaying the captured image. Have you had a look down this path of displaying the image over a network connection at all?
-
Very Cool Stuff Almost Exactly What im Looking for… but what if instead of using a Atmel SPI EEPROM i used an sd/mmc card would the data thats written on the card be readable by pc via sd card reader? instead of turning off the camera to enable a serial connection to the pc one could just remove the sd card and view the photos via pc thats the idea at least but I’m sorta new at all this so if I’m talking crazy just let me know. o and one more thing whats the possibility of storing multiple images on said sd card?
thanks.
-
Thanks for the insight. Thats a great idea. ill look into it all and if i figure anything out ill post it here.
Thanks, Pat.
-
Hi,
Do you think that the Philips DC-3840 (OV528) works with your driver? or with a adaptation? The protocol looks similar ?! Because the old Handy cam kost only 9 euros in germany, not 50.Thanks great work.
-
This ist the command list from http://www.ulrichradig.de/
-
Hey Sean:
Great article and excellent links. You’ve given me enough inspiration to attempt to build something similar to your project. Camera and boards are on the way. Want to try an XBee interface once I get your example working.
Just got some sample SPI EEPROMs from ST Micro – next day service – very impressive.
Do you anticipate any problems with ST EEPROM – or will it be a straight swap for the Atmel?
I have
1. M95256-WMN6TP (256K Serial SPI SO-8)
2. M95256-RMN6TP (256K Serial SPI SO-8)
3. M95512-RMN6TP (512K Serial SPI SO-8)
4. M95M01-RMN6TP (1MBIT Serial SPI SO-8)Not sure what the difference is between WMN and RMN – possibly clock speed.
Will keep you posted – let me know if you have any suggestions to make this a success.
Thanks again
Don -
Hey Sean:
Just an update for your other readers – anyone else that is a newbie like me and is going to order a sample or product directly from ST or Microchip to use with a breadboard, you need to order the “Plastic Dual In-Line” packaging type (PDIP). The ones I ordered are too narrow and have shorter legs than what’s needed to fit across the center breadboard channel.
Microchip product is 25AA256-I/P
I couldn’t seem to find any SPI EEPROMs on ST’s site that had a PDIP body
Hope that helps others…
My new ones should be here in a week. Testing will resume then.
Don
-
Pat:
While looking thru Arduino forums came across a couple of interesting topics on SD cards and the Arduino.
This one specifically looks to be what you want – note that he uses Sean’s library extensively.
Appropriately entitled “arduino + camera + sd memory card”
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1253595882/ -
Hi Sean,
I am working with the same camera which I purchased from Sparkfun although I am having a few problems with it see here
http://forum.sparkfun.com/viewtopic.php?t=17119&sid=0061e0c804b7ddde9a11d5f0285d2f79I was just wondering if you had tried to receive an uncompressed RGB image from the camera as they specify on page 10 of C328_UM.pdf (user manual).
It would be nice to know if this is an issue with the camera or just my code

it seems that everyone is basking in JPEG compression at the moment so maybe this quirk is going unnoticed!Thanks mate!
Regards,
Murray
-
If raw mode is so much faster, why not skip the OV328 entirely & just interface the microcontroller to the image sensor?
-
Hi,
Great tutorial!
However, i am a little bit confuse. Could be the C328 camera works with arduino 2009? It seems that the camera require 60mA, meanwhile the pins in arduino 2009 only have 40mA on exit…
So, is it neccesary any special component between the arduino 2009 and the C328R camera?
Tak, tak!
-
Hi Sean,
Great to see that you’ve update the library Smiley.
I just got mu C328 from Sparkfun and I am about to hook it up but:
Is it also possible to use the library without the EEPROM (why do you use the EEPROM?)?
The plan is to take a snapshot and directly send it to my PC instead of first save it to an EEPROM. The C328 will be connected onto my Arduino Mega board.
-
hi. thx for sharing that great idea.
i tried arms22 way of connecting the camera through serial port.
i have the same components but a lot of problems to get my camera synced. everytime i try ill get sync failed.
since the the c328r operates on 3.3V and the arduino duemilanove has TX/RX (in this case pin 2 & 3) output of 5V I added a 3.3V regulator for the RX connection (pin 3).
here is a link to my setup: http://bit.ly/7ppElz
ok, maybe one of you guys has any idea why my camera is not getting synced…
thx!
-
hey i tried what arms22 did but i a getting the error “photo.jpg containes bad image data.
can any one help?
-
Hello, im interested in doing an arduino project using a camera. Can you tell me if your library is compatible with a similar camera from Comedia, the C328-7640 or similar models.
-
I have the same question as cyril. Is it compatible?
-
@elli: You need to get a logic level converter. I found one at Sparkfun that works from converting a 5v to 3.3. Here is the link http://www.sparkfun.com/commerce/product_info.php?products_id=8745
@Sean: Thanks for fixing the site. I was getting worried I couldn’t go back to this bookmark hehe. I have a question, I’m going to use matlab to process the image and send two bytes(x,y coordinates) back to the Arduino. Is there a way in processing or Arduino to communicate with a program like matlab? I know its strange but I have an algorithm to detect the hands and I want to send the data to the Arduino to display on a 16×16 dot matrix. I have everything working separately but I just need to make them communicate. Any help is welcome! Thanks again for the awesome site!!
-
Is it really necessary to use 3.3V TX/RX pins? Or does the C328 only need 3.3 V as power?
-
What version of processing were you using?
-
Hi,
I am trying to implement the exact same thing except I need the img packets to be transferred wirelessly immediately as there is no SD card involved. The project is very weight sensitive and I cannot add an SD card for now. If you can pls take a look at my code and help me figure out how to code the transfer, I would appreciate it very much!
I cant find you email anywhere on the site and so this public plea. Please get back to me asap.
P.S.: Happy Easter!
-
@JR
I’ve used the camera with 5V for TX/RX and it seemed to work fine. I don’t know if this can somewhat damage it tho. I’m a electronic noob
But I’m currently using a simple voltage divider to provide 3.3 V for TX/RX and think this is somewhat safer!?
It uses three 1.1k resistors for this and I connect them this way:
TX (arduino) -> R -> RX (C328) -> R -> R -> GND
TX (C328) -> RX (arduino)with R being 1.1k resistors.
This works fine and I now get on RX and TX at the camera about 3.3 V and the camera still works ;D
-
Thanks Sean and Void256 for the advice. I will devide the arduino TxD to 3.3 V using your suggestion (http://en.wikipedia.org/wiki/Voltage_divider).
Just a noob question: would this voltage devider be the best solution (If I move to PCB)? Or is there a better more common solution?
@Sean
Perhaps you can add this voltage devider to you tutorial, because I think many people (like me) hook up the power to 3.3V but forget to devide the voltage of the arduino’s TxD to 3.3V.Cheers.
64 comments
Comments feed for this article
Trackback link: http://gizmologi.st/2009/04/taking-pictures-with-arduino/trackback/