Description
This project is just the latest I have devised to attempt to keep from having to take
a laptop out to the dark-site and expose it to the elements.
A capable intervalometer, the unit is built around an Atmel ATMega168 8-bit RISC processor.
This is the processor that is included with the beautiful Arduino Diecimila development board (the source code
below is based on Arduino's C development suite available for Windows or Linux).
While Canon does offer the TC80 'smart remote', it's wired for the pro and pro-sumer cameras
therefore requires hacking to enable it to work with the Rebel series. Also, it doesn't provide for mirror lock-up.
Though the 2-pot described here has
no fancy display you can 'tune' it to your desired exposure in very short order. It connects to the
camera with a simple 3.5mm stereo to 2.5mm stereo 3-conductor wire that can be built easily with parts from Radio Shack. Or, you can substitute the 3.5mm stereo plug on the unit
with a 2.5mm stereo part and just use a pre-built cord.
Features
- Simple user interface
- Millisecond accuracy
- Easy to build with either perf-board or you can etch your own PCB
- On-unit battery or off-unit DC from 7.5-30v (not implimented in shown unit)
- Programmable exposure times from 15 seconds to over 17 minutes
- Programmable sequence from 1 to 32 exposures
- Selectable mirror lock-up (Canon Rebel Series)
- Ability to 'pause' and restart a sequence
- Ability to 'read out' the exposure length and number of exposures
- Shows the current exposure number before opening the shutter
The unit can be powered for up to 40 hours by a 9v battery as the circuitry draws
very little power (~15mA with all LEDs on and shutter open). Any suitable project case can be used.
If you wish to power the unit from 12v add a suitable power plug to provide power. This can be
anything from 7.5v to 30v and is regulated and filtered by the circuitry in the unit.
Sequencing a series of exposures
The unit is readied for a sequence of exposures by adjusting the two
potentiometers. The left hand potentiometer is adjusted to select an
exposure time between 15 seconds and just over 17 minutes (1038 seconds). The right hand
potentiometer is adjusted for the number of exposures required between
1 and 32.
To know exactly what exposure length and number of exposures are set press the sequencing button
and hold it for 3 seconds. For the length of exposure the LED will then flash the hundreds,
tens and ones. It will then pause for 2 seconds and flash the tens and ones for the number
of exposures. The flashes are normally 300ms long and any 'empty' value will be shown as a brief (10ms)
flash of the LED. For example. If your exposure length was set to 320 seconds the LED would
flash 3 times, pause, flash 2 times, pause and then give a 'blip'. The number of exposures
will not show the 'hundreds'. If you have a 'favourite' exposure length (mine is 5 minutes)
mark the case and drill a tiny hole so you can quickly position the left knob.
The timer also blinks the 'code' for the exposure number at the beginning of each exposure
to tell you how far along in your sequence you are.
If you wish to use the MLU function on your Canon Rebel
DSLR camera first set the function in the camera. Then, before you start exposing, press and hold the button for longer than 1 second. The LED will flash twice to tell you that MLU is set. If you change your mind just press
and hold the button again for longer than 1 second. MLU will be turned off
and the LED will flash once to tell you.
When you are setup and ready to begin exposing just press the button momentarily. If you have set
the MLU function the LED will flash once for 500ms during which time the shutter will be activated
for the 'first press' and the mirror will lock up. Then the LED will flash very rapidly for approximately
three seconds before the exposure starts while the camera settles down from the 'slap' of the mirror.
After this rest period the LED illuminate steadily to signify that there
is an exposure being taken and that your sequence is in progress. If you have not set
MLU the LED will immediately illuminate after the exposure number is flashed. Monitoring the LED
will let you know what is happening in the sequence (see below).
While the sequence is running if you need to make some small adjustment
to the mount or optical tube press the sequencing button momentarily. This will put
the timer into the 'holding' mode and the LED will start flashing rapidly. The current
exposure will run it's course after which the sequence will pause and the LED will flash at
a slower rate. At this time you can make your adjustments and re-commence the sequence
by pressing the sequencing button momentarily one more time.
If you wish to halt a sequence completely and clear all the values press the sequencing
button and hold it for at least 1 second. You will see the LED
go dark.
LED indications
The LED tells you what is going on. It has several states:
LED off
This tells you that no sequence has been started or that a sequence
has been cleared by the sequencing button.
LED flashes twice
LED flashes once
LED off with a 1/8 second 'flash' every 3 seconds
This tells you that your sequence is complete and that the unit is
awaiting input.
LED solid on
This tells you that a sequence is running and that an exposure is
in progress but that you haven't reached the last exposure in the
sequence.
LED on with a 1/8 second interruption once every 3 seconds
This tells you that a sequence is running and that the final exposure
in the sequence is underway.
LED flashes rapidly for 2 seconds
Mirror has been locked up and the camera is resting prior to an exposure.
LED flashing 8x per second
This tells you that a sequence is running and has been asked to
hold by the sequencing button. The shutter is open while the timer
completes the current exposure.
LED flashing 4x per second
This tells you that a sequence is running and has been asked to
hold by the sequencing button and that the shutter is now closed and
the unit is awaiting re-commence.
LED flashing 2x per second
This tells you that a sequence is running and that the shutter is
closed for the 'resting' period between exposures.
LED flashing code
Exposure length and number of exposures as described above
That's about it. Here's what my test board looks like


PS file to make a board
The parts list
- 1 x ATMega168 microprocessor (programmed). You can use
an ATMega8, ATMega48 or ATMega168. The code is small
- 1 x 4N36 optocoupler
- 1 x 78L05 5v positive voltage regulator
- 1 x Red LED (2 if you want a power LED)
- 1 x Momentary on push button switch
- 4 x .01uF capacitor
- 1 x .33uF capacitor
- 1 x 1K resistor
- 2 x 3K9 resistor (3 if you want a power LED)
- 1 x 16MHz crystal
- 2 x 15pF capacitors
- 2 x potentiometers (any value, but get something like 500K)
Get yourself an Arduino Diecimila development board. They're cheap at about
$30 and are powered and controlled by a USB port. The development software
suite is very good and the C-code is mostly hidden by the environment. The board comes with a processor
ready to program. The code below is for the Arduino environment.
The schematic

|
The code
// CamTimer
// Author: Rick Saunders
// Build: 1.2
// Date: June 24, 2008
// A simple astro-photo timer based on an AVR ATMega168
// Developed using the very versatile Arduino Diecimila
// The exposure length is settable from 15 to 1039 seconds.
// Resolution is within a few milliseconds but don't expect
// real accuracy... pots ain't that accurate. Also, some pots
// don't go down to 0 ohms at minimum, find two that do.
// The maximum number of exposures is 33, the minimum is 1.
// The user interface is sparce, the 2 pots, 1 button and 1 LED.
// With this minimal approach many things can be determined by
// the action of the LED including, exposing, resting, holding
// with shutter open, holding with shutter closed, last image
// in sequence in progress and sequence done.
// The button serves several purposes which are starting
// sequences, holding sequences, re-commencing sequences and
// clearing all values to start the next sequence.
int ExpTimePin = 5; // The AD for the exposure time (uPC pin 23)
int ExpNumPin = 4; // The AD for the number of exposures (uPC pin 24)
int StartPin = 3; // The AD for the start/stop button (uPC pin 25)
int ShutterPin = 4; // The pin connected to the shutter (uPC pin 6)
int LEDPin = 2; // The pin connected to the LED (uPC pin 4)
int NumExp = 5; // The number of exposures
long RestTime = 12; // The rest time between exposures
long ExpTime = 5; // The exposure time in seconds
long HoldTime = 0; // The time we started holding
int CurExp = 0; // The current exposure number
int ShutterOpen = 0; // Are we exposing?
int ShutterResting = 0; // Are we resting?
long BlinkTime = 500; // How long to blink the LED
int LEDBlinking = 0; // Are we blinking?
int FastBlink = 0; // Are we blinking real fast or real slow?
int SeqRunning = 0; // Is the timer running?
int SeqOnHold = 0; // Are we SeqOnHold (to refocus etc)?
int MirrorLockUp = 0; // Are we using mirror lock-up?
int Counter = 0; // Just a counter;
int Done = 1; // Are we done with the timer?
long PTime; // Time the button is pressed
long RTime; // Time the button is released
int StartButton = 3; // The AD the start button is on
int ButtonPressed = 0; // Has the button been pressed
int ButtonValue; // The AD value of the start/stop button
long ExpStartTime; // The exposure start time in millis
long RstStartTime; // The rest start time in millis
int pot1; // Counter value of pot1
int pot2; // Counter value of pot2
void setup() {
pinMode( LEDPin, OUTPUT ); // Set the two output pins
pinMode( ShutterPin, OUTPUT );
}
void loop() {
// Check for a button press with a 20ms debounce
// Here we're just reading a when the button was pressed and
// setting a flag that the button has indeed been pressed.
ButtonValue = analogRead( StartPin );
if ( ButtonValue < 100 ) {
delay( 20 );
ButtonValue = analogRead( StartPin );
if ( ( ButtonValue < 100 ) && ( ButtonPressed == 0 ) ) {
ButtonPressed = 1;
PTime = millis();
}
}
// As we're going to action on release of the button (allows us
// to hold the button to clear things) we read when the button
// was released. If it is held for 400ms it'll clear all the variables
// and turn off the shutter. If it's just been pushed and released
// we'll start things rolling.
if ( ( ButtonValue > 500 ) && ( ButtonPressed ) ) {
ButtonPressed = 0;
RTime = millis();
// If pressed longer than 3 seconds show the exposure information. The
// LED will show a momentary 'flash' for a 0 in the hundreds, tens or
// ones colum, and will flash the integer number if not 0
if ( ( RTime - PTime ) > 3000 ) {
if ( ! SeqRunning ) {
pot1 = analogRead( 4 ) + 15;
CountOut( 0, pot1 );
delay( 1000 );
pot2 = ( int ) ( analogRead( 5 ) / 32 ) + 1;
CountOut( 1, pot2 );
}
}
// If pressed longer than 1 second but less than 3 seconds if a sequence is
// running will reset all variables and stop the sequence. If a sequence
// is not running then MLU will be set and the MLU LED will illuminate
else {
// If the button has been held for more than 1 second
if ( ( RTime - PTime ) > 1000 ) {
// If there is a sequence running or Done isn't set then
// clear all the variables and turn off the LED
if ( ( SeqRunning ) || ( ! Done ) ) {
ResetVars();
digitalWrite( LEDPin, LOW );
digitalWrite( ShutterPin, LOW );
}
else {
// Ok, we're setting/unsetting MLU. Flash the LED twice
// for set and once for unset
int j;
if ( ! MirrorLockUp ) {
for ( int u = 0; u < 2; u++ ) {
digitalWrite( LEDPin, HIGH );
delay( 250 );
digitalWrite( LEDPin, LOW );
delay( 250 );
}
MirrorLockUp = 1;
} else {
digitalWrite( LEDPin, HIGH );
delay( 250 );
digitalWrite( LEDPin, LOW );
MirrorLockUp = 0;
}
}
}
// If we're not running a sequence then a sequence will start. If we ARE running
// a sequence then the sequence will complete it's current exposure then put the
// sequence on hold. While the exposure is completing the LED will flash quickly
// and then flash at 1/2 that speed when the exposure is done and it's awaiting
// input.
else {
if ( SeqRunning ) { // If we're running, don't change
SeqOnHold = 1; // any of the exposure values, just set
FastBlink = 1; // SeqOnHold true and blink the LED
BlinkTime = 125;
HoldTime = millis();
} else {
Done = 0;
ShutterResting = 0;
ShutterOpen = 0;
LEDBlinking = 0;
if ( ! SeqOnHold ) { // If we're not on hold then 0 the current exposure number
CurExp = 0;
}
SeqRunning = 1;
digitalWrite( LEDPin, LOW );
delay( 500 );
CountOut( 1, ( CurExp + 1 ) ); // Show us the exposure number
delay( 1000 );
if ( ! SeqOnHold ) { // If we weren't 'holding' get a new time and number
if ( MirrorLockUp ) {
digitalWrite( ShutterPin, HIGH );
digitalWrite( LEDPin, HIGH );
delay( 500 );
digitalWrite( ShutterPin, LOW );
digitalWrite( LEDPin, LOW );
delay( 125 );
Counter = 0;
while( Counter < 66 ) {
Counter % 2 ? digitalWrite( LEDPin, HIGH ) : digitalWrite( LEDPin, LOW );
delay( 33 );
Counter++;
}
}
ExpTime = analogRead( 4 ) + 15;
NumExp = (int) ( analogRead( 5 ) / 32 ) + 1;
}
SeqOnHold = 0; // Set 'holding' off
ExpStartTime = millis(); // Set the start time and open the shutter
digitalWrite( LEDPin, HIGH );
digitalWrite( ShutterPin, HIGH );
ShutterOpen = 1; // Set the 'exposing' flag
CurExp++; // and increment the current exposure number
}
}
}
}
// Blink the LED if we're holding or if we're on the last
// exposure of a sequence.
if ( ( SeqOnHold ) || ( CurExp == NumExp ) ) {
if ( millis() > ( HoldTime + BlinkTime ) ) {
if ( FastBlink ) {
digitalWrite( LEDPin, HIGH );
FastBlink = 0;
} else {
digitalWrite( LEDPin, LOW );
FastBlink = 1;
}
// If we're on the last exposure then we want to blink the
// LED once every 2 seconds with a 2 second blink. If the
// sequence is ended then the LED is on with a short 1/8 sec
// 'blink off' ever 2 seconds
if ( ( CurExp == NumExp ) && ( ! Done ) ) {
if ( FastBlink ) {
SeqRunning ? BlinkTime += 125 : BlinkTime += 2875;
} else {
SeqRunning ? BlinkTime += 2875 : BlinkTime += 125;
}
// Ok... we're not on the last exposure but we're holding.
// If the shutter is open but holding we want to blink the
// LED 16x/sec... if the shutter is closed then 8x/sec
} else {
if ( ShutterOpen ) {
BlinkTime += 63;
} else {
BlinkTime += 125;
}
}
}
}
if ( SeqRunning ) {
// If the shutter is open then the LED will be on. When the exposure
// times out then turn off the LED and start the 'rest period'
if ( ShutterOpen ) {
if ( millis() > ( ExpStartTime + ( ExpTime * 1000 ) ) ) {
digitalWrite( LEDPin, LOW );
digitalWrite( ShutterPin, LOW );
ShutterOpen = 0;
if ( ( CurExp < NumExp ) && ( ! SeqOnHold ) ) {
ShutterResting = 1;
RstStartTime = millis();
BlinkTime = 500;
} else {
SeqRunning = 0;
BlinkTime = 2000;
}
}
}
// If the shutter is 'resting' then blink the LED 2x/second
if ( ShutterResting ) {
if ( millis() > ( RstStartTime + BlinkTime ) ) {
if ( LEDBlinking ) {
digitalWrite( LEDPin, LOW );
LEDBlinking = 0;
} else {
digitalWrite( LEDPin, HIGH );
LEDBlinking = 1;
}
BlinkTime += 500;
}
// If the shutter is 'rested' then start a new exposure
if ( millis() > ( RstStartTime + ( RestTime * 1000 ) ) ) {
if ( CurExp < NumExp ) {
delay( 1000 );
CountOut( 1, ( CurExp + 1 ) );
delay( 1000 );
if ( MirrorLockUp ) {
digitalWrite( ShutterPin, HIGH );
digitalWrite( LEDPin, HIGH );
delay( 500 );
digitalWrite( ShutterPin, LOW );
digitalWrite( LEDPin, LOW );
delay( 125 );
Counter = 0;
while( Counter < 66 ) {
Counter % 2 ? digitalWrite( LEDPin, HIGH ) : digitalWrite( LEDPin, LOW );
delay( 33 );
Counter++;
}
}
digitalWrite( LEDPin, HIGH );
digitalWrite( ShutterPin, HIGH );
ShutterOpen = 1;
ShutterResting = 0;
ExpStartTime = millis();
CurExp++;
} else {
ResetVars();
}
}
}
}
}
void ResetVars() {
Done = 1;
SeqRunning = 0;
ShutterResting = 0;
SeqOnHold = 0;
ShutterOpen = 0;
LEDBlinking = 0;
CurExp = 0;
MirrorLockUp = 0;
}
// This function flashes the LED to tell you how many seconds your
// exposure is and how many exposures you've set. YOu hold down the
// sequencing button for 5 seconds to get a reading
void CountOut( int pot, int value ) {
int hundreds = 0;
int tens = 0;
int ones = 0;
hundreds = value/100;
tens = ( value - ( hundreds * 100 ) ) / 10;
ones = value - ( ( hundreds * 100 ) + ( tens * 10 ) );
if ( ( hundreds == 0 ) && ( ! pot ) ) {
digitalWrite( LEDPin, HIGH );
delay( 10 );
digitalWrite( LEDPin, LOW );
delay ( 100 );
} else {
for ( int l = 0; l < hundreds; l++ ) {
digitalWrite( LEDPin, HIGH );
delay( 500 );
digitalWrite( LEDPin, LOW );
delay( 100 );
}
}
delay( 1000 );
if ( tens == 0 ) {
digitalWrite( LEDPin, HIGH );
delay( 10 );
digitalWrite( LEDPin, LOW );
delay ( 100 );
} else {
for ( int m = 0; m < tens; m++ ) {
digitalWrite( LEDPin, HIGH );
delay( 500 );
digitalWrite( LEDPin, LOW );
delay( 100 );
}
}
delay( 1000 );
if ( ones == 0 ) {
digitalWrite( LEDPin, HIGH );
delay( 10 );
digitalWrite( LEDPin, LOW );
delay ( 100 );
} else {
for ( int n = 0; n < ones; n++ ) {
digitalWrite( LEDPin, HIGH );
delay( 500 );
digitalWrite( LEDPin, LOW );
delay( 100 );
}
}
delay( 1000 );
}
|
|