"Uno" µm xyz positioning system for 0.21µm/pixel microscope(Raspberry HQ camera)

This is followup on the last postings in this thread:
https://forum.arduino.cc/index.php?topic=649270.msg4642386#msg4642386

This thread will focus on in progress work for controlling microscope two-axis sliding-table stepper motors, and the 28BYJ48 stepper motor I superglued to microscope stand wheel.

It all started with being able to make new Raspberry HQ camera microscope by mounting an 180° M12 lens reversed as macro lens in the distance needed with several extension rings and CStoM12 adapter ring. Optical microscopes best resolution is d=0.2µm:

... the resolution d ... In practice the lowest value of d obtainable with conventional lenses is about 200 nm. ...

I was interested in getting down to that resolution, and images from 100 divisions of 1mm long micrometer look nice (centers of neighboring divisions are 10µm apart, or 48 pixel at 0.21µm/px). Right click for 100% view of 1360x768 HDMI monitor screenshot. Zoom was chosen so that pixels of monitor preview map 1:1 onto pixels of center part of 12MP frame captured by HQ camera:

After that was done, I started work on getting microscope two-axis sliding table working (I bought three for 1.60$/pc(!) on aliexpress.com):
https://www.aliexpress.com/item/32972295033.html
I had problems in controlling the A+/A-/B+/B- pins of both stepper motors with uln2003 stepper motor driver. So I did what I know is not good, driving these (tiny) micro stepper motors directly from Arduino Uno digital pins. I was lucky that a single coil did draw only 21mA, just above maximal rating of 20mA for an Arduino Uno digital pin per datasheet. This was 1st time getting one of the two stepper motors working, I measured distance for 20 loops of 8 half-steps as 0.8mm, therefore 800µm/(8*20)=5µm per step:

Next I was able to repeatedly position exactly despite backlash of stepper motors, by making sure that final approaching target position is done with constant torque and from same direction. I recorded a youtube video where 4 positions 5µm apart get positioned 100 times:
https://www.raspberrypi.org/forums/viewtopic.php?f=43&t=210605&start=100#p1680357

Vertical positioning was done by hand. Although Raspberry raspistill command new "--focus" feature eases focusing a lot, I wanted to have finer control of Z axis movement. I decided to superglue 28BYJ48 stepper motor to microscope stand wheel and try out, and it really worked. A full stepper revolution of 2048 full-steps did move 18mm wide, that is 8.8µm per single full-step(!). This is a sequence of screenshots taken for doing a single full-step downwards and then capture screenshot and repeat. Never moved that fine grained (8.8µm step wise) for focusing:

This is current setup:
Shard with micrometer superglued onto white cardboard superglued on two-axis sliding-table superglued on yellow light box for height superglued on heavy black box for keeping everything in place. The black box is placed on aluminum alloy microscope stand, and then moved around on that for getting micrometer into view of HQ camera microscope. Moving to target positions <10µm away by hand is a puzzle sometimes. On the microscope stand you see Raspberry HQ camera looking down, connected to Pi4B with a 1m long flat ribbon cable. Right click for details in 2299x2855 part from 16MP smartphone photo:

Next steps are:

  • drive sliding-table stepper motors with uln2003 stepper driver (doable after noticing that uln2003 inverts signals)
  • implement micro-stepping for all three stepper motors for 1µm steps in xyz directions (should be doable)
  • control all three steppers with single Arduino Uno although it has 6 PWM pins only

I worked on the last step already. For each of the two coils of a stepper motor PWM is needed only on one side. Proof of concept breadboard is complete for one stepper currently, three 74HC00N 4xNAND and one 74HC04N 6×Inverter can completely deal with three stepper motors. uln2003 stepper driver inverts signals (OUT_i = NOT(IN_i), and LED_i displays IN_i). Output on two lines of one NAND will be 1 and NOT(pwm), positions decided by second input line. So uln2003 inversion will generate 0 and pwm from that. Maybe the 28BYJ48 stepper needs to be treated differently:

I used this sketch to test the circuit:

void setup() {
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);

  digitalWrite(7, 0);  analogWrite(6, 255);
  digitalWrite(8, 0);  analogWrite(9, 255);
}

int l=0, st=192, i;

void loop() {
  int i;

  digitalWrite(7, l%2);
  digitalWrite(8, (l>>1)%2);
  ++l;
  
  for(i=254; i>st; --i) {
    analogWrite(6,i);  delay(20);
  }

  for(i=254; i>st; --i) {
    analogWrite(9,i);  delay(20);
  }
    
  for(i=st; i<255; ++i) {
    analogWrite(6,i);  delay(20);
  }

  for(i=st; i<255; ++i) {
    analogWrite(9,i);  delay(20);
  }
}

And I did record a video, below you can see that the two inner uln2003 leds have pwm at that point in time:

Outputs 1 and NOT(pwm) of 74HC00N get inverted by uln2003.
So sliding table stepper motors get 0 and pwm, fine.

But 28BYJ48 stepper needs 1 amd pwm.
Solution is to replace the right IC with 74HC02N 4xNOR.
That way output on the two lines will be 0 and NOT(pwm).
uln2003 inverts those to 1 and pwm needed by 28BYJ48.

Just connected logic analyzer, here the repeating 4 phases of sketch can be seen.

When 1st loop has reached 192 (75%), 2nd loop starts to decrease as well.
Top channel is connected to D6, one of the two pins with 980Hz per datasheet.
The other four pwm pins have 490Hz frequency.

Bill of material (prices only if >5$):

  • Raspberry Pi (5$ Pi0, 25$ Pi3A+ or 35$ 2GB Pi4B)
  • 26$ 1024x600 9" HDMI display (if needed)
  • 20$ Arduino Uno (or cheap clone)
  • 50$ Raspberry HQ camera (without lens)
  • 20$ Arducam M12 180° lens with CS- to M12-mount adapter
  • 2× CS to C-mount extension rings
  • 15$ aluminum alloy microscope stand
  • 28BYJ48 stepper motor
  • few Lego pieces, superglue
  • microscope two-axis sliding-table
  • 3× uln2003 stepper motor drivers
  • 2× 74HC00N, 1×74HC02N and 1×74HC04N ICs
  • breadboard, cables, boxes
1 Like

I found nice blog posting from Jangeox:
"Change unipolar 28BYJ-48 to bipolar stepper motor"

In that blog posting he describes how to convert unipolar 28BYJ-48 stepper motor to a bipolar stepper:

Side effect is, that torque did increase from 300gcm/380gcm to 800gcm by that (he determined all torque values by experiments). He did open the motor and did cut red wire on pcb. I did not want to open the motor and just pulled red wire from connector and taped it:

He said that uln2003 driver does not work with cut red wire, and used L293D based setup. I did the same and used his simple demo code from here successfully:
https://printplussupport.be/28BYI_48_bipolar.html

int A = 0;
int B = 1;
int C = 2;
int D = 3;
long del = 2000;
int stap = 1;

void setup() {                
  pinMode(A, OUTPUT);     
  pinMode(B, OUTPUT);     
  pinMode(C, OUTPUT);     
  pinMode(D, OUTPUT);     
}

void een(){
  digitalWrite(A, LOW);   
  digitalWrite(B, HIGH);   
  digitalWrite(C, HIGH);   
  digitalWrite(D, LOW);   
  delayMicroseconds(del);
}
void twee(){
  digitalWrite(A, LOW);   
  digitalWrite(B, HIGH);   
  digitalWrite(C, LOW);   
  digitalWrite(D, HIGH);   
  delayMicroseconds(del);
}
void drie(){
  digitalWrite(A, HIGH);   
  digitalWrite(B, LOW);   
  digitalWrite(C, LOW);   
  digitalWrite(D, HIGH);   
  delayMicroseconds(del);
}
void vier(){
  digitalWrite(A, HIGH);   
  digitalWrite(B, LOW);   
  digitalWrite(C, HIGH);   
  digitalWrite(D, LOW);   
  delayMicroseconds(del);
}
void motorOff(){
  digitalWrite(A, LOW);   
  digitalWrite(B, LOW);   
  digitalWrite(C, LOW);   
  digitalWrite(D, LOW);   
}

// the loop routine runs over and over again forever:
void loop() {

  for (int i=0; i<=500; i++){
    een(); 
    twee();
    drie();
    vier();
  }
  motorOff();
  
  delay(2000);
 
}

Then I went back and tried to use uln2003, no success.
So I switched back and captured the signals that got to stepper with logic analyzer.
Channels 0/1/2/3 are connected to blue/yellow/orange/pink lines of stepper.
Each coil pair has always opposite value:

Then I went back to uln2003 and did measure on the 4 output pins.
Channel 0/1/2/3 are connected to blue/pink/yellow/orange lines of stepper.

Again each coil pair has opposite values, but motor does not turn as with L293D setup.
Not sure why the red 5V line is required for uln2003, but it is.

I was not able to drive the (bipolar) mini steppers of microscope two-axis sliding-table with uln2003, but now I have solution using L293D. Differently to Jangeox I will power all stepper with 5V. This is corrected pwm-doubler layout needed to drive 3 steppers with a single Arduino Uno. I will copy this setup three times, leaving out L293D for 3rd and use uln2003 for the 28BYJ-48 controling microscope height:

The blue and yellow lines connected to A row only are inputs, the white lines connected to A and J rows are the outputs. A pairs of (blue) select and (yellow) pwm pins produce 0/pwm signal on the corresponding two (white) outputs (location of 0 determined by select pin). Therefore a single pwm line from Uno can deliver pwn to both ends of a coil, and we have 6 coils in 3 steppers.

P.S:
4 coils drawing 21mA(max) for the mini stepper motors, and 200mA for the 28BYJ-48 are 284mA in total, and the Uno can deliver 450mA, so all is safe.

Did control xyz steppers from Raspberry Pi, xy via L293D each and z via uln2003.
There will be an Arduino only sketch for doing same; more details here:
https://www.raspberrypi.org/forums/viewtopic.php?f=43&t=210605&p=1693060#p1693060

The cables are a jumble. If you zoom into 16MP smartphone photo you can see cut lanzet lying on micrometer:

"First microscope xyz journey over micrometer and lanzet"
(with 5/5/4.4µm step width in x/y/z direction)
Display here is with 1.05µm/pixel, with using raspistill instead of libcamera open source qcam app 0.21µm/pixel is possible:

I just learned that getting microstepping right is not that easy.
Luckily I can just scale magnification of microscope by an addition 5× to better spot x direction stepper motor micro steps.
I wrote a small standalone script, where I can enter angles in degrees, and stepper motor micro-moves to that angle.
Centers of vertical micrometer divisions are 10µm apart, or 5*10µm/(0.21µm/pixel) = 240 pixels.
More details here:
https://www.raspberrypi.org/forums/viewtopic.php?f=32&t=279671&p=1695195#p1695195
(right click to see 1360x768 animated .gif full size)