/*
LCD_I2C - Arduino library to control a 16x2 LCD via an I2C adapter based on PCF8574
uC board : Arduino UNO
16x2 LCD with PCF8574 over I2C bus with Arduino UNO.
10K POT at A0
3 Button input.
DRV8825 stepper driver module.
*/
#define SERIAL_DEBUG // comment this if serial debug session is not required.
#define PIN_STEPR_DRIVER_ENABLE 8 // DRV8825_ENABLE @ D8; digital output.
#define PIN_STEP 9 // STEP @ D9; digital output. It is fixed STEP pin to be used with ardunino UNO/NANO with FastAccelStepper library, as it is dedicated TIMER output pin used by FastAccelStepper library.
#define PIN_DIR 10 // DIRECTION @ D10; digital output.
#define PIN_LED1 13 // user LED1 @ D13 on Arduino UNO itself; digital output.
#define PIN_LED2 12 // user LED2 @ D12 for extra indication; digital output.
#define PIN_FAULT 11 // FAULT @ D11; digital input; Active low input; LO : driver shutdown due to fault such as over current or thermal protection.
#define PIN_REVERSE_BUT 2 // REVERSE push button input @ A3; digital input.
#define PIN_STOP_BUT 3 // STOP push button input @ A2; digital input.
#define PIN_FORWARD_BUT 4 // FORWARD push button input @ A1; digital input.
#define PIN_SPEED_CMD A0 // Stepper motor speed command input @ A0; 0-5V analog input using external POT; 0.5-4.5V :: 0-100% speed.
/*
* SPS = RPS*SPR = (RPM/60)*SPR
*/
#define STEP_ANGLE 18 // define stepper motor's step angle from : 18, 9 :: 1.8 degree, 0.9 degree
#define MICROSTEP 8 // define microsteps values from : 1, 2, 4, 8, 16, 32 :: full step, half step, 1/4, 1/16, 1/32
// SPR = Steps per Revolution for selected STEP_ANGLE & MICROSTEP value :
#if STEP_ANGLE == 18 // if STEP_ANGLE is 1.8 degree
#if MICROSTEP == 1
#define SPR 200 // Steps per revolution.
#elif MICROSTEP == 2
#define SPR 400
#elif MICROSTEP == 4
#define SPR 800
#elif MICROSTEP == 8
#define SPR 1600
#elif MICROSTEP == 16
#define SPR 3200
#elif MICROSTEP == 32
#define SPR 6400
#endif // endif for MICROSTEP
#elif STEP_ANGLE == 9 // if STEP_ANGLE is 0.9 degree
#if MICROSTEP == 1
#define SPR 400 // Steps per revolution.
#elif MICROSTEP == 2
#define SPR 800
#elif MICROSTEP == 4
#define SPR 1600
#elif MICROSTEP == 8
#define SPR 3200
#elif MICROSTEP == 16
#define SPR 6400
#elif MICROSTEP == 32
#define SPR 12800
#endif // endif for MICROSTEP
#endif // endif STEP_ANGLE
#define SPEED_RPM 1
#define SPEED_RPS 2
#define SPEED_UNIT SPEED_RPM // Unit of command speed : 1=RPM, 2=RPS
#define SPEED_MIN 1 // Minimum speed in RPM (revolution per minute)
#define SPEED_MAX 60 // Maximum speed in RPM (revolution per minute)
/*
#define SPEED_MIN 1 // Minimum speed in RPS (revolution per second)
#define SPEED_MAX 10 // Maximum speed in RPS (revolution per second) : 10 RPS = 60*10 = 600 RPM
*/
#define SPEED_UPDATE_INTERVAL 1000 // in miliseconds. SPEED command value will get updated at pre-defined interval of time.
// Defining some upper & lower dead zone limit for 0-5V analog input signal for speed command.
// This is to avoid analog input fluctuation caused due to POT wiper instability at end points of POT.
// So effective analog input ADC range will be from SPEED_ADC_LBOUND to SPEED_ADC_HBOUND.
// With 10-bit ADC & 0-5V input signal: SPEED_ADC_LBOUND - SPEED_ADC_HBOUND :: 100-921 :: 0.5V-4.5V .
#define SPEED_ADC_LBOUND 100 // upto 0.5V analog input, speed command will be 0. With 10-bit ADC & 0-5V signal, 100 : 0.5V
#define SPEED_ADC_HBOUND 921 // above 4.5V analog input, speed command will be SPEED_MAX. With 10-bit ADC & 0-5V signal, 920 : 4.5V
#include <ezButton.h>
// create ezButton object that attach to defined pin for respective Push Buttons with internal pull up disabled and external pull up resistor connected.
//ezButton reverse_button(PIN_REVERSE_BUT, INPUT);
//ezButton stop_button(PIN_STOP_BUT, INPUT);
//ezButton forward_button(PIN_FORWARD_BUT, INPUT);
//// create ezButton object that attach to defined pin for respective Push Buttons with internal pull up enabled.
ezButton reverse_button(PIN_REVERSE_BUT);
ezButton stop_button(PIN_STOP_BUT);
ezButton forward_button(PIN_FORWARD_BUT);
#include "LCD_I2C.h"
#define LCD_I2C_SLAVE_ADDR 0x27 // Default address of most PCF8574 modules, change according.
//#define LCD_I2C_SLAVE_ADDR 0x3F // For real hardware
//Initialize 16x2 character LCD with given I2C addess.
LCD_I2C lcd_i2c(LCD_I2C_SLAVE_ADDR, 16, 2); // Default address of most PCF8574 modules, change according.
#include "FastAccelStepper.h"
#include "AVRStepperPins.h"
FastAccelStepperEngine engine = FastAccelStepperEngine();
FastAccelStepper *stepper = NULL;
bool led1_state=false;
uint16_t temp_serbyte=0;
int16_t motor_command_state=0;
int16_t speed_cmd=0, speed_cmd_disp=0;
unsigned long lastTimeStamp = 0;
#if SPEED_UNIT == SPEED_RPM
void set_runSpeed(int32_t runSpeed_rpm)
{
//SPS = RPS*SPR = (RPM/60)*SPR
int32_t runSpeed_sps=0;
runSpeed_sps = (runSpeed_rpm * SPR)/60; // convert current speed command in steps per second.
stepper->setSpeedInHz(runSpeed_sps); // steps/second
}
#elif SPEED_UNIT == SPEED_RPS
void set_runSpeed(int32_t runSpeed_rps)
{
//SPS = RPS*SPR = (RPM/60)*SPR
int32_t runSpeed_sps=0;
runSpeed_sps = runSpeed_rps * SPR; // convert current speed command in steps per second.
stepper->setSpeedInHz(runSpeed_sps); // steps/second
}
#endif // endif for SPEED_UNIT
void setup()
{
// set debounce time to 30 milliseconds for all three push button inputs.
reverse_button.setDebounceTime(30);
stop_button.setDebounceTime(30);
forward_button.setDebounceTime(30);
pinMode(PIN_LED1, OUTPUT); // sets user LED1 @ D13 as digital output on Arduino UNO itself.
pinMode(PIN_LED2, OUTPUT); // sets user LED2 @ D12 as digital output on tt1_uno board.
pinMode(PIN_FAULT, INPUT); // This active low FAULT pin of DRV8825 board will have external pull up of 10K whenever the SLEEP pin of DRV8825 pin is held high using jumper.
pinMode(PIN_STEPR_DRIVER_ENABLE, OUTPUT); // This is active low Stepper driver enable pin as digital output on tt1_uno board.
pinMode(PIN_STEP, OUTPUT); // sets user LED1 @ D13 as digital output on Arduino UNO itself.
pinMode(PIN_DIR, OUTPUT); // sets user LED1 @ D13 as digital output on Arduino UNO itself.
digitalWrite(PIN_LED2, HIGH); // set the LED2 off.
// If you are using more I2C devices using the Wire library use lcd_i2c.begin(false).
// this stop the library(LCD_I2C) from calling Wire.begin()
lcd_i2c.begin();
lcd_i2c.backlight();
lcd_i2c.print("Stepper board."); lcd_i2c.setCursor(0, 1); lcd_i2c.print("initializing.");
engine.init();
stepper = engine.stepperConnectToPin(PIN_STEP);
if (stepper)
{
stepper->setDirectionPin(PIN_DIR);
stepper->setEnablePin(PIN_STEPR_DRIVER_ENABLE);
stepper->setAutoEnable(true); // The stepper driver will be enabled before stepping and will be disabled afterwards.
set_runSpeed(60); // set current moving unit defined by SPEED_UNIT : RPM.
stepper->setAcceleration(2000); // 100 steps/s²
//stepper->disableOutputs(); // Disable stepper driver.
}
// indicating board initialization and warm up time.
for(uint8_t i=0; i<6; i++)
{
digitalWrite(PIN_LED1, HIGH); // sets the LED1 on
delay(200); // waits for a second
digitalWrite(PIN_LED1, LOW); // set the LED1 off.
delay(200); // waits for a second
}
#ifdef SERIAL_DEBUG
Serial.begin(19200); // Initialize serial port.
Serial.println("Stepper driver being initialized.");
#endif // endif for SERIAL_DEBUG
lcd_i2c.clear();
lcd_i2c.print("Motor Stopped");
// lcd_i2c.print("Speed: (8)100% (12)RPM");
lcd_i2c.setCursor(0, 1); lcd_i2c.print("Speed: ");
lcd_i2c.setCursor(11, 1); lcd_i2c.print("%");
}
void executeMotorCommand(int8_t command_state)
{
switch(command_state)
{
case 0:
//stepper->forceStop();
stepper->stopMove();
break;
case 1:
stepper->runForward();
break;
case 2:
stepper->runBackward();
break;
}
}
uint16_t serialCommands[2];
void loop()
{
// // MUST call the loop() function first before checking button states for all the initiated ezButton objects.
reverse_button.loop();
stop_button.loop();
forward_button.loop();
if(reverse_button.isPressed())
{ motor_command_state=2; lcd_i2c.setCursor(0, 0); lcd_i2c.print("Reverse Run "); executeMotorCommand(motor_command_state);}
if(stop_button.isPressed())
{ motor_command_state=0; lcd_i2c.setCursor(0, 0); lcd_i2c.print("Motor Stopped"); executeMotorCommand(motor_command_state);}
if(forward_button.isPressed())
{ motor_command_state=1; lcd_i2c.setCursor(0, 0); lcd_i2c.print("Forward Run "); executeMotorCommand(motor_command_state);}
if((millis() - lastTimeStamp) > SPEED_UPDATE_INTERVAL) // Check if SPEED_UPDATE_INTERVAL is finished: To update motor speed at regualr pre-define interval.
{
lastTimeStamp = millis();
speed_cmd = analogRead(PIN_SPEED_CMD); // get ADC value from external POT as new speed command. value range 0-1023 :: 0-5V
if(speed_cmd < SPEED_ADC_LBOUND) speed_cmd=SPEED_ADC_LBOUND;
else if(speed_cmd > SPEED_ADC_HBOUND) speed_cmd=SPEED_ADC_HBOUND;
speed_cmd_disp = map(speed_cmd, SPEED_ADC_LBOUND, SPEED_ADC_HBOUND+1, 0, 101); // mapping Speed command ADC value range from SPEED_ADC_LBOUND-SPEED_ADC_HBOUND (in ADC 10-bit value) to 0-100 (in %) to be displayed on LCD.
//speed_cmd = map(speed_cmd, SPEED_ADC_LBOUND, SPEED_ADC_HBOUND+1, 0, SPEED_MAX+1); // mapping Speed command ADC value range from SPEED_ADC_LBOUND-SPEED_ADC_HBOUND (in ADC 10-bit value) to 0-SPEED_MAX (in RPM or RPS) to set current motor speed.
speed_cmd = map(speed_cmd_disp, 0, 100, 0, SPEED_MAX); // mapping Speed command value from 0-100% (derived from external POT at PIN_A0) to 0-SPEED_MAX (in RPM or RPS) to set current motor speed.
set_runSpeed(speed_cmd); // setting new speed for stepper motor.
stepper->applySpeedAcceleration(); // Apply new speed. Without calling this function, there will be no speed update even after set_runSpeed function got executed.
if(speed_cmd==0) stepper->stopMove();
else if((motor_command_state != 0) && (stepper->isRunning() == false)) executeMotorCommand(motor_command_state);
// board running status : LED1 is toggled.
led1_state = !led1_state;
digitalWrite(PIN_LED1, led1_state);
lcd_i2c.setCursor(7, 1); lcd_i2c.print(" ");
lcd_i2c.setCursor(7, 1); lcd_i2c.print(speed_cmd_disp);
}
#ifdef SERIAL_DEBUG
while (Serial.available() > 0)
{
serialCommands[0] = Serial.parseInt();
serialCommands[1] = Serial.parseInt();
// look for the newline. That's the end of your sentence:
if (Serial.read() == '\n')
{
Serial.print("command is:"); Serial.println(serialCommands[0]);
Serial.print("value is:"); Serial.println(serialCommands[1]);
}
}
#endif //endif for SERIAL_DEBUG
/*
#ifdef SERIAL_DEBUG
if (Serial.find("s"))
{
serialCommands[0] = Serial.parseInt();
serialCommands[1] = Serial.parseInt();
Serial.print("command is:"); Serial.println(serialCommands[0]);
Serial.print("value is:"); Serial.println(serialCommands[1]);
}
#endif //endif for SERIAL_DEBUG
*/
}