Choosing right motor shield (& other components)

Dear everyone,

I'm looking on hardware advice since I am a noob on this subject. Programming questions will follow after I picked the correct components ;') in a different topic.

For a project I am working on I want to use a nema 17 sized stepper motor to drive an extruder of some kind. The rotational speed of the extruder will lie approximately between 10 and max 100 RPM.

The RPM will have to be controleable through a potentiometer and be displayed on a 16x2 LCD display.

I would like to do this with an Arduino uno Rev 3 and a motorshield.

Being a mechanical engineer I calculated the required torque and picked this motor: Nema 17 Bipolar 1.8deg 65Ncm (92oz.in) 2.1A 3.36V 42x42x60mm 4 Wires

So far I have ordered the following components:

I still need to pick and order:

  • Wall socket Power supply, how many amps do I need to run everything?

Starting with the wiring diagram I ran into a few problems which I would really appreciate some advice on.

-First of all the original arduino motor shield (an L298) is probably too small for the chosen motor? is there a better motorshield better suited for the job which I could use?

-Second the chosen LCD and current motorshield use the same pins so I can't use them together. Either I take a different motorshield (Which I need anyway to get the most out of the motor?) or a different LCD (IC2?).

-Third, what is the (easiest and) most reliable way to wire all of this together?

All help and advice is very welcome! Thanks in advance.

Yours sincerely,

Driez

None of the motor shields is suitable for your 2.1 amp stepper motor - the DRV8825 stepper drivers (which fit the stepper motor shields) cannot provide enough current.

You need to get a specialized stepper motor driver that can provide (say) 3 amps so as to give yourself some headroom. The drivers with the TB65xx or TB66xx chip will probably be the most economical. Also IIRC from another Thread the Pololu website now has a new high-current stepper driver - it would be worth checking.

Under no circumstances consider using a pair of H-bridges to drive the motor.

...R
Stepper Motor Basics
Simple Stepper Code

Dear Robin,

Thanks a lot! I have bought a TB6600. All the components should arrive today. The final component list is as follows:

  • 12V 10A laptop power supply
  • TB6600 3.5A | 1/32 microsteps | 9-40V
  • I2C 16X2 LCD
  • 10kΩ potentiometer
  • Arduino Uno Rev 3
  • Nema 17 Bipolar 1.8deg 65Ncm (92oz.in) 2.1A 3.36V 42x42x60mm 4 Wires

The plan is to power the Arduino uno rev 3 and TB6600 from the same power supply.

Next thing to do is the wiring. I made a simple diagram in Fritzing since there aren't many components. I used an IC for the TB6600 because I couldn't find it in Fritzings parts library.

Any advice is welcome! I'll start looking into code now, wish me luck.

Driez:
The plan is to power the Arduino uno rev 3 and TB6600 from the same power supply.

Without knowing details of the power supply I can't really comment. The input to an Arduino Uno is recommended not to exceed 12v and that should work for your stepper motor but if you need higher speeds a higher voltage would be better.

Yours is not the worst Fritzing diagram I have seen but there are no labels on most of the connections. A photo of a simple pencil drawing with the relevant pins clearly labelled will be a lot clearer (even if you are not a good artist).

...R

The power supply is a 12V 10A laptop power supply: Power supply (Dutch website)

"This 10A switching PSU is protected against short-circuiting, overpowering and too high temperatures, and overvoltage (12,6-16,2V). This PSU has a 90 degree DC plug (5,5 mm x 2,1 mm)

The PSU delivers 12V DC-current upto a maximum of 120W."

I don't need high speeds, I need torque at low RPM's between 15 and 100 max.

Here is a picture of the fritzing schematic connection, I hope this helps.

I still can't read the labels on your image.

Is there any reason to think you have the wiring wrong?

...R

I think the wiring is allright. I just measured the voltage from the PSU however and it's 12,17V.

The TB6600 can handle 9-42V so it will be fine. The Arduino however rates 7-12V... will the 0,17 kill it? It will most likely run hot.

Driez:
The Arduino however rates 7-12V... will the 0,17 kill it?

No. The max for an Uno is actually 20v, but that might put too much stress on the voltage regulator.

...R

Cheers, I didn't want to stress the voltage regulator so I bought a 9V 2.5A wall adapter for test purposes. Finishing up code now, hope it works ;').

Got everything connected (except for the the I2C LCD since it hasn't arrived yet.) Been trying to get my code to work the whole day but can't get it to. I'm an absolute newbie at this so please help me out. I've had the motor spin so I know it works and is connected correctly.

I want to input an RPM value between 0-100 with the potentiometer. I have the stepper motor setup at 200 pulses per revolution.

The potentiometer gives good readings between 0-1023 when I serial print the values.

I calculated the corresponding RPM using this formula: RPM =(Pot/1023)*100;

The RPM formula came up with 0 all the time when I checked it with serial print. I was thinking it had to do with the numbers being integers. I changed both to float which helped and gave me readings from 000.00 tot 1023.00 and 0.00 to 100.00. This however gave me problems with the delayMicroseconds(10^6/((RPM/60)2002)); formula where I convert the set RPM value into the corresponding delay.

Current code with RPM and Pot as integers.

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address

//defines pins
const int stepPin = 6;  //PUL -Pulse
const int dirPin = 7; //DIR -Direction
const int enPin = 8; //ENA -Enable
int Pot = A1; // Input pin for the potentiometer.
int RPM; // Declares variable for RPM

void setup(){
  Serial.begin(9600); //Initiate Serial communication.
  lcd.begin(16,2); //Defining 16 columns and 2 rows of lcd display.
  lcd.backlight(); //To Power ON /OFF the back light.
  
  //Sets the pins as Outputs
  pinMode(Pot,INPUT); 
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);
  pinMode(enPin,OUTPUT);
  digitalWrite(enPin,LOW);
}

void loop(){
  Pot = analogRead(A1);
  //Serial.print(Pot);
  //RPM =(Pot/1023)*100;
  Serial.print(RPM);
  lcd.setCursor(0,0); //Defining position to write from first row, first column.
  lcd.print ("RPM"); // Showing the letters RPM
  lcd.setCursor(0,1); //Defining position to write from second row, first column.
  lcd.print (RPM); // Showing current RPM value
  
  //Enables the motor direction to move in a certain direction.
  digitalWrite(dirPin,HIGH);
  //Makes 200 Pulses for making one full cycle (1.8 degree stepper).
  for(int x = 0; x < 200; x++){
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(10^6/((RPM/60)*200*2)); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(10^6/((RPM/60)*200*2));
}
}

Any advice is welcome!!

Driez:
I've had the motor spin so I know it works and is connected correctly.

What is the difference between your non-working program and the program you used to prove that the motor works? The problem will be somewhere in the difference.

It's a good practice to start with a working program and add extra bits in small pieces, testing as often as possible. That way the problem comes to light easily.

...R

Robin2:
What is the difference between your non-working program and the program you used to prove that the motor works? The problem will be somewhere in the difference.

The code that made the motor spin:

void loop(){
  //Enables the motor direction to move in a certain direction.
  digitalWrite(dirPin,LOW);
  //Makes 200 Pulses for making one full cycle (1.8 degree stepper).
  for(int x = 0; x < 200; x++){
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(500);
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(500);
}
}

Now I wanted to add the functionality of it being RPM controlled between 0 and 100 RPM so I changed it to:

void loop(){
  Pot = analogRead(A1);
  RPM =(Pot*100L)/1023;

  //Enables the motor direction to move in a certain direction.
  digitalWrite(dirPin,LOW);
  //Makes 200 Pulses for making one full cycle (1.8 degree stepper).
  for(int x = 0; x < 200; x++){
    digitalWrite(stepPin,HIGH); 
    motor =((((RPM*1000000)/60)/200)/2);
    delayMicroseconds(motor);
    digitalWrite(stepPin,LOW); 
    motor =((((RPM*1000000)/60)/200)/2);
    delayMicroseconds(motor);
}
}

I checked the RPM value with serial.print and it varies between 0 and 100 nicely when turning the potentiometer. I am sure the reason the motor doesn't spin has to do with the code after the RPM has been defined. Any pointers or tips on standard code to control a motors RPM? Having a hard time to find something similar and useful on the forum.

Yours sincerely,

Driez

Take things step by step.

First, change this (from the first program)

 digitalWrite(stepPin,HIGH); 
    delayMicroseconds(500);
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(500);

to this

unsigned long microsBetweenSteps = 1000;
 digitalWrite(stepPin,HIGH); 
    delayMicroseconds(10);
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(microsBetweenSteps);

Now you can change the speed by changing the value of the variable microsBetweenSteps

Next thing is to calculate (with a calculator) what should be the max and min values for that variable to give you the slowest and fastest speed that you want.

Then you can use the map() function to convert the 0 to 1023 from analogRead() to the max and min values that you have calculated.

...R

Robin2:
Take things step by step.

Next thing is to calculate (with a calculator) what should be the max and min values for that variable to give you the slowest and fastest speed that you want.

Then you can use the map() function to convert the 0 to 1023 from analogRead() to the max and min values that you have calculated.

Dear Robin,

Thanks for your time and swift reply. I would like to display the RPM value on an LCD display. An option would be to source the RPM value directly from the potentiometer value if I use your code and not use it anywhere else in the program.

The slowest speed I want is RPM 0 so the delayMicroseconds should be ∞ high (wouldn't this cause a problem and an endless loop and stop the motor from turning?)

The fastest speed I want is RPM 100 which would be 100/60 RPS which would correspond to (100/60)*200 pulses which is 333,3333333 so lets say 333 pulses per second, correct?.

The map function would then look like this:

  Pot = analogRead(A1);
  RPM = (Pot*100L)/1023;
  microsBetweenSteps = map(Pot, 0, 1023, 0, 333);

  digitalWrite(stepPin,HIGH); 
  delayMicroseconds(10);
  digitalWrite(stepPin,LOW); 
  delayMicroseconds(microsBetweenSteps);

Is this what you meant?

Driez:
The slowest speed I want is RPM 0 so the delayMicroseconds should be ∞ high (wouldn't this cause a problem and an endless loop and stop the motor from turning?)

Stationary always needs to be treated as a special case.

The fastest speed I want is RPM 100 which would be 100/60 RPS which would correspond to (100/60)*200 pulses which is 333,3333333 so lets say 333 pulses per second, correct?.

The map function would then look like this:

  Pot = analogRead(A1);

RPM = (Pot*100L)/1023;
  microsBetweenSteps = map(Pot, 0, 1023, 0, 333);

I think your maths needs a refresher.

333 steps per second is one step every 3003 microsecs according to my calculator.

...R

Robin2:
Stationary always needs to be treated as a special case.

Could you point me in the direction of how to code a stationary situation.
I'm suspecting I will have to go with an if statement when the motor wants to go below lets say 3 RPM or so?
The motor has to continue to spin when the RPM's are increased again with the potentiometer most preferably.

Robin2:
I think your maths needs a refresher.

333 steps per second is one step every 3003 microsecs according to my calculator.

Ah I forgot to convert steps per second to the corresponding delay in microseconds! Cheers for pointing that out.
so 1000000 / 333 = 3003 microseconds delay -10 for the delay the high phase already has the microsBetweenSteps becomes 2993.

You rock Robin, thanks a lot for everything so far.

I have just tested the code. It seems to respond well to the potentiometer but it seems that when I turn the potentiometer left (down) the motor starts to spin faster! And when I turn it right (up) it slows down. It also doesn't go in an endless loop when I turn the potentiometer all the way right or left as I would have expected.

Update: I realised the 0 in the map function of the microsBetweenSteps should have a higher (infinite delay instead of a shorter) delay. microsBetweenSteps = map(Pot, 0, 1023, 0, 2993); However If I make the delay approximate infinity at pot meter value 0 -> 999999 weird stuff starts to happens when I test the code. Please advice on a proper and clean way to tackle this.

Update: The situation below happened because of the mistake mentioned above.
"Another thing I noticed is that the motor is making high pitched mario kart like noises and when I want to make it approach it's top speed of a 100 RPM it stalls about halfway or so. Could this possibly have something to do with not ramping or is that unneccesary at these low RPM's?"

Here is the whole code:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address

//defines pins
const int stepPin = 6;  //PUL -Pulse
const int dirPin = 7; //DIR -Direction
const int enPin = 8; //ENA -Enable
int Pot = A1; // Input pin for the potentiometer.
int RPM; // Declares variable for RPM
int motor; // RPM calculation for LCD display

void setup(){
  Serial.begin(9600); //Initiate Serial communication.
  lcd.begin(16,2); //Defining 16 columns and 2 rows of lcd display.
  lcd.backlight(); //To Power ON /OFF the back light.
  
  //Sets the pins as Outputs
  pinMode(Pot,INPUT); 
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);
  pinMode(enPin,OUTPUT);
  digitalWrite(enPin,LOW);
}

void loop(){
  unsigned long microsBetweenSteps; // Delay in microseconds between steps
  Pot = analogRead(A1);
  //Serial.print(Pot);
  RPM =(Pot*100L)/1023;
  microsBetweenSteps = map(Pot, 0, 1023, 0, 2993);
  lcd.setCursor(0,0); //Defining position to write from first row, first column.
  lcd.print ("RPM"); // Showing the letters RPM
  lcd.setCursor(0,1); //Defining position to write from second row, first column.
  lcd.print (RPM); // Showing current RPM value
  
  //Enables the motor direction to move in a certain direction.
  digitalWrite(dirPin,LOW);
  //Makes 200 Pulses for making one full cycle (1.8 degree stepper).
  for(int x = 0; x < 200; x++){
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(10);
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(microsBetweenSteps);
}
}

Currently don't have the LCD connected because it causes the motor not to turn for some weird reason. Will focus and solve that problem later on since the motor has priority.

Driez:
Could you point me in the direction of how to code a stationary situation.

Test for when analogRead() is near 0 (assuming 0 = stop) and if it is don't call the step function. Something like

potValue = analogRead(potPin);
if (potValue < 10) {
   // signifies a STOP
}
else {
   // calculate interval between steps
   // call the motor step function
}

...R

I've changed the code a bit to work with a few different RPM's being: stationary 10,20,30,40,50,60,70,80,90 and 100 in order to make the motor run smoother. It now looks like this:

void setup(){
  Serial.begin(9600); //Initiate Serial communication.
  lcd.begin(16,2); //Defining 16 columns and 2 rows of lcd display.
  lcd.backlight(); //To Power ON /OFF the back light.
  
  //Sets the pins as Outputs
  pinMode(Pot,INPUT); 
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);
  pinMode(enPin,OUTPUT);
  digitalWrite(enPin,LOW);
}

void loop(){
  Pot = analogRead(A1);

if (0 <= Pot and Pot < 93){
  // Stationary mode
  // microsBetweenSteps = infinite;
  RPM = 0;
}
else if (93 < Pot and Pot < 186){
  microsBetweenSteps = 29990;
  RPM = 10;
}
else if  (186 < Pot and Pot < 279){
  microsBetweenSteps = 14990;
  RPM = 20;
}
else if  (279 < Pot and Pot < 372){
  microsBetweenSteps = 9990;
  RPM = 30;
}
else if (372 < Pot and Pot < 465){
  microsBetweenSteps = 7490;
  RPM = 40;
}
else if (465 < Pot and Pot < 558){
  microsBetweenSteps = 5990;
  RPM = 50;
}
else if  (558 < Pot and Pot < 651){
  microsBetweenSteps = 4990;
  RPM = 60;
}
else if  (651 < Pot and Pot < 744){
  microsBetweenSteps = 4276;
  RPM = 70;
}
else if  (744 < Pot and Pot < 837){
  microsBetweenSteps = 3740;
  RPM = 80;
}
else if  (837 < Pot and Pot < 930){
  microsBetweenSteps = 3323;
  RPM = 90;
}
else if  (930 < Pot and Pot <= 1023){
  microsBetweenSteps = 2990;
  RPM = 100;
}
else{
  // Stationary mode
  // microsBetweenSteps = infinite;

}
  Serial.print (RPM);
  lcd.setCursor(0,0); //Defining position to write from first row, first column.
  lcd.print ("RPM"); // Showing the letters RPM
  lcd.setCursor(0,1); //Defining position to write from second row, first column.
  lcd.print (RPM); // Showing current RPM value
  
  //Enables the motor direction to move in a certain direction.
  digitalWrite(dirPin,LOW);
  //Makes 200 Pulses for making one full cycle (1.8 degree stepper).
  for(int x = 0; x < 200; x++){
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(10);
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(microsBetweenSteps);
}
}

I however can't get my head around what to code in the stationary situation of pot meter value 0 - 93. I would like it to spin freely when moved by hand. I have a pin attached to the TB6600 driver's enable pin so I might be able to use this?

Also, at 60 RPM I sometimes get a hickup in the steppers turning behaviour. At 70 RPM the stepper completely freaks out. At all other of the RPM levels it's really smooth. Can't seem to figure out why this is happening. Any ideas?

LCD still disconnected because motor won't turn otherwise.

Driez:
I however can't get my head around what to code in the stationary situation of pot meter value 0 - 93.

Just don't call this piece of code unless the value is above 93

  for(int x = 0; x < 200; x++){
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(10);
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(microsBetweenSteps);
}

Life will be much easier if you break your program into separate single purpose functions. Have a look at Planning and Implementing a Program.

Your code in loop could be like this

void loop() {
   readPotentiometer();
   calculateMotorSpeed();
   if (potVal > 93) {
       moveMotor();
   }
   else {
      stopMotor();
   }
}

And. yes, you should be able to use the enable pin to allow the motor to turn freely.

You cannot expect a motor to jump instantly from stationary to a high speed. You need to increase the speed in steps.

...R