Project tutorial
Arduino HackHD Time-Lapse Dolly

Arduino HackHD Time-Lapse Dolly

Watch in high-definition as several hours or even an entire day fly by in seconds. Get lost in the dream as the camera pans & rotates.

  • 17 respects

Components and supplies

About this project


This project was inspired by someone else who made an Arduino time-lapse dolly (surprise surprise). However that project was published some years back and I have lost the link to it. Of course time-lapse is not that exciting anymore but 5 years ago it was. The thing about this specific project that really got my attention was the fact that the camera not only could slide horizontally but could rotate as well, giving the time-lapse this other-worldly effect. I decided I had to make my own.

The problem was that this project I was trying to replicate used a custom-built rig, custom-made PCBs and an expensive DSL camera. Being a fan of cheap gear, that wasn't gonna fly with me. And since the project's creator was not very helpful (I don't think he was much of an open-source kinda guy) I set out to build it from scratch... and cheaply!

I'd tinkered around with Arduino for some time, but this was a whole 'nother level. I was going to have to really step up my game. But I don't think I could have done it without a mentor who happened to live in my neighbourhood - he was a real electronics whiz. I would have been stuck in stage zero forever had he not pushed me to take that first step. And that first step was eBay!

Yes, that's right... eBay. I didn't need a genius to tell me that, but that's what happened. See I'd been caught up trying to be a designer, spending hours thinking about how my invention ideally should look and function. I was stuck in the idea/dream phase. But really that's pointless. Unless you're James Cameron who can just invent the things he wants with his bazillion-dollar budget, start looking at what is actually available on the market and go from there.

Part 1

So I started with the most expensive item - the camera! It needed to be Arduino-compatible and able to take a decent quality picture. I thought about using my iPhone camera and somehow triggering it using the audio jack, but that was too complicated. There were some Arduino cameras on the market but the image quality just wasn't there. Eventually I came across the HackHD. A GoPro-quality camera with GPIO pins. Brilliant. Add to cart. Oh and don't forget a class-10 SD card with plenty of storage for those high-def images. There goes a few hundred bucks. Then add to that a 3D-printed case which cost way more than it should have since all the measurements in the STL files had to be converted from imperial to metric (thanks America). Also 3D printers were still pretty new back in the early '10s, and any service costs a fortune in Australia.

Next on the list - 2 stepper motors. One for translation (i.e. horizontal movement) and one for rotation. The 28BYJ-48 provides a surprising amount of grunt for only a couple bucks. And it was a lot of fun learning how steppers work. You have to activate the 4 pins in a sort of leap-frogging sequence - 1000 1100 0100 0110 etc - and iterating through to get it to turn. The Arduino I used had a built-in LED on each pin which was perfect for visualizing the stepping process. Now the rotation part was pretty straightforward, but what about translation? Again my mentor proved mighty helpful by sending me in search of a pulley, which you can see is that metallic disc thing in the photo.

Then I needed a keypad and LCD so I could operate the device without a laptop. This was a pretty awesome crash course in basic UI; taking numerical inputs from the user, checking they are valid, storing them in memory, and if a mistake was made allow the user to start again. The 3 inputs I needed were:

  • total duration in hours that the device would be in operation (I managed to get up to 12 hours max with the power bank I used)
  • seconds between each image capture (the HackHD needed a minimum of 15 seconds to take a picture and save it to the SD card)
  • total amount of rotation in degrees
  • a final YES/NO confirmation before starting to take photos. If no, clear the inputs and start again.

And although not necessary it was a nice feature to also have the LCD display how far it had progressed as a percentage and how many hours were left once the photography had begun.

Part 2

In some freakishly magical way, I used every single digital AND analog pin on the Arduino (except D0 which is reserved for serial RX). This wasn't planned, it just conveniently turned out that way. Unfortunately I did not make a diagram as I was not really into documentation back then. But here's the pinout:

  • D1: Serial output to LCD display (Couldn't receive data back from LCD as it only had 3 pins; VCC, RX, & GND)
  • D2-D8: Keypad input (a hell of a lot of pins I know)
  • D9-12: Translation stepper
  • D13: LCD power supply (I needed to be able to reset the display hence couldn't use the regular 5V pin)
  • D14-17: Rotation stepper
  • D18,19: Transmit/receive signals to/from camera

Keep in mind the analog pins can be used as digital pins, which are D14-19.

Next up was the camera rig and dolly. This was the most fun as I got to go an op-shop (or a thrift shop in America) to dig for treasure. Something long and track-like was in my mind. Lo and behold, I chanced upon a CD rack about a metre long. Strip away the CD holders and you've got yourself a track. Now what about a chassis to actually hold the camera rig? It turns out that a tissue box cover slotted almost perfectly into the CD rack. Who actually uses a tissue box cover, I don't know. I suppose that's why they donated it to charity. I think these items cost no more than $5 all up.

Add a gorilla tripod to each end, slap a wooden block with a couple of sliding door wheels on the bottom of the tissue box, tie some fishing line to one end as rope for the pulley and voila, you have a time-lapse dolly.

Part 3

Of course there was the messy business of actually getting it to move but I won't go in to too much detail (see the attached code.) But I will mention that:

  • I only needed basic high school mathematics to calculate the number of steps each motor took between frames. And also I had to make sure the total movement didn't exceed the physical limits of the rig i.e. didn't travel further than the length of the CD rack and didn't turn more than 180 degrees.
  • Since the Arduino draws relatively little current, the power bank kept auto-powering down because it's minimum threshold wasn't being reached. So to prevent this I made sure that the HackHD was turned on immediately after I switched on the power bank. This isn't as simple as just supplying the camera with power. There is a specific pin on the camera that needs to be held low for a few seconds to turn it on. Once activated, it drew sufficient power to keep the power bank chugging along. (PS the power bank had 2 USB ports, one for the Arduino and one for the HackHD)
  • Last but not least, I had a run-in with the law while taking my final time-lapse. It turns out that placing a bizarre-looking camera beside a state-owned railway, facing the Sydney Harbour Bridge is not only very suspicious but also illegal. Not to mention the inside looked kinda like a home-made bomb with it's green LCD, numerical keypad and various colorful wires ("should I cut the red or the blue wire?") I used the excuses of "I thought the fence wasn't technically government property" and "It's for a uni assignment." I always enjoy a round of good cop/bad cop. Anyway, after hours of fussing about they finally returned my camera and let me off under the condition I deleted any photos with pictures of security guards or police. So if you watch the below video to the end, you'll notice the final segment jolts forward suddenly. Those missing frames are where you would have seen a bunch of confused guys in fluorescent vests peering into the camera. One of them even used a post-it note to block the lense (hello, ever heard of unplugging the power?)
In the intro you'll see the camera get pounded by a summer storm. It miraculously survived though!

Goes without saying that after shooting you need to used some sort of computer software (e.g iMovie) to compile all the single images into a movie. The more pictures taken, the longer the video.


Time-lapse dollyC/C++
/Time-lapse dolly/

#include <Keypad.h>
#define STEPS 4096

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
const byte LCDpin = 13;
const byte camTX = 18, camRX = 19; //pins to communicate with the camera
char keys[ROWS][COLS] = {
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

//variables to store and verify user input 
String filmDur = 0, sbf = 0, deg = 0;
boolean answerGiven = 0, userVerified = 0, inputValid = 0;

//stepper motors setup
const byte stepperSpeed = 10; //the higher the number the slower the stepper
const byte translate[4] = {9,10,11,12}; // pins for the translating stepper
const byte rotate[4] = {14,15,16,17}; // pins for rotating stepper
const boolean stepperSeq[8][4] = { //step sequence to move the motors


void setup(){
  pinMode(camTX, OUTPUT);
  digitalWrite(camTX, HIGH); //Make sure the camera button is not grounded
  pinMode(camRX, INPUT);  
  //set up stepper motors for translation and rotation 
  resetLCD(LCDpin); //power on LCD   
  Serial.println("Welcome to the  time-lapse dolly");
  standbyMode(camTX); //i only had to power on the camera here because 
                    //otherwise the battery turned itself off due to lack of current being drawn
  while(digitalRead(camRX) == LOW){
    ; //wait until camera verifies it is in standby mode
void loop(){
  do  {     
      Serial.println("                                \n\n");
      Serial.print("Duration of filming in hours: ");
      filmDur = getData(2); //wait for input of 2 chars in length
      do {
        Serial.println("                                \n\n");
        Serial.print("How many secondsbetween shots:");
        sbf = getData(2);
        if(sbf.toInt() < 10){
          Serial.println("                                \n\n");
          Serial.print("Minimum 15 seconds between shots");
        else {
          inputValid = 1;
      } while(inputValid == 0); 
      Serial.println("                                  \n\n");
      Serial.print("How many degreesof rotation:");
      deg = getData(3);
      Serial.println("                                  \n\n");
      Serial.print("OK! the sequencewill go for.... ");
      Serial.println("                                \n\n");
      Serial.print(" hours");
      Serial.println("                                \n\n");
      Serial.print("There will  be  ...             ");
      Serial.println("                                \n\n\n\n");
      Serial.print(" seconds      between frames");
      Serial.println("                                \n");
      Serial.print("And the camera  will rotate ");
      Serial.println("                                \n\n\n");
      Serial.print(" degrees");
      Serial.println("                                \n\n");
      Serial.print("Is this OK?     * = YES # = NO  ");
      answerGiven = 0; //reset boolean in case it isn't the first time through
      do {
        char key = keypad.getKey();
        if(key == '*'){ //if YES, mark input as verified by the user
        userVerified = 1;
        answerGiven = 1;
        if(key == '#'){ //if NO, clear entries and start from the top
          filmDur = 0; 
          sbf = 0; 
          deg = 0;
          inputValid = 0;
          answerGiven = 1;
      } while(answerGiven == 0); //wait for a valid answer
  } while (userVerified == 0); //if input is verified then continue
  //begin time-lapse photography
    Serial.println("                                \n\n");
    Serial.print("Initiating      sequence...\n");
    //calculate total frames in sequence
    int totalFrames =  filmDur.toInt() * 60 * 60  / sbf.toInt(); 
    //calculate increment for the translation stepper
    int incrementT = round(5 * STEPS / (float)totalFrames); 
    //calculate increment for the rotation stepper
    float ratio = (float) deg.toInt() / 360;
    int incrementR = round(STEPS * ratio / totalFrames);

    //get out of standby mode before actually beginning photography
    while(digitalRead(camRX) == HIGH){
    int i, nextStepT = 0, nextStepR = 0;
    for(i = 1; i <= totalFrames; i++){
      unsigned long time = millis(); //get time at beggining of iteration
      shoot(camTX); //take a picture
      Serial.println("                                \n\n");
      //calculate and display the percentage completion and hours remaining
      int percentage = i / (float)totalFrames * 100;
      Serial.print("% complete");
      int hrsLeft = round( (float)filmDur.toInt() * ( (100 - (float)percentage) / 100 ));
      Serial.print( hrsLeft );
      Serial.print(" hours left");  
      nextStepT = stepper(translate, incrementT, 1, nextStepT); //translate
      nextStepR = stepper(rotate, incrementR, 1, nextStepR);  //rotate
      //subtract the time elapsed since the beggining of the iteration from 
      //the desired delay between frames, and then delay that resulting time
      while(digitalRead(camRX) == HIGH){
        ; //wait for picture to save before moving on
      while( ( millis() - time ) < ( sbf.toInt() * 1000 ) ){


void resetLCD(byte pin) {
  delay(2000); //LCD needs time to self-test

void standbyMode(byte pin){
  digitalWrite(pin, LOW);
  digitalWrite(pin, HIGH); 

void shoot(byte pin){
  digitalWrite(pin, LOW);
  digitalWrite(pin, HIGH); 

String getData(int len){
  //function that returns a string entered on the keypad of max length "len" 
  String data = 0;
  while(data.length() < len) {
     char key = keypad.getKey();  
     if (key){
        if(key == '#' || key == '*'){
          ; //do not accept non-numeric characters
        } else {
            data += key; //concatenate character to input string
            Serial.print(key); //print character
   delay(500); //allow time so user can actually see his/her input before moving on
  return data;  

void setupStepper(const byte type[]){
  //set all stepper pins to output
  int i = 0;
  for(i=0; i<4; i++){
      pinMode(type[i], OUTPUT);

int stepper (const byte type[], int iterations, boolean dir, int lastStep){
  int d = 0;
  if(dir){ //dir = 1 means clockwise
     for(d = 0; d < 4; d ++){
      digitalWrite(type[d], stepperSeq[lastStep][d]);
     if(lastStep > 7){
       lastStep = 0;

  else { //dir = 0 means counterclockwise
    for(d = 0; d < 4; d++){  
      digitalWrite(type[d], stepperSeq[lastStep][d]);
    if( lastStep < 0 ){
     lastStep = 7;
  return lastStep;


Similar projects you might like

Automated Garden

Project tutorial by Thomas sxt

  • 67 respects

PID Control Line Follower Robot

Project tutorial by Team KittenBot

  • 25 respects

Zen Garden CNC End Table

Project tutorial by MakrToolbox

  • 42 respects

Driving a Focus Stacking Rail Using an Arduino

Project tutorial by Jennifer Deegan

  • 1 comment
  • 5 respects

Home Automation Using Raspberry Pi 2 And Windows 10 IoT

Project tutorial by Anurag S. Vasanwala

  • 706 respects

Personal Weather Station (Arduino+ ESP8266 + Thingspeak)

Project tutorial by Jayraj Desai

  • 107 respects
Add projectSign up / Login