Building A Micro Focus Stacking Rail Using Arduino

When I first showed an micro sized automatic focus stacking rail video, the response was over whelming. Some requested an DIY version of it. Though it only took me about two days, one day for hardware design and one day for software development to build a complete system shown in the video below, it is not that easy to actually write a blog about it. Why? Because it is so easy to design a PCB using Eagle and then solder components on it. Writing a blog and building one with Arduino is kinda tedious because you have to wire them and it is messy.

Anyways, here is the video I shot showing what I did, see how clean it is. While using Arduino might be messy but you will be able to do it. Important disclaimer: I am the designer of MJKZZ Focus Stacking Rail Systems

In the above video, I am using a PIC16F630 to do all the work, including stacking and remote control decoding. The Arduino version will be based on physical buttons, however.

How does this work? The system in this blog works a little bit different from “conventional” focus stacking mechanism, that is, “conventional” way is to move the camera to slice up focus planes, in stead, in this blog, the system we are building will move the subject instead of camera. One might ask what could be the disadvantage of such configuration? Well, one possible disadvantage of moving subject might change of lighting conditions, since subject is moving, lighting on it might be different from one shot to another. But, if your lighting setup is large enough relative to the subject, the change might be minor because the subject will be under a large light source that changing in millimeters would not cause much difference. Another disadvantage of moving subject is that if the subject is not movable, such as live stacking of insects, it will not work. Of course, moving subject does have some advantage: most subjects are lighter, so the rail does not have to be very powerful; keeping camera stationary also means we could have less vibration and more secure fixture of the camera.

The Rail. As you can see there is a small rail in that video that is doing all the work, ie, moving subject back and forth. So what is it and where to find it? Here is a picture of it:


This is one of the hardest part of this project — finding it. I got this particular rail on Taobao, in China, but I have searched for it on eBay and even on Alibaba but in vain.  It is rather cheap, about 4.5USD on Taobao.  On eBay, I did not find this particular one, but there seems to be some similar ones on eBay. Most of these small rails on eBay are cheap, about a dollar or two, or maybe even cheaper. However, one important thing to note is that the rail I am using has 0.3mm pitch for the screw, the ones on eBay seem to have 0.5mm pitch. Because the step motor on these type of rails commonly have 18 degrees step size, ie, there are only 20 steps per revolution. This means, a 0.5mm pitch screw will yield a 0.5mm/20 = 0.025mm or 25um minimum step size.

The smaller the minimum step size, the higher magnification the rail system can support. So, with my particular rail, the minimum step size is 0.3mm/20 = 15um, so it is very possible to support magnification of 5x with largest Numerical Aperture value of 0.15. With minimum step size of 25um, supporting a 5x NA of 0.15 microscope objective might be difficult, maybe a 5x with 0.12 NA.

The Motor Driver.  Once you have found a suitable micro stepper motor driven rail like above, we need to design a circuit to drive the step motor on the rail. Like it was noted before, most these rails come with a small step motor with 20 steps per revolution. One might suggest to use a micro stepping driver to increase resolution, yes, good idea, but I have tried it using MJKZZ rail controller, these motors do not support micro-stepping, they simply ignore sub-stepping signals or step unreliably.

But, we are still not out of options yet. Since the motor of these rails are small, they do not draw much current, typically between 0.1A to 0.3A, mostly 0.1A, and since they do not support micro stepping, we could use some regular components to drive them, ie maye an H-bridge circuit, instead of some expensive and complicated parts. One that is so commonly used with Arduino projects is the L9110S module. They are so cheap and common on eBay, the cheapest one being about $1!!! It is crazy not to use them. Here is a picture of it:


You might have a different colored one, but the underlying way using it to drive a step motor is the same: using Arduino to output stepping sequence to move the motor. From the above picture, we can see there are four outputs namely, A+, A-, B+, B-, these are connection wires to the corresponding wires on the motor (as seen on the first picture of the rail). To control the A+, A-, B+, and B-, there are four control inputs on the above board, shown as DIP pins and they are A-1A, A-1B, B-1A, and B-1B. The code to drive them in Arduino sketch is the “step()” function in source code listed at the end of blog. To move forward, go through the table top to bottom sequence, to reverse direction, go through the table from bottom up.


For those rails that are different from mine, you can use a multi-meter to find out which pairs of wire are A’s and which are B’s. If two pair is conductive or with very, very little resistance, it means the pair belong to same phase, ie, either A or B. It really does not matter which are A’s and which are B’s, as long as they are paired right.

Limiting Current. One more thing before we connect the A’s from L9110S board to the A’s for the motor, is the current limiting resistor. The static resistance of each phase of the motor on my rail is 20 ohms, ie if you measure the resistance between A+ and A- (or B+ and B-), it reads 20 ohms. Since these motors tend to heat up very fast and become very hot if high current is applied, we need to limit the current passing through it. Since when each phase of the L9110S is turned on, its resistance is very, very low, we can ignore its resistance. So if we use 5V power to drive the motor and limit the current to about 0.125A, we have to make sure the total resistance is 5/0.125A = 40 ohms. So we need to put 10 ohms resistor in series with the two phases of the L9110S and motor, to make total resistance of 10 (resistor on + side) + 20 (motor phase resistance) + 10 (resistor on – side) = 40ohms. The wattage rating of these resistors must be greater than 10*0.125*0.125 ~ 0.16W (the resistors shown in the following picture are 1/2W ones).



Here is how it works.

The Source Code. Finally, the Arduino source code for a complete system, the code is self explanatory, but here are some points to watch out. First is the speed control, it is defined as MS_SPEED in source code. With my rail, if speed is too small  (ie, too fast), the rail does not move reliably, this is the nature of the step motor. So keep the MS_SPEED to be greater than 4 (ie, making a step every 4 milliseconds).

Second thing is the debounce time. Here, to simplify hardware design, only software debounce is used and the debounce time is 30 milliseconds. For those who does not know what debounce is, please google the term “switch debounce”. In a nutshell, when mechanical switch is being closed or opened, the metal contacts bounce around before it settles, if we do not debounce it, the Arduino is too fast and will interpret those bouncing actions as multiple signals and can cause some issues. So, it must be debounced.

One important note here: in order to demonstrate in video, the step size, CT_STEP in source code, is set to 10 or 10*15um = 150um. You can set it to 1 or 2 depending on your situation.

Last but not least, the source code was done in about two hours, a lot of simplifications, it can be improved further, such as using a micro kernel for “multi-tasking”, adding a LCD screen so you can change parameters etc, but the basic ideas are there.

#define AP_PIN 2 // motor A+ #define AM_PIN 3 // motor A- #define BP_PIN 4 // motor B+ #define BM_PIN 5 // motor B- #define FW_PIN 6 // move rail forward #define BW_PIN 7 // move rail backward #define SP_PIN 8 // set starting position #define EP_PIN 9 // set ending position #define XX_PIN 10 // execute stacking #define CM_SNAP 11 // camera trigger #define CM_FOCUS 12 // camera focus #define MS_DEBOUNCE 30 // debounce time, this is used to debounce mechanical switches, else you get jitteries #define MS_SNAP 300 // camera trigger duration, how long the camera trigger must be "pressed" #define MS_HOLD 2000 // hold time after taking a picture, here it is 2 seconds #define MS_WAIT 2000 // wait time after moving and before taking a picture, here it is 2 seconds #define MS_SPEED 8 // motor speed, do not set it less han 4 as some motor can not run that fast #define CT_STEP 10 // number of minimum steps to move //********************************************* // This defines motor step state // if moving forward, increase motor_state // if moving backward, decrease motor state // enum enumStepState { ms_state_stop, ms_state_0, ms_state_1, ms_state_2, ms_state_3, ms_state_last, }; // rail position and stacking variables int CPOS = 0; // current rail position int SPOS = 0; // start position int EPOS = 0; // end positioin unsigned char motor_state = ms_state_0; void step(char dir) { //Serial.println(motor_state, DEC); if (dir == 1) { motor_state++; CPOS++; if (motor_state &gt;= ms_state_last) { motor_state = ms_state_0; } } else if (dir == -1) { if (CPOS &gt; 0) { CPOS--; } motor_state--; if (motor_state SPOS) { if (CPOS = SPOS) { // finished stacking break; } else { step(1); } } if (digitalRead(XX_PIN) == LOW) { delay(MS_DEBOUNCE); if (digitalRead(XX_PIN) == LOW) { // XX button pressed again, cancel current stacking bExec = false; break; } } } while(bExec) { snap(); Serial.println(ct++, DEC); if (bExec) { delay(MS_HOLD); } for(int i=0; i EPOS) { if (CPOS = EPOS) { // finished stacking bExec = false; break; } else { step(1); } } } if (digitalRead(XX_PIN) == LOW) { delay(MS_DEBOUNCE); if (digitalRead(XX_PIN) == LOW) { // XX button pressed again, cancel current stacking bExec = false; break; } } if (bExec) { delay(MS_WAIT); } else { // take last picture snap(); } } } Serial.println("Done"); } void setup() { pinMode(AP_PIN, OUTPUT); pinMode(AM_PIN, OUTPUT); pinMode(BP_PIN, OUTPUT); pinMode(BM_PIN, OUTPUT); pinMode(FW_PIN, INPUT); digitalWrite(FW_PIN, HIGH); // set pullup pinMode(BW_PIN, INPUT); digitalWrite(BW_PIN, HIGH); // set pullup pinMode(SP_PIN, INPUT); digitalWrite(SP_PIN, HIGH); // set pullup pinMode(EP_PIN, INPUT); digitalWrite(EP_PIN, HIGH); // set pullup pinMode(XX_PIN, INPUT); digitalWrite(XX_PIN, HIGH); // set pullup pinMode(CM_SNAP,OUTPUT); digitalWrite(CM_SNAP, LOW); pinMode(CM_FOCUS, OUTPUT); digitalWrite(CM_FOCUS, LOW); Serial.begin(115200); step(0); // initialize motor driver } void loop() { if (digitalRead(FW_PIN) == LOW) { moveFW(); } else if (digitalRead(BW_PIN) == LOW) { moveBW(); } else if (digitalRead(SP_PIN) == LOW) { delay(MS_DEBOUNCE); if (digitalRead(SP_PIN) == LOW) { SPOS = CPOS; Serial.print("Start Position = "); Serial.println(SPOS, DEC); } } else if (digitalRead(EP_PIN) == LOW) { delay(MS_DEBOUNCE); if (digitalRead(EP_PIN) == LOW) { EPOS = CPOS; Serial.print("End Position = "); Serial.println(EPOS, DEC); } } else if (digitalRead(XX_PIN) == LOW) { Serial.print("Stacking . . ."); Serial.print(abs(EPOS - SPOS) / CT_STEP + 1, DEC); Serial.println(" Images"); exec(); } }

7 thoughts on “Building A Micro Focus Stacking Rail Using Arduino”

Leave a Reply

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

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

Google photo

You are commenting using your Google 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 )

Connecting to %s