• Background Image

    News & Updates

June 5, 2015

Santa Cruz Boardwalk

Warm, sunny central California coast on a weekend.

I have been traveling to the Bay Area for both work and pleasure in an astronomically large ratio, as compared to the rest of the world in the past eight years. However, this is the only time did I ever drive west of Campbell and over the coastal hills to the waterline.

_DSC0222

 

_DSC0203

 

_DSC0201

 

_DSC0183

 

_DSC0180

 

_DSC0181

 

March 7, 2015

Times Square – 2012

The first look: Less than an hour in the heart of Times Square, flanked by over three hours drive time with traffic to/from Harrisburg, PA.

March 2, 2015

The moon and break lights

February 22, 2015

Battery Spencer

Battery Spencer was a reinforced concrete 12″ gun battery which was built in 1893-1897, a part of Fort Baker in Marin County, California. Casual tourists will identify this location as a great vantage to the Golden Gate Bridge.

_DSC8963

 

_DSC8966

 

_DSC8965

It was a clear night in Marin County – a rare treatise for photographers.

February 5, 2015

DIY Timelapse Motion Controller: The specs

OK I am going to cut the chase and go straight to the instructions or steps in how I built my time-lapse motion rig. There is probably over a hundred such articles on the web today, but I found the most practical (practical in the sense of required skills and available tools in a regular household) tutorial/instructions in two articles published by legendary Rob Taylor from Tutsplus.

Camera Dolly & Slider

The primary component for the slider is a 20mm X 40mm V-Slot aluminum channel. You can buy it in one of the pre-cut lengths, or saw off to a desired length. At the ends, I hooked up two Linear Actuator End Mounts, which can house the Stepper Motor at one end, and an Idler Pulley at the other. As for the dolly system, I used one standard Gantry Plate and six Delrin Wheels mounted on using appropriate spacers. Finally, the dolly system was hooked on to the motor for movement control using a GT2 2mm Timing belt. I am not going to repeat the detailed instructions, which you can find here.

 Additionally, I used four Universal L Brackets and two gantry plates to facilitate mounting the V-Slot Al channel onto two tripods.

IMG_1814

IMG_1815

IMG_1817

Timelapse Motion Controller

Again, going by the original instructions from a post by Rob, I decided to use an Arduino microcontroller board to programmatically control the movement of the Stepper motor. I used an Arduino Mega 2560 which is one level above the beginners’ Uno, because of the wide range of digital pins available (54 in Mega Vs 14 in Uno) allowing me to avoid pin conflicts between Arduino Shields. For the motor driver, I used SeeedStudio Motor Shield V2.0 and for user input/output one SeeedStudio 2.8″ TFT Touch Shield V2.0. Since the touch shield uses serial communication, I was able to mount both the shields directly on to the Arduino without significant ‘re-mapping’ of the pins. For firing camera shutter release, I used a common NPN transistor based switch to ‘short’ shutter release pins on the camera.

Finally, I put all the electronic parts inside a barebone Arduino Project enclosure which I bought from Radio Shack.

IMG_1803

Here is the Arduino source that I used:

/**
* Timelapse Motion Controller Arduino Program v1.0.1
* Autor: Mrinabh Dutta
* Version: 1.0.1
* Date: January 16, 2015
*
* Copyrights (c) 2015, Mrinabh Dutta
* All rights reserved.
* DISCLAIMER
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/

#include <stdint.h>
#include <TFTv2.h>
#include <SPI.h>
#include <SeeedTouchScreen.h>
#include <Stepper.h>

#define TEAL 0x0f10
#define LIGHT_YELLOW 0xffee
#define DARK_BROWN 0x7000
#define LIGHT_GREEN 0x7400
#define ORANGE 0xf8e0
#define STEPS_PER_TURN 10

#define TOUCH_DELAY 100

#define CAMERA_PIN 53

//init TouchScreen port pins
TouchScreen ts = TouchScreen(XP, YP, XM, YM);

// initialize the stepper library on pins 8 through 11:
Stepper motor(60, 8,11,12,13);

// Timelapse Motion Control user input parameters
int interval;
int moveDuration;
int staticTime;
int ramp;

const int MENU_HOME = 0;
const int MENU_INTERVAL = 1;
const int MENU_MOVDUR = 2;
const int MENU_STATTIME = 3;
const int MENU_RAMP = 4;
const int MENU_START = 5;
const int MENU_RECORDING = 6;
int currentMenu = MENU_HOME;

char* intervalDisplayVal[] = {
"1 s",
"2 s",
"5 s",
"10 s",
"15 s",
"30 s",
"1 m",
"2 m",
"5 m"
};
int intervalSeconds[] = {
1,
2,
5,
10,
15,
30,
60,
120,
300
};
int intervalIdx = 0;

char* mdDisplayVal[] = {
"30 min",
"45 min",
"1 hr",
"1.5 hr",
"2 hr",
"3 hr",
"5 hr",
};
int mdSeconds[] = {
1800,
2700,
3600,
5400,
7200,
10800,
18000
};
int mdValIdx = 0;

char* shutterSpeedVal[] = {
"0 s", "1s", "1.3s", "1.6s", "2s",
"2.5s", "3s", "4s", "5s", "6s", "8s", "10s", "13s",
"15s", "20s", "25s", "30s"
};
int cameraDelayMilliSec[] = {
1000, 1000, 1300, 1600, 2000,
2500, 3000, 4000, 5000, 6000, 8000, 10000, 13000,
15000, 20000, 25000, 30000
};
int shutterSpeedIdx = 0;

char* rampVal[] = {
"10 steps/turn",
"15 steps/turn",
"20 steps/turn",
"25 steps/turn",
"30 steps/turn",
"50 steps/turn",
"100 steps/turn"
};
int stepsPerTurn[] = {
10, 15, 20, 25, 30, 50, 100
};
int rampIdx = 0;

void setup() {
// Turn TFT backlight on and initialize
TFT_BL_ON;
Tft.TFTinit();

// Initialize motor
initMotor();

pinMode(CAMERA_PIN, OUTPUT);
digitalWrite(CAMERA_PIN, LOW);

// Serial.begin(115200);
showSplashScreen();
delay(3000);
homeScreen();
}

void loop() {
detectKeyPress();
}

void showSplashScreen() {
Tft.drawString("Timelapse", 0, 0, 3, YELLOW);
Tft.drawString("Motion", 0, 35, 3, YELLOW);
Tft.drawString("Control", 0, 70, 3, YELLOW);
Tft.drawString("by Mrinabh Dutta", 0, 105, 2, GREEN);
}

void homeScreen() {
Tft.fillScreen();

// Interval
Tft.drawRectangle(1, 1, 238, 318, TEAL);

Tft.drawRectangle(5, 5, 230, 54, TEAL);
Tft.drawString("Interval", 10, 22, 2, BLUE);
Tft.drawString(intervalDisplayVal[intervalIdx], 10, 42, 1, RED);

Tft.drawRectangle(5, 64, 230, 54, TEAL);
Tft.drawString("Move Duration", 10, 81, 2, BLUE);
Tft.drawString(mdDisplayVal[mdValIdx], 10, 101, 1, RED);

Tft.drawRectangle(5, 123, 230, 54, TEAL);
Tft.drawString("Static Time", 10, 140, 2, BLUE);
Tft.drawString(shutterSpeedVal[shutterSpeedIdx], 10, 160, 1, RED);

Tft.drawRectangle(5, 182, 230, 54, TEAL);
Tft.drawString("Ramp", 10, 199, 2, BLUE);
Tft.drawString(rampVal[rampIdx], 10, 219, 1, RED);

Tft.fillRectangle(5, 241, 230, 54, TEAL);
Tft.drawString("START", 85, 258, 2, RED);

Tft.drawString("Made by Mrinabh Dutta", 90, 300, 1, YELLOW);
}

void showPopupModal() {
Tft.fillScreen();
Tft.fillRectangle(10, 10, 220, 300, LIGHT_YELLOW);
Tft.drawLine(10, 50, 230, 50, DARK_BROWN);

if(currentMenu == MENU_START) {
Tft.fillRectangle(15, 275, 210, 30, DARK_BROWN);
Tft.drawString("CANCEL", 88, 285, 2, WHITE);

Tft.fillRectangle(15, 15, 210, 30, RED);
Tft.drawString("START", 95, 20, 2, WHITE);
} else {
Tft.fillRectangle(15, 55, 30, 30, DARK_BROWN);
Tft.drawString("-", 19, 59, 3, WHITE);

Tft.fillRectangle(195, 55, 30, 30, DARK_BROWN);
Tft.drawString("+", 199, 59, 3, WHITE);

Tft.fillRectangle(15, 275, 210, 30, LIGHT_GREEN);
Tft.drawString("SAVE", 95, 285, 2, DARK_BROWN);
}

switch(currentMenu) {

case MENU_INTERVAL:
Tft.drawString("Interval (Seconds)", 15, 25, 2, DARK_BROWN);
Tft.drawString(intervalDisplayVal[intervalIdx], 25, 120, 3, DARK_BROWN);
break;

case MENU_MOVDUR:
Tft.drawString("Movement Duration", 15, 25, 2, DARK_BROWN);
Tft.drawString(mdDisplayVal[mdValIdx], 25, 120, 3, DARK_BROWN);
break;

case MENU_STATTIME:
Tft.drawString("Static Time", 15, 25, 2, DARK_BROWN);
Tft.drawString(shutterSpeedVal[shutterSpeedIdx], 25, 120, 3, DARK_BROWN);
break;

case MENU_RAMP:
Tft.drawString("Ramp", 15, 25, 2, DARK_BROWN);
Tft.drawString(rampVal[rampIdx], 25, 120, 2, DARK_BROWN);
break;

case MENU_START:

break;
}
}

void refreshPopupModalValue() {
Tft.fillRectangle(25, 120, 220, 25, LIGHT_YELLOW);
switch(currentMenu) {

case MENU_INTERVAL:
Tft.drawString(intervalDisplayVal[intervalIdx], 25, 120, 3, DARK_BROWN);
break;

case MENU_MOVDUR:
Tft.drawString(mdDisplayVal[mdValIdx], 25, 120, 3, DARK_BROWN);
break;

case MENU_STATTIME:
Tft.drawString(shutterSpeedVal[shutterSpeedIdx], 25, 120, 3, DARK_BROWN);
break;

case MENU_RAMP:
Tft.drawString(rampVal[rampIdx], 25, 120, 2, DARK_BROWN);
break;

case MENU_START:

break;
}
}

void detectKeyPress() {
Point p = ts.getPoint();
p.x = map(p.x, TS_MINX, TS_MAXX, 0, 240);
p.y = map(p.y, TS_MINY, TS_MAXY, 0, 320);

if (p.z > __PRESURE) {
processKeyPress(p.x, p.y);
delay(TOUCH_DELAY);
}
}

void processKeyPress(int x, int y) {
if(currentMenu == MENU_START) {
if(y < 45) {
beginTimeLapse();
return;
}
}

if(currentMenu == MENU_RECORDING) {
return;
}
if(currentMenu == MENU_HOME) {

// Interval
if(y < 59) {
currentMenu = MENU_INTERVAL;
}

// Move Duration
if(y > 64 && y < 118) {
currentMenu = MENU_MOVDUR;
}

// Static Time
if(y > 123 && y < 177) {
currentMenu = MENU_STATTIME;
}

// Ramp
if(y > 182 && y < 236) {
currentMenu = MENU_RAMP;
}

// Start timelapse motion!
if(y > 241) {
currentMenu = MENU_START;
}

showPopupModal();
} else {
// A popup modal is open
if(y > 55 && y < 85) {
if(x > 15 && x < 45) {
updateValue(0);
} else if (x > 195 && x < 225) {
updateValue(1);
}
}
// Save button - simply closes the modal
if(y > 275) {
currentMenu = MENU_HOME;
homeScreen();
}
}
}

void updateValue(int dir) {
switch(currentMenu) {
case MENU_INTERVAL:
if(dir == 1) {
if(intervalIdx < 8) {
intervalIdx++;
} else {
intervalIdx = 0;
}
} else {
if(intervalIdx > 0) {
intervalIdx--;
} else {
intervalIdx = 8;
}
}
break;

case MENU_MOVDUR:
if(dir == 1) {
if(mdValIdx < 6) {
mdValIdx++;
} else {
mdValIdx = 0;
}
} else {
if(mdValIdx > 0) {
mdValIdx--;
} else {
mdValIdx = 6;
}
}
break;

case MENU_STATTIME:
if(dir == 1) {
if(shutterSpeedIdx < 16) {
shutterSpeedIdx++;
} else {
shutterSpeedIdx = 0;
}
} else {
if(shutterSpeedIdx > 0) {
shutterSpeedIdx--;
} else {
shutterSpeedIdx = 16;
}
}
break;

case MENU_RAMP:
if(dir == 1) {
if(rampIdx < 6) {
rampIdx++;
} else {
rampIdx = 0;
}
} else {
if(rampIdx > 0) {
rampIdx--;
} else {
rampIdx = 6;
}
}
break;
}

refreshPopupModalValue();
}

/**
* Begin moving the dolly with the configured
* settings.
*/
void beginTimeLapse() {
// 5 seconds countdown
Tft.fillScreen();
Tft.drawString("Timelapse", 0, 0, 3, WHITE);
Tft.drawString("Recording starts in", 0, 35, 2, RED);

for(int i = 5; i >= 0 ; i--) {
String sec = String(i);
char charBuf[2];
sec.toCharArray(charBuf, 2);

Tft.drawString(charBuf, 95, 135, 5, WHITE);
delay(1000);
Tft.fillRectangle(95, 135, 50, 50, BLACK);
}

int totalStops = mdSeconds[mdValIdx] / (intervalSeconds[intervalIdx] + (cameraDelayMilliSec[shutterSpeedIdx] / 1000));

Tft.fillScreen();
Tft.drawString("Recording!", 0, 0, 3, RED);
Tft.drawString("Remaining Steps:", 0, 35, 2, WHITE);
Tft.drawString("Press RESET to abort", 0, 100, 1, WHITE);
Tft.drawString("CAM", 190, 95, 2, CYAN);
Tft.drawLine(0, 110, 219, 110, YELLOW);
Tft.drawString("Total Stops", 0, 120, 2, WHITE);
String stopStr = String(totalStops);
char stopsBuff[50];
stopStr.toCharArray(stopsBuff, 50);
Tft.drawString(stopsBuff, 140, 120, 2, RED);

Tft.drawString("Duration", 0, 150, 2, WHITE);
Tft.drawString(mdDisplayVal[mdValIdx], 140, 150, 2, RED);

Tft.drawString("Ramp", 0, 180, 2, WHITE);
Tft.drawString(rampVal[rampIdx], 140, 180, 2, RED);

for(int i = 0; i < totalStops; i++) {

int remaining = totalStops - i;
String rem = String(remaining);
char charBuf[50];
rem.toCharArray(charBuf, 50);
Tft.drawString(charBuf, 0, 70, 2, GREEN);

triggerCamera();
delay(cameraDelayMilliSec[shutterSpeedIdx]);
motor.step(stepsPerTurn[rampIdx]);
// motor.step(STEPS_PER_TURN);
delay(intervalSeconds[intervalIdx] * 1000);

Tft.fillRectangle(0, 70, 219, 20, BLACK);
}

// After finishing operation, return to home screen
currentMenu = MENU_HOME;
homeScreen();
}

/**
* Initialize stepper motor.
*/
void initMotor() {
motor.setSpeed(60);
pinMode(9,OUTPUT);
pinMode(10,OUTPUT);
digitalWrite(9,HIGH);
digitalWrite(10,HIGH);
}

/**
* Write to CAMERA_PIN to fire
* shutter release.
*/
void triggerCamera() {
Tft.fillCircle(214, 80, 5, RED);
digitalWrite(CAMERA_PIN, HIGH);
delay(50);
Tft.fillCircle(214, 80, 5, BLACK);
digitalWrite(CAMERA_PIN, LOW);
}

Complete Parts List

Dolly & Camera Slider

3 x V-Slot Gantry Plates – 20mmx80mm

1 x V-Slot 20mmx40mm

2 x V-Slot Spacer Block

2 x V-Slot Actuator End Mount

24 x Tee Nuts

16 x M5 Low Profile Screws 6mm

1 x M5 Low Profile Screw 40 mm

6 x OpenBuilds Dual V Wheel Kit – Delrin

3 x Eccentric Spacers

1 x GT2 Timing Belt

4 x Universal L Brackets

 

Motion Controller

1 x Arduino Mega 2560

1 x SeeedStudio Motor Shield V2.0

1 x SeeedStudio 2.8″ TFT Touch Shield V2.0

1 x NPN Transistor (e.g. 2N4401)

1 x 10K Resistor

1 x Veroboard (small)

1 x Arduino Project Enclosure

Connector pins, wires, solder, etc.

 

February 4, 2015

Making a Timelapse Motion Controller

Getting a motion controlled camera slider system has always been in my list of things to either buy or build, more from hobbyist perspective than with intention to make money out of it. Though making motion controlled time-lapse videos has been around for years, there is only a handful of OEMs providing with very limited options.

IMG_1784

So without further wait, I started researching on the options to build one. I knew for sure that though I am looking at a fractional cost to buying, there would be challenge in terms of automating motion control to camera shutter release and possibility of scraping the project halfway through due to lack of available appropriate mix and match of hardware. My initial design consisted of use of electrical conduits but that idea got scrapped quickly, for it did not meet the quality that I was looking for. Then I found this article which basically talks about constructing the camera slider using CNC parts.

As for the controller/electronics, I used Arduino Mega board, a motor shield and a TFT touchscreen shield. I went for Arduino Mega for its plethora of pins and avoid conflicts between the shields. For triggering the camera, a basic transistor based switching is being used.

IMG_1803And the rest was history, I will write a post on the construction details soon.

December 26, 2014

GoPro Timelapses – Day One

[su_vimeo url=”https://vimeo.com/115418850″ width=”800″]

December 21, 2014

Weekend in the Chihuahuan Desert

[su_custom_gallery source=”media: 683,682,681,680″ link=”lightbox” width=”200″ height=”200″ title=”never”]

It was already getting dark, though I had started quite early from San Antonio. But when I was there, I was there – kept wondering if I had accidentally stepped across the US-Mexico border (there were no border patrols nor immigration police on either side). A quick hike over a plateau that stood between me and the river got me to a vantage point, from where one could not only see the dying sun, but also the other half of the cliff across the river in Mexico.

This area is known as extremely good for stargazing. It was dark in less than an hour, but the moon was up early. Though I made plans to come back (for watching the stars after dark), I didn’t.