Back to photostream

Stackduino - Arduino DIY focus stacking controller electronics

This is an early prototype of Stackduino.

 

---------------------------------------------------------------------------------------------------------------------------------

 

 

This is a focus stacking controller for the macro setup: www.flickr.com/photos/reallysmall/5649044078/in/photostream.

 

It uses an arduino, an easydriver and some optocouplers to move a camera a designated distance, take a photo and then repeat the process a chosen number of times until enough image slices have been taken to compile into a stack.

 

It's housed in an old pata hard drive enclosure for convenience as several of the input ports are already provided: power in, power switch and usb port for future reprogramming with updated code.

 

It's a prototype and the mass of wiring makes it look a lot more complicated than it is.

 

1. 12v power in

2. 12v power in on/ off switch

3. Serial breakout port carrying through stepper motor and camera signals

4. USB in - reprogramming port for the arduino

5. Arduino Uno

6. 5v+ and Ground bus

7. Easydriver stepper motor controller

8. Optocoupler camera interface board for triggering focus and shutter remotely

9. Space for battery - future upgrade to run the box outdoors for timelapse footage and similar.

10. Push button - start or stop the stack

11. Rotary encoder - choose step size and number of steps

12. LCD screen

 

Parts List:

 

1x Arduino Uno (Any cheaper clone with a similar number of pins would do)

1x Easydriver v4.3 (Powers and controls the stepper motor using signals from the Arduino. Can also power the Arduino)

2x 4n35 Optocoupler (Allow shutter activation signals to go to camera while keeping it electrically isolated)

4x 330 Ohm resistors, 2 for Optocouplers, 2 for push button and rotary encoder

Stripboard for mounting Optocouplers and collecting together +5v and ground wires

Wire

1x push button switch

1x rotary encoder with dial

1x lcd (serial is easier to implement but parallel is much cheaper with some tweaks to the code)

2x serial ports

1x serial cable to carry stepping and camera signals (straight wiring rather than cross-over is easier as the pins go to the same place at either end)

1x stepper motor (get out of an old printer if you can - the one I bought is massive over-kill for the task)

1x enclosure - in this case an old hard drive caddy

1x piece of usb cable with A type plug to connect arduino to caddy's external usb socket for programming (useful but not essential)

 

1x small electronics project enclosure - acts a junction box on your stacking setup

1x 3.5mm stereo jack - terminates the camera signals from the serial cable

1x stereo lead - carries camera signals from junction box to camera

 

How the stepper motor is hooked up to the stacking setup would vary a lot I guess, I used timing pulleys and belt to connect it to the fine focus of the microscope block.

You'll also need a soldering iron, solder and a multimeter will come in very handy too to diagnose any problems.

 

Making it Better:

 

If I ever get round to doing a second version it will all be consolidated onto one board and probably take up about a third of the space.

 

These arduino variants with prototyping areas might make a great basis for the project...

 

Prototino: www.spikenzielabs.com/Catalog/index.php?main_page=product...

 

MENTA: www.adafruit.com/products/795

 

Freetronics Eleven: www.freetronics.com/products/eleven

 

In this version of the controller the serial enabled lcd is connected directly to the arduino's tx pin. This works fine but there is a small risk that when the arduino is being reprogrammed or starting up it could spam the screen with bad instructions and stop it working. Ideally use the arduino 'software serial' library and relocate the screen connection to a standard digital pin or use a parallel screen instead if you don't mind a bit more soldering work.

 

The Sketch

 

/*

MacroPhotography Focus Stepping Controller

www.flickr.com/photos/reallysmall/5877378677/ in/photostream

Key parts:

Arduino Uno

Easydriver v4.3

Bipolar stepper motor

Momentary push button

Rotary encoder with push button

16 x 2 serial lcd

2 x 4n35 optocoupler for camera connection

Key resources used, including but not limited to:

SLCD library - www.arduino.cc/playground/Code/SLCD

Easydriver Tutorial - danthompsonsblog.blogspot.com/2010/05/easydri ver-42-tutor...

Rotary encoder code - www.circuitsathome.com/mcu/reading-rotary-enc oder-on-arduino

*/

#include // lcd library

//2x16 char display

#define numRows 2 // Display has two rows

#define numCols 16 // Display has 16 columns

SLCD lcd = SLCD(numRows, numCols);

//Rotary encoder

#define ENC_A A1

#define ENC_B A0

#define ENC_PORT PINC

int steps = 5; // No. microns stepper motor should make between pictures, default 5

int numPictures = 10; // No. pictures to take

int loopCounter = 0; // No. pictures taken so far

int pushButton = 2; // Pin 2 = Start/ Stop button

int rotaryButton = 3;// Pin 3 = Rotary encoder push button

int focus = 6; // Pin 6 = Focus the camera

int shutter = 7; // Pin 7 = Take a picture

int dir = 8; // Pin 5 = Stepper motor direction

int doStep = 9; // Pin 8 = Move stepper motor

int toggleLed = 13; // Pin 13 = Switch onboard LED on/off depending on status of toggle button

//pushButton toggle

volatile int buttonState = HIGH; // the current state of the output pin

volatile int reading; // the current reading from the input pin

volatile int previous = LOW; // the previous reading from the input pin

volatile long time = 0; // the last time the output pin was toggled

volatile long debounce = 200; // the debounce time, increase if the output flickers

//rotaryButton toggle

volatile int rbbuttonState = HIGH; // the current state of the output pin

volatile int rbreading; // the current reading from the input pin

volatile int rbprevious = LOW; // the previous reading from the input pin

volatile long rbdebounce = 200; // the debounce time, increase if the output flickers

void setup()

{

Serial.begin(9600);

delay(100);

setDisplayBaudRate(38400); // increase baud rate for better response from lcd to changes in rotary encoder value

delay(100);

Serial.begin(38400);

lcd.init(); // Start lcd display

attachInterrupt(0, buttonChange, CHANGE); // Button on interrupt 0 - pin 2

attachInterrupt(1, rotaryButtonChange, CHANGE); // Rotary encoder on interrupt 1 - pin 3

pinMode(pushButton, INPUT);

pinMode(ENC_A, INPUT);

pinMode(ENC_B, INPUT);

pinMode(dir, OUTPUT);

pinMode(doStep, OUTPUT);

pinMode(focus, OUTPUT);

pinMode(shutter, OUTPUT);

pinMode(toggleLed, OUTPUT);

digitalWrite(focus, LOW);

digitalWrite(shutter, LOW);

digitalWrite(ENC_A, HIGH);

digitalWrite(ENC_B, HIGH);

}

void loop(){

if (buttonState == HIGH){ //stacking setup section

loopCounter = 0;

if (rbbuttonState == HIGH){ //set number of microns to move (steps)

steps = constrain(steps, 1, 250); //limits input step size between 1 and 250 - increase if desired

steps += read_encoder ();

lcd.print("Step size: ", 1, 0);

if (steps < 10){

Serial.print (00, DEC); //adds two leading zeros to single digit Step size numbers on the display

}

if (steps < 100){

Serial.print (0, DEC); http: //adds one leading zero to double digit Step size numbers on the display

}

Serial.print (steps , DEC);

lcd.print("Num steps: ", 0, 0);

if (numPictures < 10){

Serial.print (00, DEC); //adds two leading zeros to single digit Step numbers on the display

}

if (numPictures < 100){

Serial.print (0, DEC); //adds one leading zero to double digit Step numbers on the display

}

Serial.print (numPictures , DEC);

}

else{

numPictures = constrain(numPictures, 10, 250); //set number of pictures to take. Limits between 10 and 250 - change if desired

numPictures += (read_encoder () * 10); //number of pictures changes in increments of 10 for quick selection of large numbers

lcd.print("Step size: ", 1, 0);

if (steps < 10){

Serial.print (00, DEC); //adds two leading zeros to single digit Step size numbers on the display

}

if (steps < 100){

Serial.print (0, DEC); //adds one leading zero to double digit Step size numbers on the display

}

Serial.print (steps , DEC);

lcd.print("Num steps: ", 0, 0);

if (numPictures < 10){

Serial.print (00, DEC); //adds two leading zeros to single digit Step numbers on the display

}

if (numPictures < 100){

Serial.print (0, DEC); //adds one leading zero to double digit Step numbers on the display

}

Serial.print (numPictures , DEC);

}

} //end of stacking setup section

else{

for (int h = 0; h < numPictures; h++){ // loop the following actions for number of times dictated by var numPictures

loopCounter = loopCounter + 1; // optional count of pictures taken so far if reverse to start on end is used

lcd.clear();

lcd.print("Moving ", 0, 1);

Serial.print (steps);

Serial.print (" mns");

lcd.print("Step ", 1, 1);

Serial.print (loopCounter);

Serial.print (" of ");

Serial.print (numPictures);

delay(1000); // Delay required for text above to have time to appear on the screen

{

digitalWrite(dir, LOW); // Set the stepper direction to clockwise

delay(100);

for (int i = 0; i <= steps * 16; i++) // Iterate doStep for number of steps dictated by var encoder0Pos. Multiply steps by 16 as the default settings on the easydriver are 16 microsteps in each full step of the motor

{

digitalWrite(doStep, LOW); // This LOW to HIGH change is what creates the

digitalWrite(doStep, HIGH); // "Rising Edge" so the easydriver knows to when to step

delayMicroseconds(2000); // Delay time between steps, too fast and motor stalls

}

{

lcd.clear();

lcd.print("Settling", 0, 1);

lcd.print("1.5 secs", 1, 1);

delay(1000); // Allow any vibrations from movement to cease before taking a picture

lcd.clear();

lcd.print("Taking picture", 0, 1);

lcd.print("Image ", 1, 1);

Serial.print (loopCounter);

Serial.print ("/ ");

Serial.print (numPictures);

digitalWrite(focus, HIGH); // Trigger camera autofocus - camera may not take picture in some modes if this is not triggered first

digitalWrite(shutter, HIGH); // Trigger camera shutter

delay(400); // Small delay needed for camera to process above signals

digitalWrite(shutter, LOW); // Switch off camera trigger signal

digitalWrite(focus, LOW); // Switch off camera focus signal

delay(4800); //Pause to allow for camera to take picture with 2 sec mirror lockup and to allow flashes to recharge before next shot

lcd.clear();

}

}

if (buttonState == HIGH){

break;

}

}

lcd.print("Stack finished", 0, 1);

/*delay(1000); // uncomment this section to have camera returned to start position when stack is finished

lcd.clear();

digitalWrite(dir, HIGH); // Set the stepper direction to anti-clockwise

delay(100);

lcd.print("Returning...", 0, 1);

int totalSteps = steps * numPictures;

int partialSteps = steps * loopCounter;

int returnSteps = min(totalSteps, partialSteps);

lcd.print ("< rbdebounce) {

if (rbbuttonState == HIGH)

rbbuttonState = LOW;

else

rbbuttonState = HIGH;

time = millis();

}

digitalWrite(toggleLed, rbbuttonState);

rbprevious = rbreading;

}

/* returns change in encoder state (-1,0,1) */

int8_t read_encoder()

{

static int8_t enc_states[] = {

0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0 };

static uint8_t old_AB = 0;

/**/

old_AB <<= 2; //remember previous state

old_AB |= ( ENC_PORT & 0x03 ); //add current state

return ( enc_states[( old_AB & 0x0f )]);

}

void setDisplayBaudRate(int baudrate) { //function to change baud rate of lcd

Serial.print(0x7C, BYTE); // command byte

switch (baudrate) {

case 2400:

Serial.print(0x0B, BYTE); // Ctrl^K

break;

case 4800:

Serial.print(0x0C, BYTE); // Ctrl^L

break;

case 9600:

Serial.print(0x0D, BYTE); // Ctrl^M

break;

case 14400:

Serial.print(0x0E, BYTE); // Ctrl^N

break;

case 19200:

Serial.print(0x0F, BYTE); // Ctrl^O

break;

case 38400:

Serial.print(0x10, BYTE); // Ctrl^P

break;

default:

Serial.print(0x12, BYTE); // reset to 9600, Ctrl^R

}

}

40,813 views
25 faves
40 comments
Uploaded on June 27, 2011
Taken on April 28, 2011