Control box for a focus rail, and other stepping motor-driven camera moving devices. The program.

With some reluctance, I have decided to upload the Arduino code that I use to drive the new electronics that I use with my focus rail. As I said in the previous post, it can be used with up to five stepping motor-driven devices. Thus, it can be used to move a camera, or a specimen one is photographing, along X, Y and Z axes as well as providing the facility to rotate the camera or specimen around the A & B axes. The control box incorporates 8 outputs (configured as 4 pairs) that can be used to trip the shutters of cameras, prefocus, or operate strobes. The box also has provision for up to 8 analogue inputs. However, these are at the present time, unused. Currently I have only the one focus rail and a turntable with which to test the box. Other bits are in the process of construction. The control box utilises an Arduino Mega, which with its provision for lots of digital outs and analogue ins, provides almost infinite scope for expansion.

The hardware consists of the aforementioned Mega, a 4 line LED display driven by an I2C module via the SDA and SLC pins on the Arduino, EasyDriver boards to drive the stepping motors, a rotary encoder with a push-button switch, and the little optocoupler boards pictured below to trigger the cameras etc. The project is housed in a die-caste aluminium box and all the connections bar those to external switches and jacks are made using DuPont connectors.  I will at some point, if I can find the time, draw up a wiring diagram. However, in many ways, this should not be necessary because the programme contains a list of all the pin connections necessary to make the different circuits work together apart from the SDA and SLC connections mentioned above.

The menu system is scrolled by the rotary encoder and operating the integral push button activates the selected menu item. The  short video below (a new one!) shows this system in operation. I brought the Arduino’s reset pin out to an external pushbutton to make it easy to resent the system should anything go wrong. I added a switch to turn off the power supply to the motors so that one can test the programme without disconnecting them. I also cut a hole in the side of the box to allow me to program (reprogram, reprogram and reprogam again and again!)  the board in situ via its USB socket.

My reluctance in publishing the code is two-fold. Firstly, I do not consider myself in any way to be an Arduino expert – my competence has grown, but I remain a complete novice by comparison to the gurus! Secondly, the code is a work in prgress and likely always will be! I am sure there are lots of errors, however, I have been using it to take pictures so, in practice it works for me. If anyone finds the code at all useful then I will be very happy. However, I do ask that should you pass it on, or use chunks, you acknowledge the source (this blog) so there is a chance that I can keep a handle on the development of the code and put right any errors  etc. Unfortunately, WordPress do not make it that easy to publish your code exactly as one would see it in the Arduino Editor. However, I am hopeful people should be able to cut and past the code presented on this page into an editor – look out for lines that have scrolled, and other formatting errors that have been produced by incorporating the code into these WordPress pages. It looks OK but beware of potential problems. I am thinking of placing the code on Github or elsewhere – news on this another time. There are about 1000 lines altogether.

Before the code here are a few pictures to help explain the components I have used.

img_3018

The box with its four line display. The red button is an external reset. The RHS carries the 4 stereo jack sockets for the outputs to the camera and flashes and also sockets for the as yet  unused sensor inputs. 4-pin aviation sockets are used for connecting the motors. There is a motor power socket and switch on the LHS, and an access hole for a USB plug.

img_3017

The two types of circuit board used inside the box other than the Arduino Mega. The little optocoupler boards are available from China for next to nothing on eBay. The EasyDriver boards are also very cheap. You will need to solder on some header pins to connect the boards to the Mega using DuPont connectors. My box currently contains three EasyDrivers and two quad optocoupler boards.

This short video shows the options currently available from the rotary encoder-based menu.

Here is the program – don’t forget there is a sideways scroll bar at the bottom of the page!

//STEPPER BOX 2 Begun 04/October 2016
// Global variables to have capital letters
//#define ENCODER_OPTIMIZE_INTERUPTS // Use if time overhead is crucial
#include <Encoder.h>
#include <LiquidCrystal_I2C.h>
Encoder myencoder(2, 3); // both of these are interupt pins - gives highest fidelity
int Button_pin = 13;// pin that connects to encoder switch/button
int Upper_limit = 8; // initial max for encoder
int Lower_limit = 1; // initial min for encoder
int Increment = 10; // initial value for multilier re: durations, distance etc
int Direction; // intial direct rail will move 0 = forward
long int Distance;
int Number_photos;
int Delay_1;// Time over which railmovement settles down
int Delay_2;// Exposure time during which rail halts
int Dir_pin = 11; // Set initial pins to those for Z axes
int Drive_pin = 12; // initial pin for Z stepper motion
int Enable_pin = 10; // initial pin to enable Z stepper motor
int Camera_pin = 16;// connected to camera remote socket - camera may need another pin for prefocus - say 15 on same jack socket
int Flash_pin = 17;// connects to flash socket - rename and initialise if using more than one flash - ie Flash_2_pin = 19
int Motor_selected = 3; //start with Z motor selected
int Number_Photos_hold;
int Direction_hold;
int Distance_hold;
int Delay_1_hold;
int Delay_2_hold;
int Delay_shutter_lag = 100; // sets period required to activate camera
byte Reuse_Z_values;
byte Button_flag; // flag for button press
byte Old_button_flag = HIGH; // carry old value of button forward

String Main_Menu_Title = " --MAIN MENU-- ";
// 12345678901234567890
String Option_1 = " Stack ";
String Option_2 = " Jog ";
String Option_3 = " Rotate ";
String Option_4 = " Shoot ";
String Option_5 = " Flash ";
String Option_6 = " Shoot & flash ";
String Option_7 = " Motor - X,Y,Z,A,B ";
String Option_8 = "Distance multiplier ";
String Explain_1 = " (Create Z stack) ";
String Explain_2 = " (Jog back/forward)";
String Explain_3 = " (Control A,B Axes)";
String Explain_4 = " (Fire w/out flash)";
String Explain_5 = " (Fire flash) ";
String Explain_6 = " (Fire and flash) ";
String Explain_7 = " (Select motor) ";
String Explain_8 = "(Click sensitivity)";
LiquidCrystal_I2C lcd(0x3F, 20, 4); // set address for LCD on I2C
void setup()
{
pinMode(10, OUTPUT);// set all motor enable pins as outputs
pinMode(20, OUTPUT);
pinMode(23, OUTPUT);
pinMode(29, OUTPUT);
pinMode(32, OUTPUT);
digitalWrite(10, HIGH); // set all motor enable pins to off = HIGH
digitalWrite(20, HIGH);
digitalWrite(23, HIGH);
digitalWrite(29, HIGH);
digitalWrite(32, HIGH);
pinMode(15, OUTPUT);//configure current flash and camera pins as outputs
pinMode(16, OUTPUT);
pinMode(17, OUTPUT);
pinMode(18, OUTPUT);
// others as one wishes
pinMode (Button_pin, INPUT_PULLUP);
lcd.init();
lcd.clear();
lcd.backlight();
lcd.setCursor(0, 0);
// 12345678901234567890
lcd.print(" M A D B O F F I N ");
lcd.setCursor(0, 1);
lcd.print(" L A B S ");
lcd.setCursor(0, 2);
lcd.print("Software version 1.3");
lcd.setCursor(0, 3);
lcd.print(" 28th October 2016 ");
delay(4000);
lcd.clear();
}

void loop()

{
lcd.setCursor(0, 0);
lcd.print(Main_Menu_Title);
int k = encode_return(Upper_limit, Lower_limit); // call subroutine with upper and lower limits, k holds value returned from encoder
// Spell out menu options using encoder to select
switch (k) {
case 1:
lcd.setCursor(0, 1);
lcd.print(Option_1);
lcd.setCursor(0, 2);
lcd.print(Explain_1);
break;
case 2:
lcd.setCursor(0, 1);
lcd.print(Option_2);
lcd.setCursor(0, 2);
lcd.print(Explain_2);
break;
case 3:
lcd.setCursor(0, 1);
lcd.print(Option_3);
lcd.setCursor(0, 2);
lcd.print(Explain_3);
break;
case 4:
lcd.setCursor(0, 1);
lcd.print(Option_4);
lcd.setCursor(0, 2);
lcd.print(Explain_4);
break;
case 5:
lcd.setCursor(0, 1);
lcd.print(Option_5);
lcd.setCursor(0, 2);
lcd.print(Explain_5);
break;
case 6:
lcd.setCursor(0, 1);
lcd.print(Option_6);
lcd.setCursor(0, 2);
lcd.print(Explain_6);
break;
case 7:
lcd.setCursor(0, 1);
lcd.print(Option_7);
lcd.setCursor(0, 2);
lcd.print(Explain_7);
break;
case 8:
lcd.setCursor(0, 1);
lcd.print(Option_8);
lcd.setCursor(0, 2);
lcd.print(Explain_8);
break;
default:
;
}
Button_flag = digitalRead(Button_pin);
if (Button_flag != Old_button_flag)
{
Old_button_flag = Button_flag;
delay(100);
}
if (Button_flag == LOW && k == 1)
{
stacker();
}
else if (Button_flag == LOW && k == 2)
{
jogger();
}
else if (Button_flag == LOW && k == 3)
{
rotate();
}
else if (Button_flag == LOW && k == 4)
{
shoot();
}
else if (Button_flag == LOW && k == 5)
{
flash();
}
else if (Button_flag == LOW && k == 6)
{
shoot_flash();
}
else if (Button_flag == LOW && k == 7)
{
select_motor();
}
else if (Button_flag == LOW && k == 8)
{
click_Increment();
}
}

//
// SUBROUTINE encode_return () is sent upper and lower limits and returns encoder_value
//

int encode_return (int Upper_limit, int Lower_limit) {
int new_encoder_value = -999;
int mult_enc;
new_encoder_value = myencoder.read();
new_encoder_value = new_encoder_value / 4; // because each click increases encoder value by 4
// now test to see if limits exceeded
if (new_encoder_value >= Upper_limit) {
new_encoder_value = Upper_limit;
mult_enc = (Upper_limit * 4); //not clear why "if" not blocking code here????? add a 2...take it out or not??!
myencoder.write(mult_enc);
}
if (new_encoder_value <= Lower_limit) { new_encoder_value = Lower_limit; mult_enc = (Lower_limit * 4) + 2; //add 2 to stop "if" blocking code myencoder.write(mult_enc); } return new_encoder_value; } // // SUBROUTINE stacker () creates a stack of photos // void stacker() { Direction = 1; Number_photos = 1; Distance = 0; Delay_1 = 0; Delay_2 = 0; byte Old_button_flag = HIGH; byte Button_flag = HIGH; if (Motor_selected > 3)
{
lcd.clear();
// 01234567890123456789
lcd.setCursor(0,0);
lcd.print("--------------------");
lcd.setCursor(0,1);
lcd.print("Use X, Y or Z motor ");
lcd.setCursor(0,2);
lcd.print("Choose when offered!");
lcd.setCursor(0,3);
lcd.print("--------------------");
delay(1500);
lcd.clear();
select_motor();
}
lcd.clear();
lcd.setCursor(0, 0);
// 12345678901234567890
lcd.print("--STACKER SUB-MENU--");
//
// do while loop here for direction to run
//
lcd.setCursor(0, 1);
// 12345678901234567890
lcd.print(" (Direction) ");
if (Reuse_Z_values == HIGH)
{
myencoder.write(Direction_hold * 4);
}
do
{
Direction = encode_return(2, 1);
if (Direction == 1)
{
lcd.setCursor(0, 2);
lcd.print(" **Forward** ");
}
if (Direction == 2)
{
lcd.setCursor(0, 2);
lcd.print(" **Reverse** ");
}
Button_flag = digitalRead(Button_pin);
if (Button_flag != Old_button_flag)
{
Old_button_flag = Button_flag;
delay(30);
}
} while (Button_flag == HIGH);
Direction_hold = Direction;
myencoder.write(4); // return to a value of 1 for encoder - divide by four!
delay(200); // inserting this delay ends problems with 'bouncing by' setting number of photos
// 12345678901234567890
// do while loop here for number of photos
Old_button_flag = HIGH;
Button_flag = HIGH;
lcd.setCursor(0, 1);
// 12345678901234567890
lcd.print(" Number of Photos: ");
lcd.setCursor(0, 2);
lcd.print(" ");
if (Reuse_Z_values == HIGH)
{
myencoder.write(Number_Photos_hold * 4);
}
do
{
Number_photos = encode_return(1000, 1);
lcd.setCursor(10, 2);
lcd.print(Number_photos);
lcd.print(" ");
Button_flag = digitalRead(Button_pin);
if (Button_flag != Old_button_flag)
{
Old_button_flag = Button_flag;
delay(30);
}
} while (Button_flag == HIGH);
Number_Photos_hold = Number_photos;
myencoder.write(4); // return to a value of 1 for encoder - divide by four!
delay(200); // inserting this delay ends problems with 'bouncing by' setting distance to run
//
// do while loop here for distance to run
//
lcd.setCursor(0, 1);
// 12345678901234567890
lcd.print(" Distance (um): ");
if (Reuse_Z_values == HIGH)
{
myencoder.write(Distance_hold * 4);
}
do
{
Distance = encode_return(10000, 1); // um could be a problem for longer distances!!
Distance = Distance * Increment;
//lcd.print(" ");
lcd.setCursor(9, 2);
lcd.print(Distance);
lcd.print(" ");
Button_flag = digitalRead(Button_pin);
if (Button_flag != Old_button_flag)
{
Old_button_flag = Button_flag;
delay(30);
}
} while (Button_flag == HIGH);
Distance_hold = Distance / Increment;
myencoder.write(4);
delay(200); // inserting this delay ends problems with 'bouncing by'
// 12345678901234567890
// do while loop here for delay to settle rail

lcd.setCursor(0, 1);
// 12345678901234567890
lcd.print("Delay - settle (ms):");
if (Reuse_Z_values == HIGH)
{
myencoder.write(Delay_1_hold * 4);
}
do
{
Delay_1 = encode_return(9999, 1);
Delay_1 = Delay_1 * Increment;
lcd.setCursor(9, 2);
lcd.print(Delay_1);
lcd.print(" ");
Button_flag = digitalRead(Button_pin);
if (Button_flag != Old_button_flag)
{
Old_button_flag = Button_flag;
delay(30);
}
} while (Button_flag == HIGH);
Delay_1_hold = Delay_1 / Increment;
myencoder.write(4); // return to a value of 1 for encoder - divide by four!
delay(200); // inserting this delay ends problems with 'bouncing by'

// 12345678901234567890
// do while loop here for delay for exposure i.e. after tripping shutter

lcd.setCursor(0, 1);
lcd.print("Delay - expose (ms):");
if (Reuse_Z_values == HIGH)
{
myencoder.write(Delay_2_hold * 4);
}
do
{
Delay_2 = encode_return(9999, 1);
Delay_2 = Delay_2 * Increment;
lcd.setCursor(9, 2);
lcd.print(Delay_2);
lcd.print(" ");
Button_flag = digitalRead(Button_pin);
if (Button_flag != Old_button_flag)
{
Old_button_flag = Button_flag;
delay(30);
}
} while (Button_flag == HIGH);
Delay_2_hold = Delay_2 / Increment;
myencoder.write(4); // return to a value of 1 for encoder - divide by four!
lcd.clear();
lcd.setCursor(0, 0);
// 12345678901234567890
lcd.print("Photos: ");
lcd.print(Number_photos);
lcd.setCursor(0, 1);
lcd.print("Distance: ");
lcd.print(Distance);
lcd.setCursor(0, 2);
lcd.print("Delay (settle): ");
lcd.print(Delay_1);
lcd.setCursor(0, 3);
lcd.print("Delay (expose): ");
lcd.print(Delay_2);
delay(2000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("--------------------");
lcd.setCursor(0, 1);
// 12345678901234567890
lcd.print(" ACCEPT VALUES? ");
lcd.setCursor(0, 2);
lcd.print("Press in 2 sec = YES");
lcd.setCursor(0, 3);
lcd.print("--------------------");
Button_flag = HIGH;
for (int i = 0; i <= 1000; i++) {
Button_flag = digitalRead(Button_pin);
delay(20);
if (Button_flag == LOW)
{
break;
}
}
if (Button_flag == LOW)
{
motor_driver();
}
lcd.clear();
lcd.setCursor(0, 0);
// 01234567890123456789
lcd.print("--------------------");
lcd.setCursor(0, 1);
lcd.print(" Finished Z stack! ");
lcd.setCursor(0, 2);
lcd.print("--------------------");
delay(2000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("--------------------");
lcd.setCursor(0, 1);
// 12345678901234567890
lcd.print("REUSE STACK VALUES? ");
lcd.setCursor(0, 2);
lcd.print("Press in 2 sec = YES");
lcd.setCursor(0, 3);
lcd.print("--------------------");
Reuse_Z_values = LOW;
Button_flag = HIGH;
for (int i = 0; i <= 200; i++) {
Button_flag = digitalRead(Button_pin);
delay(20);
if (Button_flag == LOW)
{
Reuse_Z_values = HIGH;
break;
}
}
lcd.clear();
return;
}

//
//
// SUBROUTINE motor_driver () moves the motor via EasyDriver and activates camera to produce a photo stack
//
//

int motor_driver()
{
// This value is crucial for absolute accuracy and needs to be set by moving the rail and seeing how far it actually travels.
float steps_per_micron = 0.78824; //1.25mm is 1600 steps (with 8 microsteps per step) so 0.78125 steps/micron!
long int steps_between_photos;
long int distance_between_photos;
long int total_steps;
int carry_direction;
pinMode(Dir_pin, OUTPUT);
pinMode(Drive_pin, OUTPUT);
pinMode(Camera_pin, OUTPUT);
pinMode(Flash_pin, OUTPUT);
pinMode(Enable_pin, OUTPUT);
digitalWrite(Dir_pin, LOW);
digitalWrite(Drive_pin, LOW);
digitalWrite(Enable_pin, LOW);
distance_between_photos = Distance / (Number_photos - 1);
steps_between_photos = (float)distance_between_photos / steps_per_micron; // check loss of precision!
lcd.clear();
lcd.setCursor(0, 0);
lcd.clear();
lcd.setCursor(0, 0);
// 12345678901234567890
lcd.print("Caution motor active");
lcd.setCursor(0, 1);
lcd.print("Creating Z stack of:");
lcd.setCursor(9, 2);
lcd.print(Number_photos);
lcd.setCursor(7, 3);
lcd.print("Photos");
delay(1500);

//head off in the right direction!
if (Direction == 2) {
digitalWrite(Dir_pin, LOW);
}
if (Direction == 1) {
digitalWrite(Dir_pin, HIGH);
}
//set Camera_pin high - first photo is where the rail is now
//tell operator taking first photo
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Creating Z stack! ");
lcd.setCursor(0, 1);
// 1234567890123456890
lcd.print("Photo #: 1 ");
lcd.setCursor(0, 2);
lcd.print("In stack of: ");
lcd.print(Number_photos);
digitalWrite(Camera_pin, HIGH);
delay (Delay_shutter_lag);// wait for shutter to open plus a bit - 52ms for Nikon plus a copule of ms before flash
digitalWrite(Flash_pin, HIGH);
delay(10);// trigger flash
digitalWrite(Camera_pin, LOW);
digitalWrite(Flash_pin, LOW);
delay(Delay_2);
for (int x_count_photos = 1; x_count_photos <= Number_photos - 1; x_count_photos++) {
//move rail position by steps_between_photos
//motor speed is set by delays....going slowly to avoid lost steps.....
for (int y_count_steps = 1; y_count_steps <= steps_between_photos; y_count_steps++) {
digitalWrite(Drive_pin, LOW);
delayMicroseconds(200);
digitalWrite(Drive_pin, HIGH);
delayMicroseconds(200);
}
//Tell user which photo is being taken
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Creating Z stack! ");
lcd.setCursor(0, 1);
// 12345678901234567890
lcd.print("Photo #: ");
lcd.print(x_count_photos + 1);
lcd.setCursor(0, 2);
lcd.print("In stack of: ");
lcd.print(Number_photos);
delay(Delay_1); // settle dealy to steady camera and rail
// take the photo and set Flash_pin high with Camera_pin long enough to trigger camera
digitalWrite(Camera_pin, HIGH);
delay (Delay_shutter_lag);// delay for shutter to open - ~1/10th
// Neeeds work here to set delay so flash occurs with correct timing - not relevant if camera triggers flashes
digitalWrite(Flash_pin, HIGH);
delay(10);// long enough pulse to trigger flash
digitalWrite(Camera_pin, LOW);
digitalWrite(Flash_pin, LOW);
delay(Delay_2); // allow for extended exposure times greater than 1 second provided by message delay
}
delay(1000);// so you can see the last value for photo number
//
//Rewind the slider to the starting point by reversing rail_direction and going back total number of steps in stack
//
//Warn that rewind about to take place..
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("--------------------");
lcd.setCursor(0, 1);
// 01234567890123456789
lcd.print(" Caution rewinding! ");
lcd.setCursor(0, 2);
lcd.print("<<<<<<<<<<>>>>>>>>>>");
lcd.setCursor(0, 3);
lcd.print("--------------------");
delay (1500);
carry_direction = Direction;
if (carry_direction == 2) {
digitalWrite(Dir_pin, HIGH);
}
if (carry_direction == 1) {
digitalWrite(Dir_pin, LOW);
}
//digitalWrite(Dir_pin, rail_direction);
total_steps = (Number_photos - 1) * steps_between_photos;
for (int x_count_rewind = 1; x_count_rewind <= total_steps; x_count_rewind ++) { digitalWrite(Drive_pin, LOW); delayMicroseconds(200); digitalWrite(Drive_pin, HIGH); delayMicroseconds(200); } digitalWrite(Enable_pin, HIGH); myencoder.write(4); lcd.clear(); } // /// SUBROUTINE jogger() jogs a motor via encoder // void jogger () { long int j = 0; int i = 0; int old_encoder_value; int step_size = 10; float jog_Increment = 0; Old_button_flag = HIGH; Button_flag = HIGH; if (Motor_selected > 3)
{
lcd.clear();
// 01234567890123456789
lcd.setCursor(0,0);
lcd.print("--------------------");
lcd.setCursor(0,1);
lcd.print("Use X, Y or Z motor ");
lcd.setCursor(0,2);
lcd.print("Choose when offered!");
lcd.setCursor(0,3);
lcd.print("--------------------");
delay(1500);
lcd.clear();
select_motor();
}
pinMode(Dir_pin, OUTPUT);
pinMode(Drive_pin, OUTPUT);
pinMode(Enable_pin, OUTPUT);
digitalWrite(Dir_pin, HIGH);
digitalWrite(Drive_pin, LOW);
digitalWrite(Enable_pin, LOW);
lcd.clear();
lcd.setCursor(0, 0);
// 01234567890123456789
lcd.print(" ---JOG SUB-MENU--- ");
lcd.setCursor(0, 1);
lcd.print(" Ready to move! ");
lcd.setCursor(0, 2);
lcd.print("Jog Increment (um): ");
jog_Increment = float(Increment) * float(step_size) * 0.78824;
lcd.setCursor(0, 3);
lcd.print(" ");
lcd.print(int(jog_Increment));
// 12345678901234567890
step_size = step_size * Increment; // scale step_size with value from Increment subroutine
old_encoder_value = myencoder.read();
do
{
j = myencoder.read();
if (j > old_encoder_value)
{
i = 1;
do {// not at all clear why a for loop will not work...but it doesn't!
digitalWrite(Dir_pin, HIGH);
digitalWrite(Drive_pin, HIGH);
delayMicroseconds(600);
digitalWrite(Drive_pin, LOW);
delayMicroseconds(600);
i = i + 1;
} while (i <= step_size);
}
if (j < old_encoder_value)
{
i = 1;
do {
digitalWrite(Dir_pin, LOW);
digitalWrite(Drive_pin, HIGH);
delayMicroseconds(600);
digitalWrite(Drive_pin, LOW);
delayMicroseconds(600);
i = i + 1;
} while (i <= step_size);
}
old_encoder_value = myencoder.read();
Button_flag = digitalRead(Button_pin);
if (Old_button_flag != Button_flag)
{
Old_button_flag = Button_flag;
delay(70);
}
} while (Button_flag == HIGH);
lcd.clear();
digitalWrite(Enable_pin, HIGH);
myencoder.write(4);
}

//
// SUBROUTINE rotate () rotates a rotary axis motor
//

void rotate () {
//probably needs to check that a rotary axis motor has been selected in the 'select motor' subroutine
//and then gets instructions re: clockwise or anticlockwise and the number of degrees (limits?)
int turn_clock = 0;
int turn_clock_old = 0;
if (Motor_selected <= 3) { lcd.clear(); // 01234567890123456789 lcd.setCursor(0,0); lcd.print("--------------------"); lcd.setCursor(0,1); lcd.print(" Use A or B motor "); lcd.setCursor(0,2); lcd.print("Choose when offered!"); lcd.setCursor(0,3); lcd.print("--------------------"); delay(1500); lcd.clear(); select_motor(); } Old_button_flag = HIGH; Button_flag = HIGH; pinMode (Dir_pin, OUTPUT); pinMode(Drive_pin, OUTPUT); pinMode(Enable_pin, OUTPUT); digitalWrite(Enable_pin, LOW); // been problem when I defined enable pin as output????? lcd.clear(); lcd.setCursor(0, 0); lcd.print("--ROTATE SUB-MENU---"); // 12345678901234567890 lcd.setCursor(0, 1); lcd.print("Jog step 0.22 degree"); lcd.setCursor(0, 2); lcd.print("Degrees of rotation:"); lcd.setCursor(0, 3); myencoder.write(0); // cahnged to zero -ok?? do { turn_clock = myencoder.read(); turn_clock = turn_clock / 4; if (turn_clock > turn_clock_old) {
digitalWrite(Dir_pin, LOW);
digitalWrite(Drive_pin , HIGH);
delayMicroseconds(900);
digitalWrite(Drive_pin, LOW);
delayMicroseconds(900);
}
else if (turn_clock < turn_clock_old) {
digitalWrite(Dir_pin, HIGH);
digitalWrite(Drive_pin, HIGH);
delayMicroseconds(900);
digitalWrite(Drive_pin, LOW);
delayMicroseconds(900);
}
turn_clock_old = turn_clock;
lcd.setCursor(8, 3);
lcd.print(float(turn_clock) * 0.225);
lcd.print(" ");
Button_flag = digitalRead(Button_pin);
if (Old_button_flag != Button_flag)
{
delay(100);
Button_flag = digitalRead(Button_pin);
Old_button_flag = Button_flag;
}
} while (Button_flag == HIGH);
myencoder.write(4);
lcd.clear();
digitalWrite(Enable_pin, HIGH);
}

//
// SUBROUTINE shoot () takes a picture without flash, just fires shutter on press of rotary encoder knob
//

void shoot () {
// just fires shutter on press of rotary encoder knob
int k;
int j;
byte Old_button_flag = HIGH;
byte Button_flag = HIGH;
lcd.clear();
lcd.setCursor(0, 0);
// 12345678901234567890
lcd.print("---SHOOT SUB-MENU---");
lcd.setCursor(0, 1);
lcd.print("Click to take photo!");
// 12345678901234567890
// need to get clockwise or anticlockwise and degrees
while (Button_flag == HIGH)
{
Button_flag = digitalRead(Button_pin);
if (Old_button_flag != Button_flag)
{
delay(30);
Button_flag = digitalRead(Button_pin);
Old_button_flag = Button_flag;
}
if (Button_flag == LOW)
{
digitalWrite(Camera_pin, HIGH);
delay(Delay_shutter_lag);// no need for lag here
digitalWrite(Camera_pin, LOW);
delay(Delay_2);
// 1234567890123456789
lcd.setCursor(0, 2);
lcd.print(" Photo taken! ");
delay(500);
lcd.clear();
return;
}
}
}

//
// SUBROUTINE flash () just fires the flashes on press of rotary encoder knob
//

void flash () {
// just fires flashes on press of rotary encoder knob
int k;
int j;
byte Old_button_flag = HIGH;
byte Button_flag = HIGH;
lcd.clear();
lcd.setCursor(0, 0);
// 12345678901234567890
lcd.print("---FLASH SUB-MENU---");
lcd.setCursor(0, 1);
lcd.print("Click to fire flash ");
// 12345678901234567890
// need to get clockwise or anticlockwise and degrees
while (Button_flag == HIGH)
{
Button_flag = digitalRead(Button_pin);
if (Old_button_flag != Button_flag)
{
delay(30);
Button_flag = digitalRead(Button_pin);
Old_button_flag = Button_flag;
}
if (Button_flag == LOW)
{
digitalWrite(Flash_pin, HIGH);
delay(10);
digitalWrite(Flash_pin, LOW);
delay(Delay_2);
// 1234567890123456789
lcd.setCursor(0, 2);
lcd.print(" Flash fired! ");
delay(500);
lcd.clear();
return;
}
}
}

//
// SUBROUTINE shoot_flash () just takes a single picture with flash on press of rotary encoder knob
// this and the routines shoot() & shoot_flash() could all be choices within one subroutine??
//

void shoot_flash () {
// just fires flashes on press of rotary encoder knob
int k;
int j;
byte Old_button_flag = HIGH;
byte Button_flag = HIGH;
lcd.clear();
lcd.setCursor(0, 0);
// 12345678901234567890
lcd.print("SHOOT&FLASH SUB-MENU");
lcd.setCursor(0, 1);
lcd.print("Click = flash photo ");
// 12345678901234567890
// need to get clockwise or anticlockwise and degrees
while (Button_flag == HIGH)
{
Button_flag = digitalRead(Button_pin);
if (Old_button_flag != Button_flag)
{
delay(30);
Button_flag = digitalRead(Button_pin);
Old_button_flag = Button_flag;
}
if (Button_flag == LOW)
{
digitalWrite(Camera_pin, HIGH);
delay(Delay_shutter_lag);
digitalWrite(Flash_pin, HIGH);
delay(10);
digitalWrite(Camera_pin, LOW);
digitalWrite(Flash_pin, LOW);
delay(Delay_2);
// 1234567890123456789
lcd.setCursor(0, 2);
lcd.print(" Flash photo taken!");
delay(500);
lcd.clear();
return;
}
}
}

//
// SUBROUTINE select_motor() allows choice of which motor to control
//

int select_motor () {
char motor = 'Z';
Old_button_flag = HIGH;
Button_flag = HIGH;
myencoder.write(12);
lcd.clear();
lcd.setCursor(0, 0);
// 12345678901234567890
lcd.print("---MOTOR SUB-MENU---");
// 12345678901234567890
lcd.setCursor(0, 1);
lcd.print(" A, B for rotations!");
lcd.setCursor(0, 2);
lcd.print("Current motor: ");
lcd.print(motor);
while (Button_flag == HIGH)
{
Motor_selected = encode_return(5, 1);
switch (Motor_selected) {
case 1:
motor = 'X';
break;
case 2:
motor = 'Y';
break;
case 3:
motor = 'Z';
break;
case 4:
motor = 'A';
break;
case 5:
motor = 'B';
break;
default:
;
}
lcd.setCursor(0, 2);
lcd.print(" Current motor: ");
lcd.print(motor);
Button_flag = digitalRead(Button_pin);
if (Old_button_flag != Button_flag)
{
Old_button_flag = Button_flag;
delay(70);
}
}
switch (motor) { //set pins up for the motor selected
case 'X':
Enable_pin = 20;
Dir_pin = 21;
Drive_pin = 22;
break;
case 'Y':
Enable_pin = 23;
Dir_pin = 24;
Drive_pin = 25;
break;
case 'Z': // reset these to 26, 27, 28 when rewiring done!
Enable_pin = 10;
Dir_pin = 11;
Drive_pin = 12;
break;
case 'A':
Enable_pin = 29;
Dir_pin = 30;
Drive_pin = 31;
break;
case 'B':
Enable_pin = 32;
Dir_pin = 33;
Drive_pin = 34;
break;
default:
;
}
myencoder.write(4);
//return; //Motor_selected......no need to return this variable it's global
}

//
// SUBROUTINE click_Increment() adjusts the 'sensitivity' of the rotary encoder making
// it easier to dial up big numbers
//

int click_Increment() {
byte Old_button_flag = HIGH;
byte Button_flag = HIGH;
myencoder.write(40);
lcd.clear();
lcd.setCursor(0, 0);
// 12345678901234567890
lcd.print("-CLICK INC SUB-MENU-");
lcd.setCursor(0, 1);
lcd.print(" For each encoder ");
lcd.setCursor(0, 2);
lcd.print(" click inc will be: ");
// 12345678901234567890
while (Button_flag == HIGH)
{
Increment = encode_return(50, 1);
lcd.setCursor(9, 3);
lcd.print(Increment);
// 1234567890
lcd.print(" ");
Button_flag = digitalRead(Button_pin);
if (Old_button_flag != Button_flag)
{
delay(100);
Button_flag = digitalRead(Button_pin);
Old_button_flag = Button_flag;
}
if (Button_flag == LOW)
{
lcd.clear();
myencoder.write(4); // return encoder value to 1
return Increment;
}
}
}

Advertisements

About petermobbs

Inveterate meddler.
This entry was posted in Photography and electronics and tagged , , , , , , , . Bookmark the permalink.

20 Responses to Control box for a focus rail, and other stepping motor-driven camera moving devices. The program.

  1. brede71 says:

    Hi Peter 🙂 This is fantastic work ! I will try your code today with my macro rail. Have you any plans for include code for controlling of limiting micro-switches to prevent the slide on the rail hitting the ends of the rail?

    • petermobbs says:

      Hi Eric, I hope it works for you. The code is a work in progress and, yes, I do intend to add limit switches and detect when the rails are at their ends. Quite easy to do I think. May take a while until I have a better version available but watch this space. Let me know if you need any help. All the best, Peter.

  2. marsden29 says:

    Just about finished building my version of the control box, only tidying the connections to the stacking rail remains. I’ve had a look through your code and I haven’t the foggiest idea what much of it does! All credit to you. Just one thing that I have noticed (not an error) the hex address for the display will differ, I assume depending on the I2C board used. I did a test program with mine and used this – LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); No idea what it means, I just copied someone else’s code, but it does work. I expect the HEX address to vary of course, but what are all the other numbers?
    I’ll try to get around to posting some pictures of my stuff.
    David

    • petermobbs says:

      Hi David,

      Yep, I am a bit mystified by the I2C statement you give. The I2C displays I have just require the address, the number of columns and the number of rows I.e. LiquidCrystal_I2C lcd(0x3F, 20, 4). All those other numbers suggest your display requires those number pins on the Arduino which would be a bit self-defeating since the great thing about I2C is that you can hang any number of devices on just two lines as long as they have unique addresses. However, see here in the replies section – it seems the new LiquidCrystal library doesn’t need those PIN numbers. Suggest you check you have the latest version of the library: https://arduino-info.wikispaces.com/LCD-Blue-I2C

      Good luck and all the best,

      P

  3. marsden29 says:

    Hi Peter.

    I’m just at the stage of a first compile and I’m getting a few errors, I’ve copied the verbose error log.
    I’ve renamed the file, as I’ve changed the I2C address to suite my kit.

    I appreciate that this is just what you were afraid of in publishing the listing – sorry. Please don’t spend ages on this, just look at it when you can.

    David

    Arduino: 1.6.12 (Windows 10), Board: “Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)”

    C:\Program Files (x86)\Arduino\arduino-builder -dump-prefs -logger=machine -hardware C:\Program Files (x86)\Arduino\hardware -tools C:\Program Files (x86)\Arduino\tools-builder -tools C:\Program Files (x86)\Arduino\hardware\tools\avr -built-in-libraries C:\Program Files (x86)\Arduino\libraries -libraries C:\Users\David\Documents\Arduino\libraries -fqbn=arduino:avr:mega:cpu=atmega2560 -ide-version=10612 -build-path C:\Users\David\AppData\Local\Temp\arduino_build_436040 -warnings=none -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.avr-gcc.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -prefs=runtime.tools.avrdude.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -verbose C:\Users\David\Documents\Arduino\Stepper2_org\Stepper2_org.ino
    C:\Program Files (x86)\Arduino\arduino-builder -compile -logger=machine -hardware C:\Program Files (x86)\Arduino\hardware -tools C:\Program Files (x86)\Arduino\tools-builder -tools C:\Program Files (x86)\Arduino\hardware\tools\avr -built-in-libraries C:\Program Files (x86)\Arduino\libraries -libraries C:\Users\David\Documents\Arduino\libraries -fqbn=arduino:avr:mega:cpu=atmega2560 -ide-version=10612 -build-path C:\Users\David\AppData\Local\Temp\arduino_build_436040 -warnings=none -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.avr-gcc.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -prefs=runtime.tools.avrdude.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -verbose C:\Users\David\Documents\Arduino\Stepper2_org\Stepper2_org.ino
    Using board ‘mega’ from platform in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr
    Using core ‘arduino’ from platform in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr
    Detecting libraries used…
    “C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++” -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10612 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\mega” “C:\Users\David\AppData\Local\Temp\arduino_build_436040\sketch\Stepper2_org.ino.cpp” -o “nul”
    “C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++” -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10612 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\mega” “-IC:\Users\David\Documents\Arduino\libraries\Encoder” “C:\Users\David\AppData\Local\Temp\arduino_build_436040\sketch\Stepper2_org.ino.cpp” -o “nul”
    “C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++” -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10612 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\mega” “-IC:\Users\David\Documents\Arduino\libraries\Encoder” “-IC:\Users\David\Documents\Arduino\libraries\LiquidCrystal_I2C” “C:\Users\David\AppData\Local\Temp\arduino_build_436040\sketch\Stepper2_org.ino.cpp” -o “nul”
    “C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++” -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10612 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\mega” “-IC:\Users\David\Documents\Arduino\libraries\Encoder” “-IC:\Users\David\Documents\Arduino\libraries\LiquidCrystal_I2C” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src” “C:\Users\David\AppData\Local\Temp\arduino_build_436040\sketch\Stepper2_org.ino.cpp” -o “nul”
    “C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++” -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10612 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\mega” “-IC:\Users\David\Documents\Arduino\libraries\Encoder” “-IC:\Users\David\Documents\Arduino\libraries\LiquidCrystal_I2C” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src” “-IC:\Users\David\Documents\Arduino\libraries\Encoder\utility” “C:\Users\David\Documents\Arduino\libraries\Encoder\Encoder.cpp” -o “nul”
    “C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++” -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10612 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\mega” “-IC:\Users\David\Documents\Arduino\libraries\Encoder” “-IC:\Users\David\Documents\Arduino\libraries\LiquidCrystal_I2C” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src” “C:\Users\David\Documents\Arduino\libraries\LiquidCrystal_I2C\LiquidCrystal_I2C.cpp” -o “nul”
    “C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++” -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10612 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\mega” “-IC:\Users\David\Documents\Arduino\libraries\Encoder” “-IC:\Users\David\Documents\Arduino\libraries\LiquidCrystal_I2C” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src” “C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src\Wire.cpp” -o “nul”
    “C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++” -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10612 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\mega” “-IC:\Users\David\Documents\Arduino\libraries\Encoder” “-IC:\Users\David\Documents\Arduino\libraries\LiquidCrystal_I2C” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src” “C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src\utility\twi.c” -o “nul”
    Generating function prototypes…
    “C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++” -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10612 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\mega” “-IC:\Users\David\Documents\Arduino\libraries\Encoder” “-IC:\Users\David\Documents\Arduino\libraries\LiquidCrystal_I2C” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src” “C:\Users\David\AppData\Local\Temp\arduino_build_436040\sketch\Stepper2_org.ino.cpp” -o “C:\Users\David\AppData\Local\Temp\arduino_build_436040\preproc\ctags_target_for_gcc_minus_e.cpp”
    “C:\Program Files (x86)\Arduino\tools-builder\ctags\5.8-arduino10/ctags” -u –language-force=c++ -f – –c++-kinds=svpf –fields=KSTtzns –line-directives “C:\Users\David\AppData\Local\Temp\arduino_build_436040\preproc\ctags_target_for_gcc_minus_e.cpp”
    Compiling sketch…
    “C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++” -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10612 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\mega” “-IC:\Users\David\Documents\Arduino\libraries\Encoder” “-IC:\Users\David\Documents\Arduino\libraries\LiquidCrystal_I2C” “-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src” “C:\Users\David\AppData\Local\Temp\arduino_build_436040\sketch\Stepper2_org.ino.cpp” -o “C:\Users\David\AppData\Local\Temp\arduino_build_436040\sketch\Stepper2_org.ino.cpp.o”
    C:\Users\David\Documents\Arduino\Stepper2_org\Stepper2_org.ino: In function ‘void loop()’:

    Stepper2_org:152: error: ‘stacker’ was not declared in this scope

    stacker();

    ^

    Stepper2_org:156: error: ‘jogger’ was not declared in this scope

    jogger();

    ^

    Stepper2_org:164: error: ‘shoot’ was not declared in this scope

    shoot();

    ^

    Stepper2_org:168: error: ‘flash’ was not declared in this scope

    flash();

    ^

    Stepper2_org:172: error: ‘shoot_flash’ was not declared in this scope

    shoot_flash();

    ^

    Stepper2_org:176: error: ‘select_motor’ was not declared in this scope

    select_motor();

    ^

    Stepper2_org:180: error: ‘click_Increment’ was not declared in this scope

    click_Increment();

    ^

    C:\Users\David\Documents\Arduino\Stepper2_org\Stepper2_org.ino: In function ‘int encode_return(int, int)’:

    Stepper2_org:213: error: ‘select_motor’ was not declared in this scope

    select_motor();

    ^

    Stepper2_org:398: error: ‘motor_driver’ was not declared in this scope

    motor_driver();

    ^

    Stepper2_org:441: error: a function-definition is not allowed here before ‘{‘ token

    {

    ^

    Stepper2_org:926: error: expected ‘}’ at end of input

    }

    ^

    Multiple libraries were found for “LiquidCrystal_I2C.h”
    Used: C:\Users\David\Documents\Arduino\libraries\LiquidCrystal_I2C
    Not used: C:\Users\David\Documents\Arduino\libraries\NewliquidCrystal
    Using library Encoder in folder: C:\Users\David\Documents\Arduino\libraries\Encoder (legacy)
    Using library LiquidCrystal_I2C at version 1.1.2 in folder: C:\Users\David\Documents\Arduino\libraries\LiquidCrystal_I2C
    Using library Wire at version 1.0 in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire
    exit status 1
    ‘stacker’ was not declared in this scope

  4. marsden29 says:

    Peter, I’ve just thought, when you posted you code did you enclose in (I think, and I can’t find it online)

    ..............

    David

  5. Charles says:

    Hi Peter,

    How I wish I had found out what you were developing weeks ago!! I’ve spent man months trying to learn coding / Arduino etc etc and have managed only a poor version of your control unit – as an OAP it’s been a very painful learning curve. Similar ideas but manually setting IN and OUT points for the macro stack and using an algorithm to calculate the number of shutter actuations based on the the sensor size, lens combo, magnification and effective aperture.
    Built my linear rail – again after buying a lathe and mill and acquiring basic engineering skills!
    I’ll load your sketch without further delay – I’d be very interested indeed in your build schematic / circuit diagram, any idea when you’ll publish it?

    Actually found this article when searching for articles on using leds for a flashgun – looking forward to reading more of your articles.

    Best Regards

    Charles

  6. Charles says:

    Hi Peter,

    Back already! I have the same errors as marsden29 in trying to compile the program. Have you identified the issue? Desperately keen to review the sketch.

    Best

    Charles

    • petermobbs says:

      Hi Charles, For some reason that I have yet to find, the code on the WordPress page, though it looks fine, doesn’t work when cut and pasted. However, the same code works fine when sent as an attachment. I will send you the .ino file. It should compile and work first time. Good luck!

      Peter

  7. Tony says:

    Tony
    Congratulations, it’s a great job.
    Could you indicate the encoder model you used to mimic the same circuit?
    Thank you

    • petermobbs says:

      Hi Tony, I used something like this (eBay item number 261857741214) but any cheap 5 pin top clickable encoder should work. They cost about £2.75. All the best, Peter.

  8. Tony says:

    Thank you Peter.
    When trying to compile the program I get an error in the library enconder. Could you tell me which library uses your program?

    • petermobbs says:

      Tony, I suspect the problem comes from cutting and pasting the program from the web site. I can’t find out why this happens. Email me (pmobbs@aol.com) and I’ll send you the .ino file that I know compiles ok.
      All the best, P.

  9. Albert Adderley says:

    Hi Peter
    I am building a rail similar to yours and would like to build one of your controlers to get it to work I have a couple of questions I would like to ask you if you have time
    1. Have you got a circuit diagram or schematic of the controller
    2. Is this photography always done with the camera moveing towards the object or is it ok to move the object to the camera. As this way there would be a lot less load on the rail
    Thank you in advance albert

    • petermobbs says:

      Hi Albert,

      I haven’t got a schematic for the controller but if you look at the program that controls it, it gives you a list of the arduino pins and what they do. These specify pretty much all of the connections you will need to make. Incidentally, I used an optocoupler board bought on ebay. I did this to make it easier for others to copy BUT I have found that while the control box works with my cameras which don’t mind 5V on the switched line, some cameras don’t work. Thus, I have reverted to using 4n35 couplers. You can find out how to wire them by Googling the part number and ‘Arduino’. Wiring the 4n35 only involves a resistor to limit the current to the LED within it.

      It is often easier to move the object to the camera rather than vice versa but both will work. The only downside might be that the vibration of the object causes problems with its orientation by jiggling small things about. Try both.

      Good luck. Quite a few people have built the rail and control box and are using the software. Please ask if you want a pucker copy of the ino file. It doesn’t seem to copy properly from the web site because of line wrapping and funny characters the WordPress site puts in at the end of the program.

      All the best,

      P

      • Albert Adderley says:

        Hi Peter could I please have a copy of the file will make it a lot easier Albert

  10. Albert Adderley says:

    Hi Peter the right hand end of the comments are cut off so fining them hard to follow to work out the pins. I am useing photography to help with my mental health problems it gives me something to focus on.I have nearly finished building the rail it will have 4 axis x,y,z and rotate.
    But I will need help to get the code working. And the unit wired up as my levels of concentration on most days is very poor if you have time could you please help me by your guidance if I get stuck and could you please send me a copy of you info file. If you are to busy I understand as I know you will have lots of other things to do besides this
    Thank you
    Albert

  11. petermobbs says:

    Albert, You have email! P

  12. Albert Adderley says:

    Hi Peter
    I have uploaded the program but it will not run thought it was my mega so bought another one still the same the screen lights up but no text?
    Albert

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s