Thursday, July 25, 2013

An Arduino Time-Lapse Camera Using ArduCam

While experimenting with my pinhole cameras, I did a lot of thinking about capturing and displaying the passage of time.  Day and night, the change of seasons, sunrise, sunset, moonrise, moonset, tides, rush hour, busy times at restaurants, even life and death marks the passage of time.

There are plenty of commercially-available time-lapse photography devices.  These require either an AC power source, a solar panel or a beefy battery, expensive photography equipment, and are large and easy to spot.  Between the size and the expense, this strongly limits the duration of time lapse I would be able to take even if such a device wasn't prone to being stolen by the first philistine that came across it.  My goal is to make the smallest, most energy efficient and least expensive camera possible so that I am free to set them everywhere and see what the passage of time shows me.

To do this, I decided to give ArduCam a try.

Actually, I found ArduCam pretty disappointing and difficult to use for a few reasons:
  • The open source libraries don't use any modern version control system like GitHub.  They're just a few files.  What's more, they fail to compile under Linux without changing the #include statements to correct for case-sensitivity.
  • While the ArduCam website shows full-resolution images pulled from "supported" camera modules, the ArduCam Rev. B shield is unable to take bitmap images greater than 320 by 240 pixels because it has only a 3 megabit buffer.  For cameras with on-board JPEG compression it is able to take higher-resolution images, but they come out looking fuzzier than a Georgia peach because they must be made to fit in the buffer.  If these images were indeed taken with an ArduCam shield, they weren't done with any code made available on their website.
  • The first "supported" camera I bought, the MT9D111, failed to take any worthwhile picture at any resolution.  I was only able to get it working with the OV2640 module.
Sample image taken with the "supported" MT9D111 camera using an example from the open-source ArduCam code.
So, the ArduCam was a bit of a let-down.  It is actually pretty impressive that someone figured out how to hack together a camera that interfaces with an Arduino, but it's not particularly useful.  The image size is pathetically small and the power draw is substantial (I'm able to take about 370 images on 4 AA batteries).  It may be useful for some applications, but quality time-lapse photography is not going to be one of them.

Let's take a look at my camera, my code, and the results.

My ArduCam

My camera uses the following components:
  • Arduino Uno Rev. 3
  • ArduCam Rev. B Shield
  • OV2640 Camera Module
  • 2GB MicroSD Card
  • 4 AA batteries
Assembly is positively trivial.  Everything just fits together.  Build time is under a minute.

Note that while the ArduCam board can be made to work with the Arduino Mega, it requires some rewiring because the pinouts are not exactly the same.  The power efficiency could have been improved somewhat with a DC step-down buck converter, but I decided against doing this because even if the power efficiency was greater the image resolution was unacceptable.

An image taken with the OV2640 out an office window at Google Seattle.

The first iteration of my code simply took a picture, then waited a minute using delay before taking another, repeating until the camera ran out of battery.  This managed to take 364 bitmap images until it ran out of juice.  The second iteration of my code made use of the Sleep_n0m1 library to place the Arduino in sleep mode and wake it up each minute.  I had hoped that this would improve battery life substantially, but instead it managed only to take 370 images before running out of battery.

I assembled the time-lapse photos into a video using the following command with avconv on Linux:
avconv -f image2 -i %08d.BMP output.avi


Overall, this was an enlightening exercise but it's clear that if I want a very long-term and reasonably high-quality time lapse photo solution, the platform for that is not going to be ArduCam.

25 comments:

  1. Hi Ben I have a question , can the arducam be used for object detection .
    For example , detecting a black ball on a white plane in less than 0.5 seconds.
    Or do I have to wait 10 seconds for each image i obtain?
    Thank You

    ReplyDelete
    Replies
    1. Yes, it could.

      If you look at my timelapse_2.ino code, the time it takes to grab the picture is on the order of 10ms. Reading from the FIFO is quite fast. The majority of the time spent taking a picture in my code is the time it takes to save it it to the SD card. If you were only scanning through the FIFO and somehow keeping track of the dark pixels versus the light pixels in memory instead of saving the picture, you would certainly be able to detect your ball faster than 0.5s.

      Delete
  2. Hi Ben,
    great job! I bought my stuff and tried to upload your sketch (timelapse_2.ino). However, I get the following when trying to upload it.

    timelapse_2.ino: In function 'void loop()':
    timelapse_2:117: error: 'class ArduCAM' has no member named 'enable_fifo'
    timelapse_2:134: error: 'class ArduCAM' has no member named 'disable_fifo'

    Can you help me out?Thanks.

    -Fil

    ReplyDelete
  3. Hi again,

    I got it... I've downloaded newest v3.0.0 (arducam http://www.arducam.com/download/) instead of v2.0. Also works with v2.1 but not with v1.3 (=>ArduCam Rev. B Shield).

    - Fil

    ReplyDelete
  4. hy

    i also bought a arducam with an lcd and now i am a bit angry about me that i didnt inform me more... the open source is not so open as i thought and i am used to arduino.

    as i can see rev.c. "only" needs "7" ports and spi+vcc+ground+reset

    so did you test acces the remaining I/O pins ?

    so far i read that pins: 9,19,A4,A5,6 from the spi port and the standart 5v and gnd io

    thx for answers

    ReplyDelete
    Replies
    1. I did not do any testing regarding which pins are actually used by the shield--but I'm looking and seeing that he's done two new releases of his code since I wrote this. I'll have to try again and see if it can actually capture high quality images now!

      Delete
    2. This comment has been removed by the author.

      Delete
    3. ok - cause that would be very interresting - it would make acces more easier than always adress a register and check with a mask ... etc. etc... ^^

      yep i´ve here revision "C" ...so in other words version 3.0.
      higher quality cams are available and the point you mentioned - low power - also included in the examples

      but for further question the producer of this product is not very communicative... sadly

      in particular i dont get the shield capture automatically i simply tried it like you just with delay and only comment out the query which checks the trigger register

      but if i do so for whatever reason it doesnt store the image... the trigger register with the capture done mask - so the capture done flag seems not to go on HIGH

      would be great if you see some other solutions for that - normally - simple taks ... :)

      Delete
    4. ok i just got it to save pictures but ... the result.... i just ad a manuall delay for 12s between start capture and store it, .. but as there is no further information about it it seems the fifo reading and saving is full of errors with that clumsy solution

      i got wrinkels and shifted images

      normally i thought a delay is not necessary but as the modul with a manual trigger also waits a few seconds i implemented that ... seems the right direction but also wrong in some kind ... :( :(

      i miss a datasheet like it normla for electronic parts (except cheap clones from china)

      so i realy look forward if you find the "master trick" :)

      Delete
    5. ugh i miss an "edit" function here:

      additional question:

      do you know how to manage the thumbnail for bmp ?
      this is also not correct it stores somewhere a very old thumbnail made with the shield and puts that on newer stored photos ...

      arducam is realy not an easy solution ^^

      Delete
    6. Hello

      I am attempting to capture photos at 5 minute intervals without using the shutter button. It is using jpeg output and it seems like anything above 1 minute intervals are not working. I wondered if you or anyone has a solution to my issue?

      Here is my code

      #include
      #include
      #include
      #include
      #include
      #if defined(__arm__)
      #include
      #endif

      #define SD_CS 9
      // set pin 10 as the slave select for SPI:
      const int slaveSelectPin = 10;

      ArduCAM myCAM(OV5642,10);
      UTFT myGLCD(slaveSelectPin);

      void setup()
      {
      #if defined (__AVR__)
      Wire.begin();
      #endif
      #if defined(__arm__)
      Wire1.begin();
      #endif
      Serial.begin(115200);
      Serial.println("hello");
      // set the slaveSelectPin as an output:
      pinMode(slaveSelectPin, OUTPUT);

      // initialize SPI:
      SPI.begin();
      myCAM.write_reg(ARDUCHIP_MODE, 0x00);


      myGLCD.InitLCD();
      myCAM.set_format(JPEG);
      myCAM.InitCAM();
      myCAM.write_reg(ARDUCHIP_TIM, VSYNC_LEVEL_MASK); //VSYNC is active HIGH

      //Initialize SD Card
      if (!SD.begin(SD_CS))
      {
      //while (1); //If failed, stop here
      }

      }

      void loop()
      {
      char str[8];
      File outFile;
      static int k = 0;
      static int n = 0;
      uint8_t temp,temp_last;

      //Flush the FIFO
      myCAM.flush_fifo();
      //Clear the capture done flag
      myCAM.clear_fifo_flag();
      //Start capture
      myCAM.start_capture();

      // Wait for the capture to finish.
      while (!(myCAM.read_reg(ARDUCHIP_TRIG) & CAP_DONE_MASK)) {
      delay(10);
      }

      //Construct a file name
      k = k + 1;
      itoa(k, str, 10);
      strcat(str,".jpg");
      //Open the new file
      outFile = SD.open(str,FILE_WRITE);
      if (! outFile)
      {
      return;
      }
      //Read JPEG data from FIFO
      while( (temp != 0xD9) | (temp_last != 0xFF) )
      {
      temp_last = temp;
      temp = myCAM.read_fifo();
      //Write image data to file
      outFile.write(temp);
      }
      //Close the file
      outFile.close();

      //Clear the capture done flag
      myCAM.clear_fifo_flag();

      delay(60000);
      }

      Delete
    7. Hi Jim--

      I'd expect it to work fine at longer intervals, because generally the limiting factor seems to be the transfer time between the ArduCAM and the SD card. You could try using my code from this post and see if that works for you.

      Just one suggestion. I don't think this is actually the problem, but try changing your delay there at the end to delay(65535) and see if it works. Then, change it to delay(65536) and see if it works. Even though delay supposedly takes an unsigned long, I wonder if there's a bug somewhere whereby argument's getting truncated to an unsigned int, which would limit you to a maximum of delay(65535) as the longest possible delay. If it works at 65535 and not 65536, it's certainly a bug in the Arduino libraries (or their documentation).

      ~Ben

      Delete
    8. Your code helped me get 1 minute intervals. I have tried 65535 and 65536 and it works for both. But It appears that the photos captured already do not overwrite and it is working for 5 minute intervals now. Thanks for your assistance.

      Delete
  5. hi ben
    i wanna ask if your code can be used for arduino mega? im using arducam rev c. with arduino mega 2560R3 for the same project as you done.. if the code can be used can you tell me which part that i need to change the code

    ReplyDelete
    Replies
    1. I seem to recall that there should be no code change, but the Arducam is not pin compatible with the Mega2560R3--you need to get a couple of wires and switch two pins for it to work. I think there are instructions on the Arducam website.

      Delete
  6. Hi Ben, can you help me to read JPEG Data from arducam to show on serial monitor?
    Can you show me how to print jpeg data?
    Thx Before

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Solved!
    I just print temp.
    Thx

    Anyway, I want to ask other now, can you tell me, how to get the pixel to 200x200?
    What should I do to change on the library?
    I using OV2640, & arducam Rev. C.
    Thx before

    ReplyDelete
  9. Or can you tell me how to take the original data from arducam? Not a JPEG and still have 3 byte from RGB.

    Thanks

    ReplyDelete
  10. I realise that this is an old thread, ubt I am looking for a solution to wirlessly connect multiple cameras to an Odroid. Is this even viable using Adruinos with WiFi ?
    Thanks in advance

    ReplyDelete
  11. Hi Ben,

    This error message came up after compiling the timelapse.ino program

    timelapse_1:25: error: no matching function for call to 'ArduCAM::ArduCAM(int, const uint8_t&, const uint8_t&, const uint8_t&, const uint8_t&, int)'
    timelapse_1.ino:25:36: note: candidates are:
    In file included from timelapse_1.ino:9:0:
    C:\Users\engineering\Documents\Arduino\libraries\ArduCAM/ArduCAM.h:267:3: note: ArduCAM::ArduCAM(byte, int)
    ArduCAM(byte model,int CS);
    ^
    C:\Users\engineering\Documents\Arduino\libraries\ArduCAM/ArduCAM.h:267:3: note: candidate expects 2 arguments, 6 provided
    C:\Users\engineering\Documents\Arduino\libraries\ArduCAM/ArduCAM.h:266:3: note: ArduCAM::ArduCAM()
    ArduCAM();
    ^
    C:\Users\engineering\Documents\Arduino\libraries\ArduCAM/ArduCAM.h:266:3: note: candidate expects 0 arguments, 6 provided
    C:\Users\engineering\Documents\Arduino\libraries\ArduCAM/ArduCAM.h:263:7: note: ArduCAM::ArduCAM(const ArduCAM&)
    class ArduCAM
    ^
    C:\Users\engineering\Documents\Arduino\libraries\ArduCAM/ArduCAM.h:263:7: note: candidate expects 1 argument, 6 provided
    timelapse_1.ino: In function 'void loop()':
    timelapse_1:114: error: 'class ArduCAM' has no member named 'enable_fifo'
    timelapse_1:131: error: 'class ArduCAM' has no member named 'disable_fifo'
    no matching function for call to 'ArduCAM::ArduCAM(int, const uint8_t&, const uint8_t&, const uint8_t&, const uint8_t&, int)'
    We are using this shield https://learn.sparkfun.com/tutorials/microsd-shield-and-sd-breakout-hookup-guide
    Where can we download avr/pgmspace.h?

    Thank you,

    Arduino Team EVC

    ReplyDelete
    Replies
    1. I'm not entirely sure what you're doing or why you think avr/pgmspace.h will help you. It looks to me like you're trying to call ArduCAM's constructor with the wrong number of arguments. Jim Hawkins has an example above where the correct number of arguments is used.

      Delete
    2. Hello, my name is Joseph Guzman and i am a team member in Arduino Team EVC. I would like to add on Alma's request. We are running the exact program you provided online which we are grateful for. We are not modifying any part of the program, simply compiling it and running into the same errors. The number of arguments are as follows:

      " ArduCAM myCAM(OV2640,A2,A1,A0,A3,10) " <- this is straight out of you code.

      Here is the list of our errors we are encountering:

      " no matching function for call to 'ArduCAM::ArduCAM(int,const uint8_t&, const uint8_t&, const8_t&, const8_t&

      timelapse_1:25: error: no matching function for call to 'ArduCAM::ArduCAM(int, const uint8_t&, const uint8_t&, const uint8_t&, const uint8_t&, int)'
      timelapse_1.ino:25:36: note: candidates are:
      In file included from timelapse_1.ino:9:0:
      C:\Users\engineering\Documents\Arduino\libraries\ArduCAM/ArduCAM.h:267:3: note: ArduCAM::ArduCAM(byte, int)
      ArduCAM(byte model,int CS);
      ^
      C:\Users\engineering\Documents\Arduino\libraries\ArduCAM/ArduCAM.h:267:3: note: candidate expects 2 arguments, 6 provided
      C:\Users\engineering\Documents\Arduino\libraries\ArduCAM/ArduCAM.h:266:3: note: ArduCAM::ArduCAM()
      ArduCAM();
      ^
      C:\Users\engineering\Documents\Arduino\libraries\ArduCAM/ArduCAM.h:266:3: note: candidate expects 0 arguments, 6 provided
      C:\Users\engineering\Documents\Arduino\libraries\ArduCAM/ArduCAM.h:263:7: note: ArduCAM::ArduCAM(const ArduCAM&)
      class ArduCAM
      ^
      C:\Users\engineering\Documents\Arduino\libraries\ArduCAM/ArduCAM.h:263:7: note: candidate expects 1 argument, 6 provided
      timelapse_1.ino: In function 'void loop()':
      timelapse_1:114: error: 'class ArduCAM' has no member named 'enable_fifo'
      timelapse_1:131: error: 'class ArduCAM' has no member named 'disable_fifo'
      no matching function for call to 'ArduCAM::ArduCAM(int, const uint8_t&, const uint8_t&, const uint8_t&, const uint8_t&, int)'
      "
      Please, any help you can provide will get us to complete our project by the end of this month and we've been stuck for the past 2 weeks. We deeply appreciate your help and look foward toyour reply.

      Evergreen Valley College Arduino team,

      - Joseph Guzman

      Delete
    3. As I said before, the problem is that you are calling the ArduCAM constructor with incorrect arguments.

      Apparently, since I wrote my code the ArduCAM libraries have changed so that the constructor takes 2 arguments, not 6 (as in my code). This means that if you are using my code as an example, you will have to find a more recent one as my code will no longer work.

      There is an example that someone else has posted in these comments above where they use the newer constructor. Please see Jim Hawkins' comment. He uses, for instance, ArduCAM myCAM(OV5642,10) to construct his ArduCAM object. This is with 100% certainty a source of your woes and is what you need to do also, except using arguments appropriate to your camera and slaveSelectPin.

      Delete
  12. Hi, Can I replace the arducam shield with "Arducam-LF Rev.C+ Camera module + 3.2 inch LCD for arduino UNO MEGA2560 DUE" to capture and display images and video.

    Thank you.

    ReplyDelete