AVR 2 Pot Timer
A DIY micro-processor controlled shutter controller for Canon Rebel series DSLRs.


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
      Mirror lock-up set
    LED flashes once
      Mirror lock-up un-set
    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


    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 );
    }
      
    
       
      
      
     
     
    
      
    
Board layout, schematic and source code Copyright © 2008, Rick Saunders. All rights reserved

If you modify this code in any way please contact the author on the
Celestar-8 Yahoo Group. If useful your code will be merged and credit given.


Back to Ozzzy's Astronomy Pages