YAPI 2.0 Yet Another Programmable Intervalometer

This project was born out of a need for a device to trigger the wake/focus and shutter actions of a Canon Digital Rebel XT camera at predetermined intervals. I wanted this device for time-lapse photography to be something simple in terms of both hardware and software yet flexible enough to be easily reconfigurable if necessary.

The result is "YAPI" or "Yet Another Programmable Intervalometer", so named because devices like this seem to be popular home brew projects as any Google search will show. This article will describe my version of the the hardware and software.

The overall operation of YAPI is simple:

1) YAPI is connected to the remote focus/shutter port on the camera.
2) The switch pack is set to the desired interval.
3) Power is supplied to the micro-controller.

As soon as power is applied, YAPI immediately triggers the shutter, determines the user selectable duration and then begins a countdown to the next shutter event. This loop continues until power is removed.

General Description

The core hardware component behind YAPI is an Arduino based micro-controller. This project uses a Bare-Bones Arduino clone from Modern Device but any of the other Arduino product configurations could easily be adapted by making the appropriate data line connections.

The Bare-Bones Arduino clone itself came as a kit and was very easy to put together. 17 male pins extend below the Bare-Bones and slide into a 17-pin female connector/riser that is soldered to the main YAIP board.

This connector/riser provides access to all Arduino's digital lines as well as +5v and ground. Additionally this configuration provides an open area under the Bare-Bones for future hardware expansion as well as allowing the Bare-Bones to be removed for other uses if the need arises.

YAPI Hardware Description

The YAPI board connects a small 4x switch pack to Arduino pins D2, D3, D4 and D5 which are configured as inputs. Four 10K ohm resistors pull these pins to ground so unless a switch is in the 'on' position, Arduino will see a 0 or LOW input on these
pins. The switches are used to select the shutter trigger interval.

What the switch setting mean in terms of duration and action is completely up to the software in the Arduino. In other words a switch setting could mean "take a picture every five minutes" or by simply changing the software the same switch setting could mean "take 10 pictures in a row, wait an hour and take 3 more pictures". That is the "programmable" part of YAPI.

The camera wake/focus and shutter are activated by Arduino pins D7 and D12 which are configured as outputs. When Arduino wants to wake up and focus the camera for example, it will present a HIGH output on pin D7. D7 is connected to the input pin of a TIL111 optoisolator. Just about any optoisolator should work, I used the TIL111 only because I had a few laying around. A 470 ohm current limiting resistor on another pin of the optoisolator provides a path to ground. This lets Arduino turn on the internal phototransistor which has the effect of activating the reed relay and also electrically isolates Arduino from the relay as well as the camera.

The reed relay is a small 0.5A relay I found at Radio Shack and in this configuration acts a simple continuity switch connecting the focus signal from the camera to camera common. This is the same behavior as someone manually pressing the shutter button
on the camera itself part way down to make the camera auto-focus.

A second set of optoisolator and relay are connected to Arduino pin D12 and behaves the same way but is connecting the shutter signal from the camera to camera common. This is the action that actually takes the picture.

I added a 1N914 protection diode to each relay in order to prevent the voltage spike from the relay from damaging the phototransistor in the optoisolator. Maybe the diodes were not really needed at these low voltage levels but since they were so easy to attach directly to the top of the relays I went ahead and put them in.

Two LEDs with current limiting 470 ohm resistors are connected to Arduino pins D9 and D10 which are configured as outputs. These are for user feedback to show when the focus and the shutter are active. Besides, every gadget needs blinky lights! The LED actions could easily be reconfigured in software for another purpose.

All hardware is mounted on a small perf-board, with optoisolators mounted in in sockets. All the components are connected with wires via point to point soldering on the back of the perf-board.

Power for the unit is fed directly into the Bare-Bones board and can be supplied from a 9v wall transformer (wall-wart) or a 9v battery. I haven't done any tests to see how long YAPI can run on a battery, but for any extended operation a plug in wall-wart would be the way to go.

The result is a compact device with an overall size of about 3 3/4 inches x 2 3/4 inches x 1 inch (9.5 cm x 7cm x 2.5cm).

Arduino Software Description

Note: Sadly, Blogger won't allow me post the source code as an actual file so I'll just display the full listing at the end of this discussion.

The general logic flow of the software is as follows:
1. read the user input from the switches
2. take action ( auto-focus and trip the shutter)
3. pause for the duration
4. go back and begin again

Currently the software is coded to trigger the camera at 5, 10, 15, 30, 45, 60, 90, or 120 intervals with those intervals either seconds or minutes, based on one of the input switch setting.

Other home-brew intervalometers I have seen use an LCD screen, selection buttons, debounce code and additional software to make a wide range of duration selections. Thats quite nice but I have found that I typically only needed a small set of durations in my photography. I also wanted to keep this project as simple as possible.

As mentioned in the hardware section above, duration selection for the intervalometer is set through the physical switches attached to input pins D2, D3, D4 and D5. As Arduino runs in its main loop() routine it calls DetermineTimeDelay() to check the switches and get the delay duration. More on this routine in a minute.

Arduino then calls triggerWakeFocus() which outputs a HIGH on pin 7, causing the relay to close, waking up the camera if it was asleep and activating focus. After a slight delay of 10 microseconds the the triggerShutter() routine activates the camera shutter with a HIGH on output pin 12. Each of these two routines also turn on and then off their respective LED for some visual feedback.

That 10 microsecond delay between wake/focus and shutter serves two purposes. First, gives the camera time to actually wake up and focus and second it allows Arduino to active both relays in short order. I found that without the delay, only one relay would fire and I have no idea why. The 10ms delay solved that strange problem.

After triggering the shutter, Arduino goes into it's delay() routine for the required time. I elected to use the Arduino internal delay(milliseconds) routine rather than add a Real Time Clock (RTC) chip like the DS1307 for a few reasons. First, without an LCD display and addition input buttons, setting the time on the clock chip would be a problem. Second, I don't really need the kind of accuracy a RTC provides since this isn't a NASA moon landing after all. Besides, even if YAPI loses or gains a few seconds over the course of a day of shooting photos that is close enough for me. Perhaps someday I'll set up YAPI to take time-lapse shots of a clock over 24 hours to check the actual accuracy of using delay().

That DetermineTimeDelay() routine at the beginning of loop() is the "programmable" part of YAPI. With four input switches I decided to use a binary coding of on/off to make 16 different durations available. One of the switches is assigned to be a
"minutes or seconds" selection and the other three switches participate in the following 8 durations:

Switch 1 is for minutes / seconds

3 remaining Resulting
switches duration
000 5
001 10
010 15
011 30
100 45
101 60
110 90
111 120

For example, if switch 1 off (meaning, "use seconds") and the three remaining switches are "off-on-off" (010) then the duration is determined to be 15 seconds.

If switch 1 is on (meaning "use minutes") and the three remaining switches are off-on-off (010) then the durations is determined to be 15 minutes.

The "If (swC && swB && swA)" statements in the remaining part of the DetermineTimeDelay() routine assign a predetermined milliseconds count to the value that will be returned and used in the delay() routine. For example:

Seconds (duration * 1000)
time value return for delay(ms)
010 = 15 15000

Minutes (duration * 60,000)
time value return for delay(ms)
010 = 15 900000

It is there in the "if (swC && swB && swA)" statememts that code can easily be modified to have YAPI do different things depending on the switch selection.


I hope this description explains the YAPI project, how it works and how it can be taken and easily modified. Interesting modifications I have considered are analog input triggers such as motion, sound or light beam interruption. Even altitude or sudden accelerations changes (using someone else's camera of course!) Digital triggers like door switches, pressure gauges or water level sensors could also be used to trigger a photograph.

YAPI (Arduino) source code


YAIP 2.0 Yet Another Programmable Intervalometer

Hardware base: Arduino AtMega168

This Arduino software is released to the public
domain "as is". The author makes no warranty
expressed or implied and assumes no responsibilities.
Feel free to fold, bend, spindle or mutilate as you see fit.


// user input switches
int switch_A = 2;
int switch_B = 3;
int switch_C = 4;
int switch_MS = 5; // minutes, seconds selection

// camera activity pins
int wake_focus = 7;
int shutter = 12;

// feedback LEDs
int redWakeFocusLED = 9;
int greenShutterLED = 10;

unsigned long timeDelay = 0; // used in Delay(timeDelay);

// adjust these as needed
int wakeFocusDelay = 500;
int shutterDelay = 500;
int cameraProcessingDelay = 500;

void setup(){

pinMode(switch_A, INPUT);
pinMode(switch_B, INPUT);
pinMode(switch_C, INPUT);
pinMode(switch_MS, INPUT);

pinMode(wake_focus, OUTPUT);
pinMode(shutter, OUTPUT);

pinMode(redWakeFocusLED, OUTPUT);
pinMode(greenShutterLED, OUTPUT);

void loop() {
// read the user's input switches each loop
timeDelay = DetermineTimeDelay();

// activate the camera
// need a delay in between two relay calls otherwise
// one of the relay relay won't trigger

// give the camera time to process and store the image

// pause user input switch duration

void triggerWakeFocus(){

digitalWrite(redWakeFocusLED, HIGH);
digitalWrite(wake_focus, HIGH);
digitalWrite(wake_focus, LOW);
digitalWrite(redWakeFocusLED, LOW);

void triggerShutter(){

digitalWrite(greenShutterLED, HIGH);
digitalWrite(shutter, HIGH);
digitalWrite(shutter, LOW);
digitalWrite(greenShutterLED, LOW);

unsigned long DetermineTimeDelay(){
// reads input switches and returns the number of milliseconds

boolean swA, swB, swC, use_minutes;
unsigned long result = 0;

// read the switches and gather their state
if (digitalRead(switch_MS) == HIGH)
use_minutes = true; // switch is up
use_minutes = false; // switch is down, use seconds instead

if (digitalRead(switch_C) == HIGH)
swC = true;
swC = false;

if (digitalRead(switch_B) == HIGH)
swB = true;
swB = false;

if (digitalRead(switch_A) == HIGH)
swA = true;
swA = false;

// based on swtich state, determine determin the delay
if (!swC && !swB && !swA){
//result = 5;
if (!use_minutes)
result = 5000;
result = 300000;
else if (!swC && !swB && swA){
//result = 10;
if (!use_minutes)
result = 10000;
result = 600000;
else if (!swC && swB && !swA){
//result = 15;
if (!use_minutes)
result = 15000;
result = 900000;
else if (!swC && swB && swA){
//result = 30;
if (!use_minutes)
result = 30000;
result = 1800000;
else if (swC && !swB && !swA){
//result = 45;
if (!use_minutes)
result = 45000;
result = 2700000;
else if (swC && !swB && swA){
//result = 60;
if (!use_minutes)
result = 60000;
result = 3600000;
else if (swC && swB && !swA){
//result = 90;
if (!use_minutes)
result = 90000;
result = 5400000;
else if (swC && swB && swA){
//result = 120;
if (!use_minutes)
result = 120000;
result = 7200000;
result = 0;

return result;