Basic Handling of an Stepper Motor.

This is the first of few projects I will dedicate to handling Stepper Motors and the transmission of movement. For this project I will be using an Arduino Mega 2560 to send commands to a Stepper Motor Driver with a motor plugged to it. Figures 1 and 2 below shows the Stepper Motor and the Driver Module respectively.

Figure 1: Stepper Motor.

Figure 2: Stepper Motor Driver Module.

The wiring diagram below shows the connections between Arduino Mega, the Driver Module, and the Stepper Motor.

Figure 3: Wiring Diagram.

For this project, I developed a C# Program to pass two simple commands to the Arduino Mega 2560 board, and ultimately, to the motor, to move to one side or to the other, until finding a limit. Figure 4 below shows the C# Interface program to perform these two simple tasks.

Figure 4: C# Interface program.

On the Arduino side, I am receiving the mnemonic and performing the task by sending the proper command to the Driver Module which, in turn, handles the Stepper Motor. The second task performed by Arduino is to send an update to the C# program as to where the motor is with the purpose of refreshing the slider the C# interface is updating.

There is an issue when communicating with Arduino via the USB port that has become rather annoying. The USB port does not have a way to signal the C# interface that data is being uploaded, some sort of Handshake, so if there is no program or routine, checking the USB port for upcoming data, then the USB buffer can get easily swamped resulting in data loss. That is an issue I found right off the bat after starting this project. The solution I found was to create a second “thread” in C# solely dedicated to check the USB port. “Threading” is one of the most important features in any high level language and C# does a beautiful job implementing it. When threading is implemented, what the Operating System does is to give equal processing time to two or more different tasks creating the impression of parallel processing, but regardless of that, thanks to threading I have been able to reduce the data loss to almost zero percent.

Aside for the C# Interface I created to handle the motor, I also implemented a series a manual buttons to command the motor left and right movements as shown on Figures 5 and 6 below.

Figure 5: Pictures showing Manual Controls LEDs and Buttons.

Figure 5.1

Figure 5.2

Figure 5.3

Figure 6: Manual Controls Wiring Diagram.

The motor is moving within certain constraints, left and right, top and bottom, so the Left and Right Limit Buttons, simulate what otherwise would be a Limit Sensor, so when pressed, in this case by me, I am simulating that the motor has reached the end of the rope. This two buttons, have a particular significance, not only because of the task they are performing, but because of how they are being handle. To handle them I am using Interrupts and Interrupts have a special chapter dedicated just to them in the Microcontrollers and Microprocessors book. What I can say about them, in a nutshell, is that when that state of an interrupt pin changes from 0 to 1 (or vice versa), the processor stops what is doing and jumps to an special routine to handle the interruption, hence the name. In this particular application, my interrupt routine immediately stops the motor then lights the proper led.

This routines will be powering a device I am currently building and shown below in Figure 7.

Figure 7: Slider powered by Stepper Motors.

Below you will find the entirety of the Arduino Mega 2560 program.

Figure 8: Arduino Program to handle one (1) Stepper Motor:


//
//--------------------------------------------------------------------------------------------
// LED display using LedControl.h Library.
//
// This program as well as the C# program were developed in its entirety by:
// Arnoldo B. Canales
// July 8, 2018.
//
// To power the motor(s), I am using the AccelStepper Library property of Mike McCauley.
//
//--------------------------------------------------------------------------------------------
//include libraries
#include <stdio.h>
#include <time.h>
#include <AccelStepper.h>
//#include <MultiStepper.h>
#include "pitches.h"
//
//Memory variables
//
String mCntrlrName;				//Assigning Name to Controller Board.
//
int mDelayTime = 10;			//This Delay Time.
int mDialLocation = 399;		//Assuming Dial is position in the middle.
const int mStepsXcm = 50;		//Number of steps to advance one (1) cm.
int mCurrentStep = 0;			//Current step to reach mStepsXcm.
bool mMoveRight, mMoveLeft;		//Motor is rotating Left/Right.
bool mMoveRight1Step, mMoveLeft1Step;		//Motor is rotating Left/Right.
bool mIntLeftLEDOnOff = false;	//Preserves status of the Left Interrupt LED.
bool mIntRightLEDOnOff = false;	//Preserves status of the Right Interrupt LED.
String mCurrLocation;			//Dial Current Location. To be sent via USB to C# Routine.
bool mHomeLeft = false;			//Signals that Home Left has been reached.
bool mHomeRight = false;		//Signals that Home Right has been reached.
int mMoveLeftAma = 3000;
int mMoveRightAma = mMoveLeftAma * -1;
//
//Variables to talk to the Master Computer via USB port.
byte cmdByte0; byte cmdByte1; byte cmdByte2; byte cmdByte3;
byte cmdByteX[80] = { 0x5E };
//
bool mReadingPort = false; unsigned int mPulseFrequency = 0;
//
//Definition of Frequencies to play Tones and signal events.
int mStartUpFreq = NOTE_C5;
int mGenericFreq = NOTE_E4;
//
// Pin-related Memory variables
//
//RIGHT SIDE
const byte interruptPin3	= 3;	//Interrupt Pin 2 to detect when Far Left is reached.
const int pButtonPin29		= 29;	//Right Button Power Motor
const int LedPin30			= 30;	//Right Moving LED
const int LedPin31			= 31;	//Right Button FAR RIGHT LIMIT reached
const int LedPin32			= 32;	//Right Interrupt LED.
//
//LEFT SIDE
const byte interruptPin2	= 2;	//Interrupt Pin 2 to detect when Far Left is reached.
const int pButtonPin37		= 37;	//Left Button Power Motor
const int LedPin38			= 38;   //Left Moving LED
const int LedPin39			= 39;	//Left Button FAR RIGHT LIMIT reached
const int LedPin40			= 40;	//Left Interrupt LED.
//
//BUZZER PIN
const int LedPin50			= 50;	//Main Loop w Reading Port Cycle LED.
const int pBuzzerPin51		= 51;	//Buzzer -plugged at Controller's pin 39.
//
//
// Define the stepper motors and the pins that will be using
AccelStepper stepper1(1, 7, 6); // (Type:driver, STEP, DIR)

//
//--------------------------------------------------------------------------------------------
//
//Main Setup
//
//--------------------------------------------------------------------------------------------

void setup()
{
	//LEFT SIDE
	pinMode(pButtonPin37, INPUT_PULLUP);	//Left Button Power Motor
	pinMode(LedPin38, OUTPUT);				//Left Moving LED
	pinMode(LedPin39, OUTPUT);				//Left Button FAR RIGHT LIMIT reached
	pinMode(LedPin40, OUTPUT);				//Left Interrupt LED.
	digitalWrite(LedPin38, LOW);
	digitalWrite(LedPin39, LOW);
	digitalWrite(LedPin40, LOW);
	//
	//RIGHT SIDE
	pinMode(pButtonPin29, INPUT_PULLUP);	//Right Button Power Motor
	pinMode(LedPin30, OUTPUT);				//Right Moving LED
	pinMode(LedPin31, OUTPUT);				//Right Button FAR RIGHT LIMIT reached
	pinMode(LedPin32, OUTPUT);				//Right Interrupt LED.
	digitalWrite(LedPin30, LOW);
	digitalWrite(LedPin31, LOW);
	digitalWrite(LedPin32, LOW);
	//
	pinMode(LedPin50, OUTPUT);				//Main Loop w Reading Port Cycle LED LED.
	digitalWrite(LedPin50, LOW);			//initialize Main Loop w Reading Port Cycle LED.
	pinMode(pBuzzerPin51, OUTPUT);			//initialize the buzzer pin as an output
	//
	pinMode(interruptPin2, INPUT_PULLUP);
	attachInterrupt(digitalPinToInterrupt(interruptPin2), FarLeft_Reached, FALLING);
	//
	pinMode(interruptPin3, INPUT_PULLUP);
	attachInterrupt(digitalPinToInterrupt(interruptPin3), FarRight_Reached, FALLING);
	//
	//Set initial seed values for the steppers
	stepper1.setMaxSpeed(3000); stepper1.setSpeed(200);
	//
	mMoveRight = false; mMoveLeft = false;
	//
	Serial.begin(9600);				//start serial communication
	//
	PlayTone(2, mStartUpFreq);
}
//
//--------------------------------------------------------------------------------------------
//
//Main Loop Starts
//Main Loop Starts
//Main Loop Starts
//
//--------------------------------------------------------------------------------------------
void loop()
{
	if (Serial.available() > 0) //Read data to bytes
	{
		for (int i = 0; i <= 63; i++) cmdByteX[i] = 0x5E;  //this was 0x20
		//
		Serial.readBytesUntil('|', cmdByteX, 80); // (^) Carat char indicates end of text being trasmitted
		Serial.flush();
		//
		cmdByte0 = cmdByteX[0] - 48;	//These four bytes are the command.
		cmdByte1 = cmdByteX[1] - 48;
		cmdByte2 = cmdByteX[2] - 48;
		cmdByte3 = cmdByteX[3] - 48;
	}
	//
	//----------------------------------------
	// START: This code is to process commands
	//----------------------------------------
	if (cmdByte0 == 1)				//  FIRST byte is equal to "1", then someone maybe talking to Arduino,
	{
		if (cmdByte1 == 1)			//  SECOND byte is equal to "1".
		{
			if (cmdByte2 == 1)      //  THIRD byte is equal to "1".
			{
				buzzerX(10);
				//Command 1,1,1,1	-	Command query the controller.
				//						FORTH byte is equal to "1", tell me your ID!
				if (cmdByte3 == 1) { mCntrlrName = "A1"; Serial.print(mCntrlrName); }
				//
				//Command 1,1,1,2	-	
				if (cmdByte3 == 2) {}
				//
				//Command 1,1,1,3	-	Command to Increase/Decrease the Delay Time.
				if (cmdByte3 == 3) { mDelayTime += 50; if (mDelayTime == 500) mDelayTime = 50; }
				//
				//Command 1,1,1,4	-	Command to Test Code.
				if (cmdByte3 == 4) {}
				//
				//Command 1,1,1,5	-	Command to 
				if (cmdByte3 == 5) { buzzerX(10); }
			}
		}
		//
		//Command 1,2,x,x are the ... Commands
		if (cmdByte1 == 2) {}
		//
		//Command 1,3,x,x are the ... Commands
		if (cmdByte1 == 3) {}
		//
		//Command 1,4,x,x are the ... Commands
		if (cmdByte1 == 4)			// SECOND byte is equal to "4".
		{
			buzzerX(10);
			//Command 1,4,1,0	-	Move Right.
			if (cmdByte2 == 1)		// THIRD byte is equal to "1".
			{
				mMoveRight = true;
				//
				mMoveLeft = false; mMoveLeft1Step = false; mMoveRight1Step = false;
			}
			//
			//Command 1,4,2,0	-	Move Left.
			if (cmdByte2 == 2)		// THIRD byte is equal to "2".
			{
				mMoveLeft = true;
				//
				mMoveRight = false; mMoveLeft1Step = false; mMoveRight1Step = false;
			}
			//
			//Command 1,4,3,0	-	Command to Stop Motor.
			if (cmdByte2 == 3)		// THIRD byte is equal to "3".
			{
				mMoveRight = false; mMoveLeft = false; mMoveLeft1Step = false; mMoveRight1Step = false;
			}			
			//
			//Command 1,4,4,0	-	Command to Find Home.
			if (cmdByte2 == 4)      // THIRD byte is equal to "4".
			{ FindHome(); }
			//
			//Command 1,4,5,0	-	Command to Move One Step to the Left.
			if (cmdByte2 == 5)      // THIRD byte is equal to "5".
			{ mMoveRight1Step = false; mMoveLeft1Step = true; }
			//
			//Command 1,4,6,0	-	Command to Move One Step to the Right.
			if (cmdByte2 == 6)      // THIRD byte is equal to "6".
			{ mMoveLeft1Step = false; mMoveRight1Step = true; }
			//
			//Command 1,4,7,0	-	Command to .
			if (cmdByte2 == 7)      // THIRD byte is equal to "7".
			{}
			//
			//Command 1,4,8,0	-	Command to .
			if (cmdByte2 == 8)      // THIRD byte is equal to "8".
			{}
			//
			//Command 1,4,9,0	-	Command to Test.
			if (cmdByte2 == 9)      // THIRD byte is equal to "9".
			{ Testing_Routine(); }
		}		
	}
	//----------------------------------------
	// END: This code is to process commands
	//----------------------------------------
	//
	//
	//
	//Button pressed			- Right BUTTON
	if (digitalRead(pButtonPin29) == LOW)
	{
		buzzerX(10);
		do { MoveRight(); } 
		while (digitalRead(pButtonPin29) == LOW); //de-bounce pressed button
	}
	//
	//Button pressed			- Left BUTTON
	if (digitalRead(pButtonPin37) == LOW)
	{
		buzzerX(10);
		do { MoveLeft(); } 
		while (digitalRead(pButtonPin37) == LOW); //de-bounce pressed button
	}
	//
	//
	//
	if (mMoveRight == true) MoveRight();
	if (mMoveLeft == true) MoveLeft();
	if (mMoveLeft1Step == true) MoveOneStep(mMoveLeftAma);
	if (mMoveRight1Step == true) MoveOneStep(mMoveRightAma);
	//
	//
	//
	cmdByte0 = 0; cmdByte1 = 0; cmdByte2 = 0; cmdByte3 = 0;
	if (++mPulseFrequency == 65500) { mReadingPort = !mReadingPort; digitalWrite(LedPin50, mReadingPort); mPulseFrequency = 0; }
	//
	//xLedPin35 = !xLedPin35; digitalWrite(LedPin35, xLedPin35);
}
//
//--------------------------------------------------------------------------------------------
//
//Main Loop Ends
//Main Loop Ends
//Main Loop Ends
//
//--------------------------------------------------------------------------------------------
//
//
//--------------------------------------------------------------------------------------------
//Routine to Centralize the Delays in one point
//
//--------------------------------------------------------------------------------------------
void xDelay(int x)
{
	delay(x);
}
//
//--------------------------------------------------------------------------------------------
//Testing Routine
//This routine is called when command 1,4,4,0 is issued.
//--------------------------------------------------------------------------------------------
void Testing_Routine()
{
	//delay(500);
}
//-------------------------------------------------------------------------------
//buzzerX Routine - Sound the Buzzer.
//
//-------------------------------------------------------------------------------
void buzzerX(int xDuration)
{
	for (int i = 0; i<xDuration; i++)
	{
		digitalWrite(pBuzzerPin51, HIGH);
		xDelay(10);			//wait for 5ms
		digitalWrite(pBuzzerPin51, LOW);
	}
}
//-------------------------------------------------------------------------------
//PlayTone Routine - Sound the Buzzer using Tone() function.
//
//-------------------------------------------------------------------------------
void PlayTone(int pTone,int pFreq)
{
	int mFrequency = pFreq; // NOTE_C5 = 523Hz
	switch (pTone)
	{
		case 1:
			tone(pBuzzerPin51, mFrequency, 250);
			delay(300);
			tone(pBuzzerPin51, mFrequency + 1, 250);
			delay(300); break;
		case 2: //Start-Up
			for (int freq = mFrequency; freq <= (mFrequency* 2); ++freq)
			{tone(pBuzzerPin51, freq, 5); delay(1);} break;
		case 3: break;
		case 4: break;
		default: break;
	}
}
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//
// START - Routines to power up/down the Motor.
//
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------
//Stepper Motor
//MoveRight Function.
//-------------------------------------------------------------------------------
void MoveRight()
{
	if (mHomeRight == false)
	{
		digitalWrite(LedPin30, HIGH);
		//
		mDialLocation++;
		if (mDialLocation < 800)
		{
			MoveOneStep(mMoveRightAma);
			mHomeLeft = false;
			Report_Current_Location();
		}
		else
		{
			mMoveRight = false; mHomeRight = true; mHomeLeft = false;
			mDialLocation = 800;
			Report_Current_Location();
		}
		if (mDialLocation == 800) { digitalWrite(LedPin31, HIGH); }
		if (mDialLocation != 000) { digitalWrite(LedPin39,  LOW); }
		//
		digitalWrite(LedPin30, LOW);
	}
}
//-------------------------------------------------------------------------------
//Stepper Motor
//MoveLeft Function.
//-------------------------------------------------------------------------------
void MoveLeft()
{
	if (mHomeLeft == false)
	{
		digitalWrite(LedPin38, HIGH);
		//
		mDialLocation--;
		if (mDialLocation > 0)
		{
			MoveOneStep(mMoveLeftAma);
			mHomeRight = false;
			Report_Current_Location();
		}
		else
		{
			mMoveLeft = false; mHomeLeft = true; mHomeRight = false;
			mDialLocation = 0;
			Report_Current_Location();
		}
		if (mDialLocation == 000) { digitalWrite(LedPin39, HIGH); }
		if (mDialLocation != 800) { digitalWrite(LedPin31,  LOW); }
		//
		digitalWrite(LedPin38, LOW);
	}
}
//-------------------------------------------------------------------------------
//Stepper Motor
//MoveOneStep Function.
//-------------------------------------------------------------------------------
void MoveOneStep(int pLR)
{
	for (mCurrentStep = 0; mCurrentStep < mStepsXcm; ++mCurrentStep)
	{ stepper1.setSpeed(pLR); stepper1.run(); }
}
//-------------------------------------------------------------------------------
//Stepper Motor
//Report_Current_Location.
//-------------------------------------------------------------------------------
void Report_Current_Location()
{
	char mLocIndicator = 'z';
	switch (mDialLocation)
	{
		case   0: mLocIndicator = 'a'; break;
		case  50: mLocIndicator = 'b'; break;
		case 100: mLocIndicator = 'c'; break;
		case 150: mLocIndicator = 'd'; break;
		case 200: mLocIndicator = 'e'; break;
		case 250: mLocIndicator = 'f'; break;
		case 300: mLocIndicator = 'g'; break;
		case 350: mLocIndicator = 'h'; break;
		case 400: mLocIndicator = 'i'; break;
		case 450: mLocIndicator = 'j'; break;
		case 500: mLocIndicator = 'k'; break;
		case 550: mLocIndicator = 'l'; break;
		case 600: mLocIndicator = 'm'; break;
		case 650: mLocIndicator = 'n'; break;
		case 700: mLocIndicator = 'o'; break;
		case 750: mLocIndicator = 'p'; break;
		case 800: mLocIndicator = 'q'; break;
	}
	if (mLocIndicator != 'z') Serial.print(mLocIndicator);
}
//-------------------------------------------------------------------------------
//Stepper Motor
//FarLR_Reached Function.
//Routine called when Interrupt on Pin 2 (INT 0) is triggered.
//-------------------------------------------------------------------------------
void FarLeft_Reached()
{
	noInterrupts();
	//
	mIntLeftLEDOnOff = !mIntLeftLEDOnOff;
	digitalWrite(LedPin40, mIntLeftLEDOnOff);
	//
	mCurrentStep = mStepsXcm;
	mDialLocation = 0;
	mHomeRight = false;
	mHomeLeft = true; mMoveLeft = false;
	buzzerX(10);
	//
	interrupts();
}
//-------------------------------------------------------------------------------
//Stepper Motor
//FarRight_Reached Function.
//Routine called when Interrupt on Pin 3 (INT 1) is triggered.
//-------------------------------------------------------------------------------
void FarRight_Reached()
{
	noInterrupts();
	//
	mIntRightLEDOnOff = !mIntRightLEDOnOff;
	digitalWrite(LedPin32, mIntRightLEDOnOff);
	//
	mCurrentStep = mStepsXcm;
	mDialLocation = 800;
	mHomeLeft = false;
	mHomeRight = true; mMoveRight = false;
	buzzerX(10);
	//
	interrupts();
}
//-------------------------------------------------------------------------------
//Stepper Motor
//FindHome Function
//-------------------------------------------------------------------------------
void FindHome()
{
	mHomeRight = false;
	mMoveRight = false;
	digitalWrite(LedPin30, LOW);
	digitalWrite(LedPin31, LOW);
	digitalWrite(LedPin32, LOW);
	mIntRightLEDOnOff = false;
	//
	mHomeLeft = false;
	digitalWrite(LedPin38, LOW);
	digitalWrite(LedPin39, LOW);
	digitalWrite(LedPin40, LOW);
	mIntLeftLEDOnOff = false;
	//
	mMoveLeft = true;				//Moving LEFT
	digitalWrite(LedPin38, HIGH);	//Moving LEFT
	//
	do { MoveOneStep(mMoveLeftAma); } while (mHomeLeft == false);
	//
	mDialLocation = 0;				//Set Dial location to zero.
	digitalWrite(LedPin38, LOW);	//Moving LEFT no longer
	digitalWrite(LedPin39, HIGH);	//FAR LEFT reached
}
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//
// END - Routines to power up/down the Motor.
//
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
// END OF PROGRAM - 542 LoC

						

Please check out the videos for this project and let me know your thoughts in the comments section below and thanks for visiting.

Videos

Stepper Motor Manual/Automated control.




Connect