Friday, 8 February 2013

Arduino Remote Trigger for DSLR, stop motion etc.

I have a Canon DSLR camera which uses a 2.5mm stereo phono jack connector to allow for a tethered remote.  These are surprisingly expensive.  http://www.doc-diy.net provides an illustration of the way the connector works.  It is pretty simple to put some electronics behind your camera for stop motion or candid photography.  

I ended up with something looking like this:




It controls the camera to automatically take a photograph within a period of 0-60 seconds or 0-60 minutes, set by a sliding potentiometer.  I can enable or disable the focus control.  I can also switch it to take a photo at a random interval within 0-60 seconds/minutes.  It runs off two 1.5v AAA batteries. 

This post mostly explains the electronics.  The arduino code is provided at the end.  I don't explain my butchery of a cigar box tin.  Read on if you fancy replicating!





The principle:


Image from http://www.doc-diy.net

Using a stereo connector allows you to focus your camera as well as take a shot.   The drawing above shows how it works.  The left most ring is connected to the internal ground of the DSLR camera.   The focus and shutter are connected to pull-up resistors inside the camera.  Shorting either to the ground triggers the actions.  As you see, making a wired push button remote is trivial.  

Concerning some smarter electronics; we don't want to apply any voltage to the DSLR camera, these things are expensive after all.  By this, I mean we don't want to use a microcontroller to directly source or sink the shutter and focus pins of the camera.  So it is best to keep it electrically isolated with relays and make the connections as if they were push buttons.


The Relays:


I raided my electronic scrap and found some Finder 30.22.7.005.0010 relays.  They are conveniently low power, requiring 40mA to activate the relay.  The official Arduino web page says the atmel chip should be able to source 40mA, but that would be working on the upper limit.  So it is best to use a transistor to trigger the relay, and keep the relay a little isolated from the heart of the project.  I found some BC238B npn transistors to use.  Any general purpose npn transistor should suffice.  

Drawn in Fritzing, ignore the relay designation

I used 220 ohm resistors on the base of the transistors.  Remember your flyback diode across the relay activation terminals to nullify the collapse of the electromagnetic field.  I connected the transistors to the digital pins 1 and 2 on the arduino, but they could connect to any.  You want to wire the normally open terminal to your phono jack connector, so that the camera only takes photo graphs when the relay is activated. 

Interface Electronics:


I wanted to keep the interface as simple as possible, so no LCD or seven segment display.  I used 5 light emitting diodes (LEDs) to determine the state of the controller, 2 push buttons to toggle the mode, and a sliding potentiometer to set the time. 

Drawn in Fritzing


The Arduino digital pins 10 to 13 are used as outputs to turn on and off 4 of the LEDs.  Pins 10 and 11 can provide pulse-width modulation (PWM), so we will make this get dimmer as the time to shoot runs down.  The LEDs require current limiting resistors, which are all 220 ohms.  We use the 5 LEDs in the following way:
  1. Power (connected to 5v)
  2. Autofocus (on/off)
  3. Random timing (on/off)
  4. Time in Minutes ( PWM / off)
  5. Time in Seconds ( PWM / off )
The Arduino digital pins 4 and 5 are used as inputs for the 2 push buttons.  It is conventional to add a pull up resistor on a push button, but the Arduino has internal pull-up resistors we can use.  You can activate them by writing a 'high' value to the pin after you have declared it as an input.  One button is used to toggle the Autofocus, the other is used to move between the modes
  1. 'time set in seconds'
  2. 'time set in minutes'
  3. 'time randomly set in seconds'
  4. 'time randomly set in minutes' 
The sliding potentiometer is simply wired between 5v and ground, with the armature pin wired in to an Arduino analog input pin.

Power:


Lastly, we provide power and a power switch to the Arduino.  I used a Polulu 5v Boost regulator to convert my 3v power source into a 5v power source.  It provides up to 200mA (not necessarily 200mA), which should be enough for a few LEDs and two low power relays.  I placed a 22uF capacitor across the power lines to help absorb some noise from the supply and switching relays.  The power switch needs to break the connection to the batteries, isolating all of the electronics.  Remember to tie your grounds together.  

Drawn in Fritzing, the mystery part is a 3 pin dc-dc boost converter.

Arduino Code:


Hopefully this code is pretty self-explanatory:

   
   
 // Global Declarations  
   
 // Milliseconds of relay activation 
 const int FOCUS_TIME = 1000;   
 const int SHOT_TIME = 500;  
   
 // Digital out pins  
 const int FOCUS_LED = 13;  
 const int RANDM_LED = 12;  
 const int SECS_LED = 11;  
 const int MINS_LED = 10;  
   
 // Digital in pins  
 const int FOCUS_BTN = 4;  
 const int MODE_BTN = 5;  
   
 // Analog in pin  
 const int SLIDR_PIN = A3;  
   
 // Digital out pins (relays)  
 const int FOCUS_RLY = 3;  
 const int SHUTR_RLY = 2;  
   
 // Mode values  
 // Easier to read, can still  
 // increment mode (mode++)  
 // with button pushing  
 const int M_MINS = 0;  
 const int M_SECS = 1;  
 const int M_RND_MINS = 2;  
 const int M_RND_SECS = 3;  
 const int MAX_MODE = 3;  
   
 // Set initial mode  
 int mode = M_SECS;  
 boolean autoFocus = true;  
   
 // Set an initial time  
 int secsToShoot = 1000;  
   
 // To track a change in time  
 long lastTime;  
   
 // To randomly choose within a time  
 int timeSpan;  
   
   
   
 // One time set up  
 void setup() {   
    
  randomSeed( A0 );  
  //Serial.begin(9600);  
  //Serial.println("Reset");  
  pinMode( FOCUS_LED, OUTPUT );  
  pinMode( RANDM_LED, OUTPUT );  
  pinMode( SECS_LED, OUTPUT );  
  pinMode( MINS_LED, OUTPUT );  
    
  pinMode( FOCUS_RLY, OUTPUT);  
  pinMode( SHUTR_RLY, OUTPUT);  
  digitalWrite( FOCUS_RLY, LOW);  
  digitalWrite( SHUTR_RLY, LOW);  
    
  pinMode( FOCUS_BTN, INPUT );  
  pinMode( MODE_BTN, INPUT );  
  digitalWrite( FOCUS_BTN, HIGH );  
  digitalWrite( MODE_BTN, HIGH );  
    
  pinMode( SLIDR_PIN, INPUT );  
   
 }  
   
 // Uses the timer to monitor the elapsed time.  
 // Uses spare time to do output/input  
 void loop() {  
    
   checkFocusButton();  
   checkModeButton();  
     
   displayMode();  
   getTime();  
     
   // If a second has passed, decrease our counter  
   if( millis() - lastTime > 1000 ) {  
    lastTime = millis();  
    secsToShoot--;  
    //Serial.println( secsToShoot );  
   }  
     
     
   // If the counter has dropped below 1  
   // take a shot.  
   if( secsToShoot < 1 ) {  
      if( autoFocus == true ) {  
         doFocus();  
      }   
      doShot();  
     
      // We either use the previous time span  
      // or set a new random time span.  
      if( mode == M_SECS || mode == M_MINS ) {  
          secsToShoot = timeSpan;  
      } else if( mode == M_RND_SECS ) {  
          secsToShoot = 1 + random( timeSpan );  
      } else if( mode == M_RND_MINS ) {  
          // ensures more than a minute  
          secsToShoot = random( timeSpan ) + 60;   
      }   
   }  
    
   delay(20);  
}  
   
   
 // Writes out LED values  
 void displayMode() {  
    
  // FOCUS is either true or false  
  digitalWrite( FOCUS_LED, autoFocus );  
     
  // Display the current mode variables.  
  switch( mode ) {  
    case M_SECS:  
      digitalWrite( RANDM_LED, LOW );  
      digitalWrite( MINS_LED, LOW );  
      analogWrite( SECS_LED, map( secsToShoot, 0, timeSpan, 0, 255));  
      break;  
    case M_MINS:  
      digitalWrite( RANDM_LED, LOW );  
      digitalWrite( SECS_LED, LOW );    
      analogWrite( MINS_LED, map( secsToShoot, 0, timeSpan, 0, 255));    
      break;  
   case M_RND_SECS:  
      digitalWrite( RANDM_LED, HIGH );  
      digitalWrite( MINS_LED, LOW );  
      analogWrite( SECS_LED, map( secsToShoot, 0, timeSpan, 0, 255));      
      break;   
   case M_RND_MINS:  
      digitalWrite( RANDM_LED, HIGH );  
      digitalWrite( SECS_LED, LOW );    
      analogWrite( MINS_LED, map( secsToShoot, 0, timeSpan, 0, 255));      
      break;  
   default:  
      break;  
   }  
     
 }  
   
   
 // Reads the time from the sliding pot  
 void getTime() {  
  static int lastReading;  
  int reading = 0;  
    
  reading = analogRead( SLIDR_PIN );  
    
  // Scaling to seconds  
  // Scaling down also helps to remove electrical noise  
  reading = map( reading, 0, 1023, 1, 60 );  
    
  if( mode == M_SECS || mode == M_RND_SECS ) {  
    // nothing   
  } else if( mode == M_MINS || mode == M_RND_MINS ) {  
   // Multiple up to minutes  
   reading *= 10;   
  }   
    
  // We can quit because neither mode nor time has changed.  
  if( reading == lastReading ) return;  
    
  // Else we save the change for the next check  
  lastReading = reading;  
    
  // Set the new time variable  
  // If the slider moves, a new   
  // random time is set  
  timeSpan = reading;  
  if( mode == M_SECS || mode == M_MINS ) {  
   secsToShoot = reading;  
  } else if( mode == M_RND_SECS ) {  
   secsToShoot = random( timeSpan );  
  } else if( mode == M_RND_MINS ) {  
   // ensures more than a minute  
   secsToShoot = random( timeSpan ) + 60;   
  }  
    
 }  
   
   
 // Activates the relay for focus  
 void doFocus() {  
  digitalWrite( FOCUS_RLY, HIGH );  
  delay( FOCUS_TIME );   
  digitalWrite( FOCUS_RLY, LOW);  
 }  
   
 // Activates the relay for shutter  
 void doShot() {  
  digitalWrite( SHUTR_RLY, HIGH );  
  delay( SHOT_TIME );  
  digitalWrite( SHUTR_RLY, LOW );   
 }  
   
   
 // Reads the focus button, only updates  
 // on the initial button press.  
 void checkFocusButton( ) {  
  static int LastButtonState = HIGH;  
  static int ButtonState;  
    
  int reading = digitalRead( FOCUS_BTN );  
    
  if( reading != LastButtonState ) {  
    if( reading == LOW ) autoFocus = !autoFocus;  
      
  }  
  LastButtonState = reading;  
   
  return;   
 }  
   
   
 // Reads the mode button, only updates  
 // on the initial button press.  
 void checkModeButton( ) {  
  static int LastButtonState = HIGH;  
  static int ButtonState;  
    
  int reading = digitalRead( MODE_BTN );  
    
  if( reading != LastButtonState ) {  
    if( reading == LOW ) mode++;  
    if( mode > MAX_MODE ) mode = 0;  
      
  }  
  LastButtonState = reading;  
   
  return;   
 }  
   
   



2 comments:

  1. Do you need a usb host shield for this?
    Thanks

    ReplyDelete
    Replies
    1. Hi. I used a pro mini and an FTDI cable like this one:
      http://www.oomlout.co.uk/usb-serial-cable-33v-p-232.html

      Delete