Go Down

Topic: Intervalometer Code (and design!) Submitted for Peer Review (Read 8366 times) previous topic - next topic

KenF

do you suggest keeping my transistor in place along with your diode/resistor arrangement?
No.  Personally I think the transistor is redundant.

The resistor is probably also redundant, but I'd leave it there as a bit of bullet proofing. 

Fortunately, the pulses you send to set off the shutter are likely very short, so you've probably got away with it thus far.  I wouldn't leave it like that on a long term basis though.

nschoonover

What about this setup? Here I have made the changes you suggest for the shutter pin, and I have utilized Digital pin 13 to run the LCD backlight. It does flicker during startup, but once the program is running the controls work well. It shuts off the LCD to conserve battery power during the exposure cycle.


Parts Used:
Arduino Uno (Rev3)
LCD screen 16x2
Rotary Encoder w/ switch
Push button
Diode
Ceramic Capacitor .22uF
Ceramic Capacitor .22uF
Potentiometer 10kΩ
330Ω Resistor
20kΩ Resistor
10kΩ Resistor
LED (shutter simulate)

I also updated the code to reflect these hardware changes. I am eagerly awaiting my LCD backpack which (I hope) will drastically cut down on the number of pins I am using.

As usual, I welcome any comments or feedback. I really, really don't want to ruin my camera. I think this setup is safe, but I would rather be positive! Thanks!
Nate

nschoonover

Can someone tell me why I am using resistors and capacitors for debouncing the rotary encoder/switch? I know that is what they are there for, I just don't understand how the values are chosen.

KenF

I have utilized Digital pin 13 to run the LCD backlight. It does flicker during startup, but once the program is running the controls work well.
Pin 13 is pulsed by the bootloader during startup just to let you know all is well.  Naturally this behaviour is having a minor impact on your backlight.  You can either just accept it or pick another pin. 

Can someone tell me why I am using resistors and capacitors for debouncing the rotary encoder/switch? I know that is what they are there for, I just don't understand how the values are chosen.
Encoders are just a pair of switches arranged so that they turn on and off at the appropriate moments.  All switches have some bounce where the contacts make/break a few times before finally staying on properly.  This bouncing is usually over within about 50 microseconds so is un noticeable to the casual obserer.  Your uno, however, is fast enough to percieve these bounces as if they are separate events. 

As the switch goes from one state to the other, it's output is temporarily counteracted by the charge on the capacitor. 

When the switch closes, the current on the output of the switch will be swallowed by the capacitor until it's charged.  By that time any bouncing on the contacts will be complete.

When the switch opens, the capacitor will feed the circuit for a moment longer, until it is finally discharged.  By this time, the contacts on the switch are now well separated so there will be no further bounce.

The charge/discharge rate of the capacitor will determine how much "debounce" is achieved.  As an example, if the capacitor value were too big, your uno wouldn't notice the switch turning on until it has been active for a significant amount of time.  In an extreme case, that could be longer than the time the switch is actually active.

The rate of charge/discharge is dictated by the resistor and by the current drawn by the circuit.  The research involved in determining these factors and then doing the calculations is more trouble than it's worth when someone else has done the job already.  Besides the EXACT amount of debounce is not critical, so if the values look about right, they'll generally work.

Without the hardware debounce your encoder could still be debouned by software within your sketch.  A simple (very short) delay whenever a pulse is detected, will do the job fine. So they're not essential, but a nice touch.

nschoonover

Thanks again Ken! :)
This is just the concise, informed answer that I was looking for!

I am fine with the flashing for now. Once I clear up some digital pins I can switch it. Do you think I can utilize the analog pins for any of my inputs? (Possibly digital pins 2-5 to analog pins 0-3)? And what would the implications of this change be?

I did start with just a software debounce using a delay of about 20ms. It makes sense that the caps even out the current, creating an effective hardware delay. I wonder, can I remove the caps and use a longer software delay?

I have lots of capacitors, and some may fit the final build better than the current ones. Their values could be significantly different. I am not sure how to understand the range of capacitance values that I could use to achieve the debounce. Does choosing a polarize cap make any difference here?

I can see this debouncing is a whole separate conversation, so I will take up the research aside. If I make any significant changes as a result I will post them to the final build.

Thanks for your help! :)
Nate

KenF

Yes you can use any of the analog pins as digital inputs.  When you're using pinMode, digitalRead, or digitalWrite, On an uno you can call them pins 14 through to 19 within your code, but I believe you can still use the names A0 - A5 and it will still work.

Yes it's easy to get too obsessive about debounce.  For a simple push button that is going to be used by a human, I'll generally just stick a delay(50); whenever a key press is detected.  If there's no timing critical events going on, it's a no brainer. 

When time is a consideration (such as within ISRs) I tend to use another approach, rather than working out how long the debounce NEEDS to be I'll consider just how close legitimate triggers can be.  For instance if it's a pulse from an engine that has a maximum speed of 6000 RPM then that's a maximum of 100 pulses per second, so any pulse that arrives less than 10 ms from the previous pulse must be bounce.  So I'll simply ignore any pulse that is received within that time of the previous one.  Not only does this keep it simple, it also allows for some pretty badly worn components to continue to give good results.

nschoonover

I think that I am forced to use digital pins 2 and 3 for the clock and data pins on my rotary encode because they create interrupts and are handled by the ISR portion. I tried moving these pins to other digital pins and it wouldn't work.

My rotary encoder has 20 notches, and I want the values to change as rapidly as possible. When you have to dial in 5000 pictures in increments of 10 (as I have currently coded) it takes forever turning the little encoder. I have a big wheel knob on the way that will help with that. But, could I remove my hardware debounce, and get faster results with just a software debounce?  If I turn the encoder too fast it either ignores the input, increases only a little, or actually goes backwards. This could be in part due to the cheap encoder i have, but it seems like I should be able to get better response from it.

Thanks as usual Ken! (Does this forum have any other users?) :P
Nate

KenF

The hardware debounce may be causing you more trouble than it's worth.  If you post the code you're using (don't forget to enclose it in [code] and [/code] tags) I could probably give you the necessary to debounce it in software.  Then you could remove those caps and resistors.  Sounds like it may be a more reliable way to go.

BTW How many pulses per second are you expecting?

nschoonover

That's a good way to look at it!
If there are 20 clicks in a full revolution, I can turn about halfway or 3/4 in a second. So, 10-15 pulses a second, but with fine tune control for just a few pulses a second.

Here are the extracted bits of code relevant to the rotary encoder. I posted the full program as an attachment a couple of posts back. I harvested this code from this guy: http://forum.arduino.cc/index.php?topic=242356.0


Code: [Select]

// Rotary Encoder Variables
volatile boolean TurnDetected;          // TODO! Learn what a volatile is!
volatile boolean up;
const int PinCLK = 2;                   // Used for generating interrupts using CLK signal
const int PinDT = 3;                    // Used for reading DT signal
const int PinSW = 4;                    // Used for the push button switch
int wheelRelease = 1;          // Set initial wheel click state to up/not clicked.

// ---------------------------------------------------------------------------------------
// Interrupt service routine
// ---------------------------------------------------------------------------------------
void isr () //  Interrupt service routine used for Rotary Encoder
{
  up = !digitalRead(PinDT);
  TurnDetected = true; // Interrupt service routine is executed when a HIGH to LOW transition is detected on CLK
}

void setup() {
// Setup the Rotary Encoder
  pinMode(PinCLK,INPUT);
  pinMode(PinDT,INPUT); 
  pinMode(PinSW,INPUT);
  attachInterrupt (0,isr,FALLING);   // interrupt 0 is always connected to pin 2 on Arduino UNO
 
}


void loop()
{
  //Serial.println("Loop clickState = " + String(clickState));
  if (!digitalRead(PinSW)) // Check if wheel is down - this starts the input procedure.
  { 
   delay(2);           
    if (wheelRelease == 1 ) // Wheel was previously up
     {
       clickState++;
    //   Serial.println("Set clickState = " + String(clickState));
       wheelRelease = 0; // Wheel is down 
       clearonce = 0;
     }
  }       

  else // Wheel click is up
  {
    if (wheelRelease == 0) wheelRelease = 1;
  } 
   
  if (clickState >= 7)  // Reset the variables if user clicks past the Go! prompt.
  { 
  //  Serial.println("Reset variables!");
     activeState = 0;   // Deactivate the timelapse state.
     clickState = 1;    // Set clickState to start of menu.
     qtyPics = 0;       // Set minimum pictures to 1.
     startDelay = 0;    // Set minimum start delay to 0.       
     shotDelay = 0;     // Set minimum shot delay to 0.       
     exposureMSecs = 0; // Set minimum exposure to 1ms.
     remStartDelay = 0; // Reset remaining start delay counter.
     remShotDelay = 0;  // Reset remaining shot delay counter.
  } 
 
if (activeState == 0)  // Not in anactive state, so collect input from the user.
{
  if (TurnDetected)    // A turn was detected on the rotary encoder
  {    
    delay(2);
    if (up) // Turn up
    {
  //    Serial.println("Wheel turned up!");
      switch (clickState)
      {
        case 1:
          // Ignore the wheel until the next page
          break;
        case 2:
          // Increase exposureMSecs
          clearonce = 0;
          exposureMSecs = exposureMSecs + 10;
     //     Serial.println("exposureMSecs = " + String(exposureMSecs));
          if (exposureMSecs >= (expMax + 1)) exposureMSecs = expMax; // The maximum value
          break;
        case 3:
          // Increase shotDelay
          clearonce = 0;
          shotDelay = shotDelay + 1 ;
     //     Serial.println("shotDelay = " + String(shotDelay));
          if (shotDelay >= (shotDelayMax + 1)) shotDelay = shotDelayMax; // The maximum value
          break;
        case 4:
          // Increase startDelay
          clearonce = 0;
          startDelay = startDelay + 1;
      //    Serial.println("startDelay = " + String(startDelay));
          if (startDelay >= (startDelayMax + 1)) startDelay = startDelayMax; // The maximum value
          break;
        case 5:
          // Increase qtyPics
          clearonce = 0;
          qtyPics = qtyPics + 10;
     //     Serial.println("qtyPics = " + String(qtyPics));
          if (qtyPics >= (qtyMax + 1)) qtyPics = qtyMax; // The maximum value         
          break;
        case 6:
          // Ignore wheel input on Ready? page
          break;
      }
    } 
    else // Turn down
    {
    //  Serial.println("Wheel turned down!");
      switch (clickState)
      {
        case 1:
          // Ignore the wheel until the next page
          break;
        case 2:
          // Decrease exposureMSecs
          clearonce = 0;
          exposureMSecs = exposureMSecs - 10;   
    //      Serial.println("exposureMSecs = " + String(exposureMSecs));
          if (exposureMSecs <= 0) exposureMSecs = 1; // The minimum value
          break;
        case 3:
          // Decrease shotDelay
          clearonce = 0;
          shotDelay = shotDelay - 1;
      //    Serial.println("shotDelay = " + String(shotDelay));
          if (shotDelay <= 0) shotDelay = 0; // The minimum value       
          break;
        case 4:
          // Decrease startDelay
          clearonce = 0;
          startDelay = startDelay - 1;
      //    Serial.println("startDelay = " + String(startDelay));
          if (startDelay <= 0) startDelay = 0; // The minimum value     
          break;
        case 5:
          // Decrease qtyPics
          clearonce = 0;
          qtyPics = qtyPics - 10;
     //     Serial.println("qtyPics = " + String(qtyPics));
          if (qtyPics <= 0) qtyPics = 1; // The minimum value
          break;
        case 6:
          // Ignore wheel input on Ready? page
          break;
      }
    } 
    TurnDetected = false;          // Do NOT repeat IF loop until new rotation detected   
  }

}


Thanks again for your help!
Nate

nschoonover

I got my i2c backpack and put it to use! Attached is the updated code, and the image shows the updated wiring diagram.

I also removed the .22uF caps and replaced them with a debounce variable delay in the code. I left the resistor in the encoder switch button, but also gave it the same debounce variable.

The setup seems stable, however, when turning the encoder quickly the values barely increment at all. A slow steady turn is more effective. Also, when I turn down the encoder, it still registers the turn as a turn up. I think there is something wrong with my encoder section or the hardware. What do you think?

Thanks again for your feedback!
Nate

nschoonover

I'm not too sure about your transistor switching arrangement.  I believe that the canon uses 3.3v logic so needs to be protected from the arduinos 5v.  Also there should be a current limiting resistor on the feed to that base transistor.  Otherwise, you can potentially damage the arduino.

Here's my suggestion for the interface to the camera shutter input.  The diode does the job of protecting the camera from the arduino and the resistor (probably redundant here) is to protect the arduino from having too much current drawn from it's I/O pin.

BTW in this configuration the output from the arduino will be active LOW (ie digitalWrite(10, LOW) will activate the shutter.
Hey Ken, I am having trouble implementing the diode/resistor setup. I need to ground the shutter pin to take a picture, which is why I had the transistor there to ground the shutter when it received a signal from pin 10. In the diode setup you showed, pin 10 connects directly to the shutter pin (through the diode/resistor), but it never grounds the shutter.

Am I missing something obvious? I am very close to a full solution and will post my build once I have a fully working device.

I am still having trouble debouncing my rotary encoder too if you have any thoughts on that.
Thanks! :)
Nate

nschoonover

I found this example using a rotary encoder, and it seems to have good response. What are the relative benefits of using this encoder method over the ISR method I am currently using? The ISR code is simpler, but this code is also very easy to follow, and it doesn't rely on interrupts.
http://www.hobbytronics.co.uk/arduino-tutorial6-rotary-encoder


I am also considering going back to the transistor method of triggering the shutter. As it is the only thing that is making sense to me right now. I will still use the resistor/diode to protect my camera and the Uno.

Please let me know if you have any ideas! Thanks! :)
Nate

nschoonover

#27
Jan 20, 2015, 01:59 am Last Edit: Jan 20, 2015, 02:04 am by nschoonover
Here it is! This is the complete working device with wiring diagram and code! I still have some tweaking to do with the rotary encoder, but everything works like a charm! I wish I had planned ahead for the battery. I will velcro it to the side or something.

Sorry Ken, I dropped the diode because it was causing me trouble. The transistor seems to work great. I could still be convinced to add it back in for safety.

Please keep the ideas coming! I can't wait to put my new toy to use!
Thanks for your help!
Nate

nschoonover


AlxDroidDev

Are you familiar with the Magic Lantern Project?  I think you'll find that you can achieve everything you want with that, without any extra hardware (except an SD card).

It was actually a spin off from the film industry and offers more features than you can shake a stick at.  Even if you still opt to go with the arduino, I think you'll find that magic lantern will offer you more control than you'd have believed possible.
I was about to post the exact same suggestion. Magic Lantern is a lot better than the original Canon firmware, and the chance to risk damaging the hardware is nil.

If Magic Lantern wasn't a choice, then using an Arduino would be a great choice, but since there is Magic Lantern for the T3i (aka 600D), using an Arduino is overkill.
Some of my projects:
Shield for DS1337+, DS1624 and AT24C1024B (RTC, temp & mem): http://forum.arduino.cc/index.php/topic,126197.0.html
CHDK Camera remote shutter (BT, IR, USB): http://forum.arduino.cc/index.php?topic=295377.0

Go Up