Time Lapse Video Project

I'm sure there are plenty of cameras out there that can do time lapse video out there, but the few that I tried always were hobbled in some sort of fashion.

Just for fun, I got one of those fancy COMedia C328R UART cameras to mess around with.

Sean Voisen made an Arduino C328R library which takes away a good deal of the pain of interfacing with the C328R camera (and believe me, there's a LOT of pain). I had to modify the library a bit to accommodate using the SerialBase class (so I can use either NewSoftSerial or HardwareSerial).

Storing data was easy - on a uMMC.

I used an rDuino LEDHead to control the whole smash. This will work on an Arduino Duemilanove or an Arduino Mega just as well, but you won't get time stamps on the files, unless you add an RTC. I used the rDuino LEDHead also because of the blinky lights for a nice progress bar.

I wanted to take pictures at a minimum rate of 1 every 30 seconds (or 2880 frames/day). Piece of cake.

Connections

Because the camera runs at 3.3V, you have to take care to change the signal levels from the Arduino to the camera, or else you may damage it.

Here is a schematic of the connections for the camera.

Camera Connection

Libraries

Here are the libraries I used:

Demonstration

Files are stored as jpeg images (named with a time stamp). After the sequence is done, you can assemble the jpeg images using your favorite video editor (that allows you to import a sequence of images).

Here is a sample time lapse video out the window. Nothing to write home about, but a good demonstration. The images from the camera are pretty low quality (you can see color quantization artifacts in the clouds).

The Adobe Flash Plugin is needed to display this content.

Source Code

Finally, here's the source code.

jpeg_cam_timelapse.pde
#include <rDuinoLEDs.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t
#include <RogueSD.h>
#include <CameraC328R.h>
#include <NewSoftSerial.h>
#include <avr/pgmspace.h>
 
char *Failed = "Failed!";
char *Done = "Done";
 
int8_t filehandle = 0;
uint32_t position = 0;
uint32_t segsize = 0;
uint8_t segcount = 0;
uint8_t progbar = 0;
 
#define debugSerial Serial
 
//NewSoftSerial ummc_s(2, 3);
NewSoftSerial cam_s(4, 5);
//NewSoftSerial db_s(6, 7);
//NewSoftSerial debugSerial(4, 5);
CameraC328R camera(cam_s);
RogueSD ummc1(Serial1);
 
 
void errorFlash(uint8_t l)
{
  while (1)
  {
    LEDS.set(1 << l);
//digitalWrite(13, 1);
    delay(300);
    LEDS.set(0);
//digitalWrite(13, 0);
    delay(300);
  }
}
 
 
void setup()
{
  int rtc[6];
 
  // input pins
  pinMode(8, INPUT);
  digitalWrite(8, HIGH);
 
  Serial1.begin(57600);
//  ummc_s.begin(57600);
  cam_s.begin(57600);
  debugSerial.begin(9600);
 
  // uMMC
  debugSerial.print("Syncing uMMC: ");
  if (ummc1.sync() == 0)
  {
    debugSerial.println(Done);
    LEDS.set(0, HIGH);
    // TODO: create directory if it doesn't exist
    // (open "/snaps/test.txt", F5 = /snaps doesn't exist)
  }
  else
  {
    if (ummc1.LastErrorCode == ERROR_CARD_NOT_INSERTED)
      debugSerial.println("No card inserted!");
    else
    {
      debugSerial.print(Failed);
      debugSerial.print(" Error Code: ");
      debugSerial.println(ummc1.LastErrorCode, HEX);
    }
 
    errorFlash(0);
  }
 
  // RTC
  debugSerial.print("Syncing RTC: ");
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  if (timeStatus() == timeSet)
  {
     LEDS.set(1, HIGH);  // this may not get set if RTC can not be synced
     debugSerial.println(Done);
     // now set the time on the uMMC
     rtc[0] = year();
     rtc[1] = month();
     rtc[2] = day();
     rtc[3] = hour();
     rtc[4] = minute();
     rtc[5] = second();
     ummc1.settime(rtc);
     debugSerial.println("uMMC RTC has been set");
  }
  else
  {
     debugSerial.println(Failed);
  }
 
  // Camera
  debugSerial.println("Camera:");
  debugSerial.print("  Sync: ");
  if(camera.sync())
  {
    LEDS.set(2, HIGH);
    debugSerial.println(Done);
  }
  else
  {
    debugSerial.println(Failed);
    errorFlash(2);
  }
 
  delay(200);
 
  debugSerial.print("  Init: ");
  if(camera.initial( CameraC328R::CT_JPEG, CameraC328R::PR_80x60, CameraC328R::JR_640x480 ))
  {
    LEDS.set(3, HIGH);
    debugSerial.println(Done);
  }
  else
  {
    debugSerial.println(Failed);
    errorFlash(3);
  }
 
  delay(200);
 
  debugSerial.print("  Set pSize: ");
  if(camera.setPackageSize(64))
  {
    LEDS.set(4, HIGH);
    debugSerial.println(Done);
  }
  else
  {
    debugSerial.println(Failed);
    errorFlash(4);
  }
 
  delay(200);
 
  debugSerial.print("  Set lFreq: ");
  if(camera.setLightFrequency(CameraC328R::FT_60Hz))
  {
    LEDS.set(5, HIGH);
    debugSerial.println(Done);
  }
  else
  {
    debugSerial.println(Failed);
    errorFlash(5);
  }
 
  debugSerial.println("Initialization complete");
  for (int i = 6; i < 8; i++)
  {
    LEDS.set(i, HIGH);
    delay(300);
  }
 
  delay(500);
  LEDS.set(0);
 
  randomSeed(analogRead(0));
 
  // now start time lapse
 
  Alarm.timerRepeat(30, takePicture);
 
}
 
 
void takePicture()
{
  bool res = true;
  char filename[60];
 
//  if (ummc1.sync() != 0)
//    debugSerial.println("uMMC Sync Failure");
 
  if (!camera.sync())
    debugSerial.println("Bad sync");
 
  if(!(res = camera.snapshot( CameraC328R::ST_COMPRESSED, 0 )))
    debugSerial.println("Bad Snapshot?");
 
  LEDS.set(0);
 
  if (res == true)
  {
    debugSerial.println("Snapshot ready");
    debugSerial.print("Opening file: ");
    sprintf_P(filename, PSTR("/snaps/IMG%04u%02u%02u%02u%02u%02u.JPG"), year(), month(), day(), hour(), minute(), second());
//    sprintf_P(filename, PSTR("/snaps/IMG%04u.JPG"), random(10000));
    debugSerial.println(filename);
 
    filehandle = ummc1.open(filename, OPEN_WRITE);
 
    if (filehandle > 0)
    {
      position = 0;
      segsize = 0;
      // get the picture
      if(!(res = camera.getJPEGPicture( CameraC328R::PT_JPEG, 1000, &getJPEGPicture_callback)))
        debugSerial.println("BAD JPG GET?");
      else
        debugSerial.println("Picture saved");
 
      ummc1.close(filehandle);
 
      LEDS.set(0);
    }
    else
    {
      debugSerial.print("  Error Code: ");
      debugSerial.println(ummc1.LastErrorCode, HEX);
    }
  }  
}
 
 
void loop()
{
  // now wait for keypress
  // then take a snap
 
  delay(500);
 
  // wait until the key has been released first
  while (digitalRead(8) == LOW)
    Alarm.delay(100);
 
  // now wait for a press
  while (digitalRead(8) == HIGH)
    Alarm.delay(100);
 
  takePicture();
}
 
 
void getJPEGPicture_callback( uint16_t pictureSize, uint16_t packageSize, uint16_t packageCount, byte* package )
{
  if (segsize == 0)
  {
    segsize = pictureSize / 8;
    segcount = 1;
    progbar = 1;
  }
 
  if(filehandle > 0 && package != NULL && packageSize <= 64)
  {
    position += packageSize;
    if (position > segcount * segsize)
    {
      segcount++;
      progbar = (progbar << 1) + 1;
    }
 
    LEDS.set(progbar);
//    debugSerial.print(position, DEC);
//    debugSerial.print('/');
//    debugSerial.println(pictureSize, DEC);
    ummc1.write(filehandle, packageSize, (const char *)package);
  }
  else
  {
    debugSerial.println("package is NULL!");
  }
}

Discussion

smokeyninja, 2010/04/17 01:52

Hey bhagman,

Awesome stuff here, but I was wondering if you could answer a troubleshooting question. I'm using a duemilanove – hopefully I will be able to incorporate an RTC as soon as I get this to function – and I am recieving an error when the file is being written at:

debugSerial.println(filename);

filehandle = ummc1.open(filename, OPEN_WRITE);

The error has a code of FF – I'm assuming F – or 6, and the errors are returned at each iteration of the timelapse. Also, I'm powering the ummc from the arduino – both the camera and the datalogger are feeding off th 3.3v line. The sd card is a 2gb SD formatted under FAT32. When you were putting this project together did you run into similar difficulties, and do you have any idea what might be causing this?

bhagman, 2010/04/17 03:37

While the uMMC will work at 3.3V because of the LDO regulator, it would be better if you powered it at 5V because of the amount of current that the SD card requires. This could be the cause of the errors as well.

Are you converting the levels from the Duemilanove? That is, the TX signal from the Duemilanove is 5V, and the camera needs to have that divided down to around 3V. The same applies if you are powering the uMMC at 3.3V.

You only need to convert the TX from the Arduino to the camera, so you can use a voltage divider to bring the level down.

I've added a picture above to show how the connections should be made to the camera.

b

smokeyninja, 2010/04/17 13:43

Thanks for the quick response and the image! I did not think to drop the voltage on the receive pin – psuedo-engineers (ie. BMEs) don't have this intrinsic knowledge.

I hope I didn't damage the camera, although now that the components are hooked up correctly my errors seem to be coming back as “4” or “6”, which indicates an unrecognized or improperly formatted command to the ummc.

Here's an excerpt from the serial monitor:

   Snapshot ready
   Opening file: /snaps/IMG3833.JPG
   F
   Error Code: 4

I used your commented out file naming code for troubleshooting purposes. Any advice would be greatly appreciated. Thanks again!

bhagman, 2010/04/17 16:07

What uMMC firmware version are you using?

smokeyninja, 2010/04/18 13:46

I just updated to 102.08.b004 from 101.56 this morning using the instructions in the examples section – no dice.

Do you know of any example sketches which I can use to quickly ascertain if the camera and ummc are working individually? Also, any other insight into the error code “4” would be greatly appreciated.

Thanks again.

smokeyninja, 2010/04/18 16:53

Also, my camera never synchronizes on the first “sync()” call – the c328 user manual states that this is usually the case. To circumvent this I simply looped the sync command until the thing cooperated. I ended up doing the same thing for all of the camera setup commands, although this may not be entirely necesary. The serial monitor prints out:

 Syncing uMMC: VSLSPZC
 Done
 Camera:
   Sync: Done
   Init: Done
   Set pSize: Done
   Set lFreq: Done
 Initialization complete

Before I get any errors, so hopefully nothing is damaged. I tested the ummc with one of the sample programs provided with the RogueSD library – it also seems to work fine.

bhagman, 2010/04/18 21:33

Yeah, the camera sync can be finicky. It seems to me that it really depends on which way the wind is blowing whether you'll get a good sync or not. Looping the there until you get a good sync is a good idea.

It looks like you have the uMMC sync'd and the camera sync'd. Did you get it to take a picture?

smokeyninja, 2010/04/18 22:53

The only indication that I've gotten of a snapshot being taken was the “snapshot ready” debugging message before the errors appear – nothing on the sd card. I don't quite know another way to test this guy out – I could try Sean Voisen's posting for the c328 library but I don't have a similar eeprom chip to the one he uses in the tutorial. Perhaps there is an easy to use sample code in one of these forums.

bhagman, 2010/04/19 00:19

Try to see if the uMMC is working by testing with the RogueSD Example Sketch: RogueSD_uMMC_Read_Write_Example.pde (found in the Arduino IDE under File→Examples→RogueSD). It will write a line and read back the file each time the Arduino is reset.

sweepstar, 2013/03/30 19:45

I noticed in the code you never set the baudrate of the camera, you set the baudrate for the camera to communicate through the cam_S

smokeyninja, 2010/04/19 10:03

Thanks for the suggestion. The read-write example works – I was able to create and add to a text file each time the micro-controller was reset.

You could leave a comment if you were logged in.
  • Bookmark at
  • Bookmark "Time Lapse Video Project" at del.icio.us
  • Bookmark "Time Lapse Video Project" at Digg
  • Bookmark "Time Lapse Video Project" at Google
  • Bookmark "Time Lapse Video Project" at StumbleUpon
  • Bookmark "Time Lapse Video Project" at Technorati
  • Bookmark "Time Lapse Video Project" at Facebook
  • Bookmark "Time Lapse Video Project" at Twitter
  • Bookmark "Time Lapse Video Project" at Slashdot
  • Bookmark "Time Lapse Video Project" at Yahoo! Bookmarks
  • Bookmark "Time Lapse Video Project" at Furl
  • Bookmark "Time Lapse Video Project" at Reddit
  • Bookmark "Time Lapse Video Project" at Ask
  • Bookmark "Time Lapse Video Project" at BlinkList
  • Bookmark "Time Lapse Video Project" at blogmarks
  • Bookmark "Time Lapse Video Project" at Ma.gnolia
  • Bookmark "Time Lapse Video Project" at Netscape
  • Bookmark "Time Lapse Video Project" at ppnow
  • Bookmark "Time Lapse Video Project" at Rojo
  • Bookmark "Time Lapse Video Project" at Shadows
  • Bookmark "Time Lapse Video Project" at Simpy
  • Bookmark "Time Lapse Video Project" at Socializer
  • Bookmark "Time Lapse Video Project" at Spurl
  • Bookmark "Time Lapse Video Project" at Tailrank
  • Bookmark "Time Lapse Video Project" at Live Bookmarks
  • Bookmark "Time Lapse Video Project" at Wists
  • Bookmark "Time Lapse Video Project" at Yahoo! Myweb
  • Bookmark "Time Lapse Video Project" at BobrDobr
  • Bookmark "Time Lapse Video Project" at Memori
  • Bookmark "Time Lapse Video Project" at Faves
  • Bookmark "Time Lapse Video Project" at Favorites
  • Bookmark "Time Lapse Video Project" at Newsvine
  • Bookmark "Time Lapse Video Project" at myAOL
  • Bookmark "Time Lapse Video Project" at Fark
  • Bookmark "Time Lapse Video Project" at RawSugar
  • Bookmark "Time Lapse Video Project" at LinkaGoGo
  • Bookmark "Time Lapse Video Project" at Mister Wong
  • Bookmark "Time Lapse Video Project" at Wink
  • Bookmark "Time Lapse Video Project" at BackFlip
  • Bookmark "Time Lapse Video Project" at Diigo
  • Bookmark "Time Lapse Video Project" at Segnalo
  • Bookmark "Time Lapse Video Project" at Netvouz
  • Bookmark "Time Lapse Video Project" at DropJack
  • Bookmark "Time Lapse Video Project" at Feed Me Links
  • Bookmark "Time Lapse Video Project" at funP
  • Bookmark "Time Lapse Video Project" at HEMiDEMi
code/time_lapse_video_project.txt · Last modified: 2010/04/28 15:45 by bhagman
Valid CSS Recent changes RSS feed Valid XHTML 1.0