How to Interface Stepper Motor to Arduino Uno

Introduction

I am thinking on creating this tinkering with stepper motor long time ago until yesterday when I met someone in Facebook group asking help regarding his project related to controlling the stepper motor while being able to check for some controls and update LCD to display information. So basically his project should be able to do multiple things at the same time or multi-tasking. Even though Arduino Uno is a powerful microcontroller but it has only one processing core. It means it can process only one task at one time.

To mimic or to copy the functionalities of multi-tasking, I suggest to use the proto-threading. Proto-threading is basically doing small chunks of task by using the power of millis() Arduino function. To do this, BlinkWithoutDelay example of Arduino is used (please refer to the source code below).

Bill Of Materials

  1. Arduino Uno.
  2. Stepper motor.
  3. Stepper motor driver (such as L298)
  4. Keypad shield with 16×2 LCD.
  5. Some jumper wires

Hardware Instruction

  1. By referring to the attached schematic above, connect the keypad shield to Arduino Uno.
  2. Connect the L298N motor driver to Arduino Uno as follows:
  • Connect the IN1 of L298N to Arduino Uno pin 13.
  • Connect the IN2 of L298N to Arduino Uno pin 12.
  • Connect the IN3 of L298N to Arduino Uno pin 11.
  • Connect the IN4 of L298N to Arduino Uno pin 3.
  1. Connect the stepper motor to L298N outputs.
  2. Enable the L298N by placing a jumper on enable pins.
  3. Connect a power supply on +12V pin according to your stepper motor voltage requirements.
  4. Connect the power supply GND to the GND pin of L298N.
  5. Connect the Arduino Uno GND to the GND pin of L298N.
  6. Upload the sketch provided in the source code below.
  7. The sketch basically functions as follows:
    UP button – increases the angle
    DOWN button – decreases the angle
    LEFT button – counter clockwise direction
    RIGHT button – clockwise direction
    SELECT button – execute the stepper rotation according to angle and direction set.

The serial command basically functions by sending command in this format: <A, B, C>
Where A: is the command to rotate the stepper
B: is the parameter of direction, 1: CW 0: CCW
C: is the parameter of angle
< and > is necessary for parsing the command.

Video Demonstration

Source Code

  1// For LCD and keypad refer to: 
  2//      https://wiki.dfrobot.com/Arduino_LCD_KeyPad_Shield__SKU__DFR0009_
  3//      https://create.arduino.cc/projecthub/electropeak/using-1602-lcd-keypad-shield-w-arduino-w-examples-e02d95
  4
  5#include "LiquidCrystal.h"
  6#include "Stepper.h"
  7
  8//LCD pin to Arduino
  9const int pin_RS = 8; 
 10const int pin_EN = 9; 
 11const int pin_d4 = 4; 
 12const int pin_d5 = 5; 
 13const int pin_d6 = 6; 
 14const int pin_d7 = 7; 
 15const int pin_BL = 10; 
 16
 17const int stepsPerRevolution = 200;  // change this to fit the number of steps per revolution
 18
 19LiquidCrystal lcd( pin_RS,  pin_EN,  pin_d4,  pin_d5,  pin_d6,  pin_d7);
 20Stepper myStepper(stepsPerRevolution, 3, 11, 12, 13);
 21
 22// ####################################
 23// PROTOTHREADING DEFINITIONS HERE:
 24// ####################################
 25unsigned long checkSerialTaskTimer = 0;
 26const unsigned long checkSerialTaskInterval = 100;
 27
 28unsigned long setSerialTaskTimer = 0;
 29const unsigned long setSerialTaskInterval = 100;
 30
 31unsigned long updateSerialTaskTimer = 0;
 32const unsigned long updateSerialTaskInterval = 100;
 33
 34unsigned long doStepperTaskTimer = 0;
 35const unsigned long doStepperTaskInterval = 500;
 36
 37unsigned long updateLCDTaskTimer = 0;
 38const unsigned long updateLCDTaskInterval = 100;
 39
 40unsigned long checkButtonTaskTimer = 0;
 41const unsigned long checkButtonTaskInterval = 100;
 42
 43// Global variables
 44const byte numChars = 32;
 45char receivedChars[numChars];
 46char tempChars[numChars];        // temporary array for use when parsing
 47
 48// variables to hold the parsed data
 49char userCmd[numChars] = {0};
 50int firstParam = 0;
 51int secondParam = 0;
 52
 53boolean newData = false;
 54bool newCmd = false;
 55
 56int currAng = 0;
 57bool currDir = true;
 58bool executeSteps = false;
 59
 60
 61void setup() {
 62  // set the speed at 60 rpm:
 63  myStepper.setSpeed(60);
 64  // initialize the serial port:
 65  Serial.begin(9600);
 66  Serial.println("Stepper motor controller: ");
 67  Serial.println("Enter command like <a href="https://www.blogger.com/null">  ");
 68  Serial.println("A: command to turn stepper, 1:turn CW 0:CCW, 360: degrees");
 69  Serial.println();
 70
 71  lcd.begin(16, 2);
 72  lcd.setCursor(0,0);
 73  lcd.print("L/R:Dir  U/D:Deg");
 74  lcd.setCursor(0,1);
 75  lcd.print("DIR:");
 76  lcd.setCursor(9,1);
 77  lcd.print("DEG:");
 78}
 79
 80void loop() {
 81  if(millis() >= checkSerialTaskTimer + checkSerialTaskInterval){
 82    checkSerialTaskTimer += checkSerialTaskInterval;
 83    // do the task
 84    checkSerial();
 85  }
 86
 87  if(millis() >= setSerialTaskTimer + setSerialTaskInterval){
 88    setSerialTaskTimer += setSerialTaskInterval;
 89    // do the task
 90    setSerial();
 91  }
 92
 93  if(millis() >= updateSerialTaskTimer + updateSerialTaskInterval){
 94    updateSerialTaskTimer += updateSerialTaskInterval;
 95    // do the task
 96    updateSerial();
 97  }
 98
 99  if(millis() >= doStepperTaskTimer + doStepperTaskInterval){
100    doStepperTaskTimer += doStepperTaskInterval;
101    // do the task
102    doStepper();
103  }
104
105  if(millis() >= updateLCDTaskTimer + updateLCDTaskInterval){
106    updateLCDTaskTimer += updateLCDTaskInterval;
107    // do the task
108    updateLCD();
109  }
110
111  if(millis() >= checkButtonTaskTimer + checkButtonTaskInterval){
112    checkButtonTaskTimer += checkButtonTaskInterval;
113    // do the task
114    checkButton();
115  }
116
117}
118
119void checkSerial() {
120    static boolean recvInProgress = false;
121    static byte ndx = 0;
122    char startMarker = '<';
123    char endMarker = '>';
124    char rc;
125
126    while (Serial.available() > 0 && newData == false) {
127        rc = Serial.read();
128
129        if (recvInProgress == true) {
130            if (rc != endMarker) {
131                receivedChars[ndx] = rc;
132                ndx++;
133                if (ndx >= numChars) {
134                    ndx = numChars - 1;
135                }
136            }
137            else {
138                receivedChars[ndx] = ''; // terminate the string
139                recvInProgress = false;
140                ndx = 0;
141                newData = true;
142            }
143        }
144
145        else if (rc == startMarker) {
146            recvInProgress = true;
147        }
148    }
149}
150
151void setSerial() {
152  char * strtokIndx; // this is used by strtok() as an index
153
154  if (newData == true) {
155    strcpy(tempChars, receivedChars);
156            // this temporary copy is necessary to protect the original data
157            //   because strtok() used in parseData() replaces the commas with 
158
159    strtokIndx = strtok(tempChars,",");
160    strcpy(userCmd, strtokIndx);       // user command
161 
162    strtokIndx = strtok(NULL, ",");
163    firstParam = atoi(strtokIndx);     // first parameters
164    if (firstParam) {
165      currDir = true;
166    }
167    else {
168      currDir = false;
169    }
170
171    strtokIndx = strtok(NULL, ",");
172    //secondParam = atoi(strtokIndx);    // second parameters
173    currAng = atoi(strtokIndx);    // second parameters
174
175    //newCmd = true;
176    executeSteps = true;
177    newData = false;
178  }
179    
180}
181
182
183void updateSerial() {
184}
185
186void doStepper() {
187  int steps = 0;
188
189  // command from keypad shield
190  if (executeSteps) {
191    steps = rotate(currDir, currAng);
192    if (firstParam) {
193      Serial.print("Rotated CW by ");
194    }
195    else {
196      Serial.print("Rotated CCW by ");
197    }
198    Serial.println(currAng);
199    executeSteps = false;
200  }
201
202}
203
204void updateLCD() {
205  lcd.setCursor(4,1);
206  if (currDir) {
207    lcd.print("CW ");
208  }
209  else {
210    lcd.print("CCW");
211  }
212  lcd.setCursor(13,1);
213  lcd.print(currAng);
214  
215}
216
217void checkButton() {
218   int x;
219   x = analogRead (0);
220   lcd.setCursor(10,1);
221   if (x < 60) {
222     //lcd.print ("Right ");
223     currDir = true; // CW
224   }
225   else if (x < 200) {
226     //lcd.print ("Up    ");
227     if (currAng >= 360) {
228      currAng = 360;
229     }
230     else {
231      currAng = currAng + 5; // increase angle
232     }
233   }
234   else if (x < 400){
235     //lcd.print ("Down  ");
236     if (currAng <= 0) {
237      currAng = 0;
238     }
239     else {
240      currAng = currAng - 5; // decrease angle
241     }
242   }
243   else if (x < 600){
244     //lcd.print ("Left  "); 
245     currDir = false; // CCW
246   }
247   else if (x < 800){
248     //lcd.print ("Select");
249     executeSteps = true;
250   }
251   else {
252     //lcd.print ("      ");  // delete previous text when nothing is press
253   }
254   if (executeSteps) {
255   }
256}
257
258
259int rotate(bool rotateDirection, int reqAngle) {
260  int steps = reqAngle / 1.8;
261  // Clockwise direction
262  if (rotateDirection) {
263    myStepper.step(int(steps));
264  }
265  else {
266    myStepper.step(int(-steps));
267  }
268  return steps;
269}

Call To Action

If you find this lesson useful, please consider supporting my Youtube channel: TechToTinker

Please leave your comments and suggestions in the comment box below.

Thank you and have a good day. Bye.



Posts in this series



No comments yet!

GitHub-flavored Markdown & a sane subset of HTML is supported.