Updates for the stepper motor control panel

In this video I show some new features of my stepper motor control box and I also show some different implementations of an oscillating motion. Furthermore, I share a nice little gadget which can turn your stepper motor salvaged from a DVD drive into a nice linear actuator. The oscillation is either done by predefined steps or by the triggering of the limit switches. I also explain the parts of the whole code which take care of the oscillating motions. The idea behind the whole control box is to create a universal control panel for the most popular stepper motor drivers for hobbyists.



Schematics

I use the whole system at 3.3 V. This is particularly important if you use the Nokia 5110 LCD because it typically works at 3.3 V and it can be damaged at 5 V. The circuit itself is relatively simple: there are 2 buttons, a rotary encoder with a button and two limit switches. For stepper motors, I only indicated the step and direction pins.

I use the whole system at 3.3 V. This is particularly important if you use the Nokia 5110 LCD because it typically works at 3.3 V and it can be damaged at 5 V. The circuit itself is relatively simple: there are 2 buttons, a rotary encoder with a button and two limit switches. For stepper motors, I only indicated the step and direction pins.



Source code

//The LCD-related code was based on the following library:
//https://github.com/cbm80amiga/N5110_GUI_encoder_demo_STM32

//Defining the pins for the Nokia 5110 LCD
#define N5110_RST       PA0
#define N5110_CS        PA4
#define N5110_DC        PA1
#define N5110_BACKLIGHT PA2

//Nokia pins:
//RST: PA0
//CS: PA4
//DC: PA1
//BL: PA2 (optional)
//DIN: PA7
//CLK: PA5

#include "N5110_SPI.h"
#if USESPI==1
#include <SPI.h>
#endif
N5110_SPI lcd(N5110_RST, N5110_CS, N5110_DC); // RST,CS,DC
#include "c64enh_font.h" //smaller font
#include "term9x14_font.h" //larger font
//make sure that the font files are in the same folder as the project file!

//AccelStepper
#include <AccelStepper.h>
AccelStepper stepper(1, PA9, PA8);// pulses/steps 9; Direction 8

//Defining pins
const int RotaryCLK = PB9; //CLK pin on the rotary encoder
const int RotaryDT = PB8; //DT pin on the rotary encoder
const int RotarySW = PB7; //SW pin on the rotary encoder (Button function)
const int UpButton = PB12; //Up button which is also a start button
const int DownButton = PB13; //Down button which is also a stop button
const int LimitSwitch_1 = PB11; //Input for the limit switch
const int LimitSwitch_2 = PB10; //Input for the limit switch
const int enablePin = PB1; //enable pin for the stepper drivers - not yet implemented!

//Statuses of the DT and CLK pins on the encoder
int CLKNow;
int CLKPrevious;
int DTNow;
int DTPrevious;

//Timing related variables, act mainly as debouncing
float RotaryTime1 = 0; //Rotary encoder timing
float RotaryTime2 = 0;
unsigned long previousInterrupt; //Button timing
float LCDRefreshTimer = 0;

//Menu-related variables
//Values
int RotaryButtonValue; //Pressed or not pressed rotary switch
volatile int menuCounter = 0; //this is for counting the main menu (menu item number)
volatile int stepperMaxSpeed = 400; //for setMaxSpeed
volatile int stepperSpeed = 400; //for setSpeed
volatile int stepperAcceleration = 800; //for setAcceleration
volatile int stepperAbsoluteSteps; //moveTo()
volatile int stepperRelativeSteps; //move()
volatile int stepperRotarySteps; // This keeps track of the steps done with the rotary encoder
volatile int stepperButtonSteps; // This keeps track of the steps done with the buttons
volatile int stepperGoToLocation; //Go to steps
volatile int stepperPingPongSteps; //amount of steps for Ping-pong demo
volatile int stepperStepsize = 1; //Step size for changing the variable. One click on the encoder will in/decrease with this amount
int stepperPosition = 0; //position of the stepper motor, measured in steps
int previousStepperPosition = 0; //This helps us to update the display
int pingpongInitialPosition = 0; //initial position for the step-based ping-pong
int pingpongFinalPosition = 0; //final position for the step-based ping-pong
int limitSwitchTempPosition = 0; //temporary position for remembering the position when the limit switch was triggered

//booleans
bool stepperMaxSpeed_Selected = false; //for setMaxSpeed
bool stepperSpeed_Selected = false; //for setSpeed
bool stepperSpeedRun = false; //for "indefinite" running
bool stepperAcceleration_Selected = false; //for setAcceleration
bool stepperAbsoluteSteps_Selected = false; //moveTo()
bool stepperRelativeSteps_Selected = false; //move()
bool rotaryStepping_Selected = false; //precisely step the motor with the rotary encoder
bool buttonStepping_Selected = false; //precisely step the motor with the buttons (UpButton/DownButton)
bool stepperStepsize_Selected = false; //Step size menu
bool stepperSetZero_Selected = false; //Reset to 0 menu
bool stepperGoTo_Selected = false; //Go To function
bool pingpongStep_Selected = false; //ping-pong demo function - based on steps
bool pingpongStep_Enabled = false; //step-based ping pong demo enable/disable swtich
bool pingpongLimit_Selected = false; //ping pong demo function - based on limit switch
bool pingpongLimit_Enabled = false; //limitswitch based pingpongdemo
bool stepperhoming_Selected = false; //Homing with limit switch
bool menuChanged = false; //Switched between menus
bool valueChanged = false; //changed the value of a menu item
bool updateValueSelection = false; //if we want to change the value of a variable, it becomes true
bool pingpong_CW = false; //keeps track of the direction inside the limit switch-based pingpong
bool pingpong_CCW = false; //keeps track of the direction inside the limit switch-based pingpong

char buf[25]; //buffer for printing on the LCD

void setup()
{
  //Definition of the pins, remember where you need
  pinMode(PB9, INPUT); //CLK
  pinMode(PB8, INPUT); //DT
  pinMode(PB7, INPUT_PULLUP); //SW
  pinMode(PB12, INPUT); //UP
  pinMode(PB13, INPUT); //DOWN
  pinMode(PB11, INPUT); //LimitSwitch_1 (default is 1 for me)
  pinMode(PB10, INPUT); //LimitSwitch_2 (default is 1 for me)
  pinMode(enablePin, OUTPUT);
  digitalWrite(enablePin, LOW);

  //Store states
  CLKPrevious = digitalRead(RotaryCLK);
  DTPrevious = digitalRead(RotaryDT);

  //Rotary encoder interrupts
  attachInterrupt(digitalPinToInterrupt(RotaryCLK), RotaryEncoder, CHANGE);

  //Stepper setup
  stepper.setSpeed(stepperSpeed); //SPEED = Steps / second
  stepper.setMaxSpeed(stepperMaxSpeed); //SPEED = Steps / second
  stepper.setAcceleration(stepperAcceleration); //ACCELERATION = Steps /(second)^2

  lcd.init(); //initialize LCD
  lcd.clrScr(); //clear the whole display
  printLCD(); //print the welcome message

  //digitalRead() UP and DOWN = 0, Limit 1 and 2 = 1 - my default pin readings

  updateMenuPosition();
  updateValue();
}

void loop()
{
  if (menuChanged == true)
  {
    updateMenuPosition();
  }

  if (valueChanged == true )
  {
    updateValue();
  }

  if (updateValueSelection == true)
  {
    updateSelection();
  }

  if (pingpongStep_Enabled == true)
  {
    PingPongStep();
  }

  if (pingpongLimit_Enabled == true)
  {
    PingPongLimit();
  }


  stepper.run(); //If a step is due, this function will do a step in each loop() iteration

  //Looping buttons
  CheckRotaryButton();
  UpButtonPressed();
  DownButtonPressed();
  LimitSwitchPressed();

  /*
    //The below part takes care of the position
    stepperPosition = stepper.currentPosition();

    if (stepperPosition != previousStepperPosition)
    {
    updatePosition(); //Pos. on the bottom of the LCD
    updateValue(); //Pos or value in the middle of the screen
    previousStepperPosition = stepper.currentPosition();
    //^if the motor does not move in the next loop() iteration, the code skips this part of the code
    }
  */ //I removed this on 2021-05-09 because the currentPosition() is very slow! I just use a timer

  if (millis() - LCDRefreshTimer > 500)
  {
    stepperPosition = stepper.currentPosition();
    updatePosition(); //Pos. on the bottom of the LCD
    //updateValue(); //Pos or value in the middle of the screen
    LCDRefreshTimer = millis();
  }
}


void RotaryEncoder()
{
  if (stepperMaxSpeed_Selected == true)
  {
    CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin
    // If last and current state of CLK are different, then a pulse occurred
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
      // If the DT state is different than the CLK state then
      // the encoder is rotating in A direction, so we increase
      if (digitalRead(RotaryDT) != CLKNow)
      {
        stepperMaxSpeed = stepperMaxSpeed + stepperStepsize; //1 step size increment
      }
      else
      {
        if (stepperMaxSpeed < 2)
        {
          //Don't decrease further, it should be at least 1
        }
        else
        {
          stepperMaxSpeed = stepperMaxSpeed - stepperStepsize;  //1 step size decrement
        }
      }
      valueChanged = true;
    }
    CLKPrevious = CLKNow;  // Store last CLK state
  }
  //Stepper acceleration
  else if (stepperAcceleration_Selected == true)
  {
    CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin
    // If last and current state of CLK are different, then a pulse occurred
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
      // If the DT state is different than the CLK state then
      // the encoder is rotating in A direction, so we increase
      if (digitalRead(RotaryDT) != CLKNow)
      {
        stepperAcceleration = stepperAcceleration + stepperStepsize; //1 step size increment
      }
      else
      {
        stepperAcceleration = stepperAcceleration - stepperStepsize;     //1 step size decrement
      }
      valueChanged = true;
    }
    CLKPrevious = CLKNow;  // Store last CLK state
  }
  //Absolute rotation
  else if (stepperAbsoluteSteps_Selected == true)
  {
    CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin
    // If last and current state of CLK are different, then a pulse occurred
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
      // If the DT state is different than the CLK state then
      // the encoder is rotating in A direction, so we increase
      if (digitalRead(RotaryDT) != CLKNow)
      {
        stepperAbsoluteSteps = stepperAbsoluteSteps + stepperStepsize; //1 step size increment
      }
      else
      {
        stepperAbsoluteSteps = stepperAbsoluteSteps - stepperStepsize;  //1 step size decrement
      }
      valueChanged = true;
    }
    CLKPrevious = CLKNow;  // Store last CLK state
  }
  //Relative rotation
  else if (stepperRelativeSteps_Selected == true)
  {
    CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin
    // If last and current state of CLK are different, then a pulse occurred
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
      // If the DT state is different than the CLK state then
      // the encoder is rotating in A direction, so we increase
      if (digitalRead(RotaryDT) != CLKNow)
      {
        stepperRelativeSteps = stepperRelativeSteps + stepperStepsize; //1 step size increment
      }
      else
      {
        stepperRelativeSteps = stepperRelativeSteps - stepperStepsize; //1 step size decrement
      }
      valueChanged = true;
    }
    CLKPrevious = CLKNow;  // Store last CLK state
  }
  //Rotary movement
  else if (rotaryStepping_Selected == true)
  {
    CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin
    // If last and current state of CLK are different, then a pulse occurred
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
      // If the DT state is different than the CLK state then
      // the encoder is rotating in A direction, so we increase
      if (digitalRead(RotaryDT) != CLKNow)
      {
        stepperRotarySteps = stepper.currentPosition() + stepperStepsize; //1 step size increment relative to the current position
      }
      else
      {
        stepperRotarySteps = stepper.currentPosition() - stepperStepsize; //1 step size decrement relative to the current position
      }
      valueChanged = true;
    }
    CLKPrevious = CLKNow;  // Store last CLK state
    stepper.moveTo(stepperRotarySteps);
  }
  else if (buttonStepping_Selected == true)
  {
    //do nothing
  }
  //Stepper step size
  else if (stepperStepsize_Selected == true)
  {
    CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin
    // If last and current state of CLK are different, then a pulse occurred
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
      // If the DT state is different than the CLK state then
      // the encoder is rotating in A direction, so we increase
      if (digitalRead(RotaryDT) != CLKNow)
      {
        stepperStepsize++; //1 step increment of the step size
      }
      else
      {
        if (stepperStepsize < 2)
        {
          //If it goes below 2 (=1), it won't be decreased anymore further
        }
        else
        {
          stepperStepsize--;    //1 step decrement of the step size
        }
      }
    }
    CLKPrevious = CLKNow;  // Store last CLK state
    valueChanged = true;
  }
  //-------------------------------------------------------------------------------
  //GOTO
  else if (stepperGoTo_Selected == true)
  {
    CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin
    // If last and current state of CLK are different, then a pulse occurred
    if (CLKNow != CLKPrevious && CLKNow == 1)
    {
      // If the DT state is different than the CLK state then
      // the encoder is rotating in A direction, so we increase

      if (digitalRead(RotaryDT) != CLKNow)
      {
        if (stepperGoToLocation < 4) //5 menu items (0, 1, 2....4)
        {
          stepperGoToLocation++;
        }
        else
        {
          stepperGoToLocation = 0; //0 comes after 4, so we move in a "ring"
        }
      }
      else
      {
        // Encoder is rotating CW so decrease
        if (stepperGoToLocation > 0)
        {
          stepperGoToLocation--;
        }
        else
        {
          stepperGoToLocation = 4; //4 comes after 0 when we decrease the numbers
        }
      }
      valueChanged = true;
    }
    CLKPrevious = CLKNow;  // Store last CLK state
  }
  //-------------------------------------------------------------------------------
  //Ping-pong based on user input
  else if (pingpongStep_Selected == true)
  {
    CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin
    // If last and current state of CLK are different, then a pulse occurred
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
      // If the DT state is different than the CLK state then
      // the encoder is rotating in A direction, so we increase
      if (digitalRead(RotaryDT) != CLKNow)
      {
        stepperPingPongSteps = stepperPingPongSteps + stepperStepsize; //1 step increment of the step size
      }
      else
      {
        stepperPingPongSteps = stepperPingPongSteps - stepperStepsize;
      }
      valueChanged = true;
    }
    CLKPrevious = CLKNow;  // Store last CLK state
  }
  //-------------------------------------------------------------------------------
  else if (pingpongLimit_Selected == true)
  {
    //do nothing
  }
  //-------------------------------------------------------------------------------
  //Homing
  else if (stepperhoming_Selected == true)
  {
    //do nothing
  }
  //Set new zeropoint
  else if ( stepperSetZero_Selected == true)
  {
    //do nothing
  }
  else //MENU COUNTER----------------------------------------------------------------------------
  {
    CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin
    // If last and current state of CLK are different, then a pulse occurred
    if (CLKNow != CLKPrevious  && CLKNow == 1)
    {
      // If the DT state is different than the CLK state then
      // the encoder is rotating CCW so increase
      if (digitalRead(RotaryDT) != CLKNow)
      {
        if (menuCounter < 11) //12 menu items (0, 1, 2....11)
        {
          menuCounter++;
        }
        else
        {
          menuCounter = 0; //0 comes after 11, so we move in a "ring"
        }
        menuChanged = true;

      }
      else
      {
        // Encoder is rotating CW so decrease
        if (menuCounter > 0)
        {
          menuCounter--;
        }
        else
        {
          menuCounter = 11; //9 comes after 0 when we decrease the numbers
        }
        menuChanged = true;
      }
    }
    CLKPrevious = CLKNow;  // Store last CLK state
  }
}

void printLCD() //Prints a "welcome screen"
{
  lcd.setFont(c64enh);
  lcd.printStr(ALIGN_CENTER, 0, "Stepper");
  lcd.printStr(ALIGN_CENTER, 1, "Controller");
  lcd.printStr(ALIGN_CENTER, 3, "Please");
  lcd.printStr(ALIGN_CENTER, 4, "Wait");
  lcd.printStr(ALIGN_CENTER, 5, "3 seconds...");
}

void CheckRotaryButton()
{
  RotaryButtonValue = digitalRead(RotarySW); //read the button state

  if (RotaryButtonValue == 0) //0 and 1 can differ based on the wiring
  {
    RotaryTime1 = millis();
    if (RotaryTime1 - RotaryTime2 > 1000)
    {
      switch (menuCounter)
      {
        case 0:
          stepperMaxSpeed_Selected = !stepperMaxSpeed_Selected;  //we change the status of the variable to the opposite
          if (stepperMaxSpeed_Selected == false)//exiting
          {
            stepper.stop(); //stop upon exiting the menu
          }
          updateValueSelection = true;
          break;
        //
        case 1:
          stepperAcceleration_Selected = !stepperAcceleration_Selected;
          updateValueSelection = true;
          break;
        //
        case 2:
          stepperAbsoluteSteps_Selected = !stepperAbsoluteSteps_Selected;
          if (stepperAbsoluteSteps_Selected == false)//exiting
          {
            stepper.stop(); //stop upon exiting the menu
          }
          updateValueSelection = true;
          break;
        //
        case 3:
          stepperRelativeSteps_Selected = !stepperRelativeSteps_Selected;
          if (stepperRelativeSteps_Selected == false)//exiting
          {
            stepper.stop(); //stop upon exiting the menu
          }
          updateValueSelection = true;
          break;
        //
        case 4:
          rotaryStepping_Selected = !rotaryStepping_Selected;
          updateValueSelection = true;
          valueChanged = true;
          break;
        //
        case 5:
          buttonStepping_Selected = !buttonStepping_Selected;
          updateValueSelection = true;
          valueChanged = true;
          break;
        //
        case 6:
          stepperStepsize_Selected = !stepperStepsize_Selected;
          updateValueSelection = true;
          break;
        //
        case 7:
          stepperhoming_Selected = !stepperhoming_Selected;
          updateValueSelection = true;
          break;
        //
        case 8:
          stepperGoTo_Selected = !stepperGoTo_Selected;
          updateValueSelection = true;
          break;
        //
        case 9:
          pingpongStep_Selected = !pingpongStep_Selected;
          updateValueSelection = true;
          break;
        //
        case 10:
          pingpongLimit_Selected = !pingpongLimit_Selected;
          updateValueSelection = true;
          break;
        //
        case 11:
          stepperSetZero_Selected = true; //this always has to be set to true and we set it to false somewhere else
          updateValueSelection = true;
          break;
      }
      RotaryTime2 = millis();
      menuChanged = true; //Refresh LCD after changing the value of the menu
    }
  }
}

void updateMenuPosition()
{
  lcd.clrScr();//delete the entire screen

  switch (menuCounter) //this checks the value of the counter (0, 1, 2 or 3)
  {
    //MAX SPEED
    case 0:
      lcd.setFont(c64enh); //set the font (small one)
      lcd.printStr(ALIGN_CENTER, 0, "            "); //erase previous text
      lcd.printStr(ALIGN_CENTER, 0, "Max speed"); //print new text
      lcd.setFont(Term9x14); //Set another (larger) font
      snprintf(buf, 8, "%d", stepperMaxSpeed); //the decimal value of the variable is in the buf
      lcd.printStr(10, 2, buf); //set the cursor to 2nd line 10th pixel and print the buffer
      break;
    //-------------------------------
    //ACCELERATION
    case 1:
      lcd.setFont(c64enh);
      lcd.printStr(ALIGN_CENTER, 0, "            ");
      lcd.printStr(ALIGN_CENTER, 0, "Acceleration");
      lcd.setFont(Term9x14);
      snprintf(buf, 8, "%d", stepperAcceleration);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //ABSOLUTE STEPPING
    case 2:
      lcd.setFont(c64enh);
      lcd.printStr(ALIGN_CENTER, 0, "            ");
      lcd.printStr(ALIGN_CENTER, 0, "Absolute");
      lcd.setFont(Term9x14);
      snprintf(buf, 8, "%d", stepperAbsoluteSteps);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //RELATIVE STEPS
    case 3:
      lcd.setFont(c64enh);
      lcd.printStr(ALIGN_CENTER, 0, "            ");
      lcd.printStr(ALIGN_CENTER, 0, "Relative");
      lcd.setFont(Term9x14);
      snprintf(buf, 8, "%d", stepperRelativeSteps);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //MOVEMENT WITH ROTARY ENCODER
    case 4:
      lcd.setFont(c64enh);
      lcd.printStr(ALIGN_CENTER, 0, "            ");
      lcd.printStr(ALIGN_CENTER, 0, "Rotary-move");
      lcd.setFont(Term9x14);
      snprintf(buf, 8, "%d", stepperRotarySteps);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //MOVEMENT WITH BUTTONS
    case 5:
      lcd.setFont(c64enh);
      lcd.printStr(ALIGN_CENTER, 0, "            ");
      lcd.printStr(ALIGN_CENTER, 0, "Button-move");
      lcd.setFont(Term9x14);
      snprintf(buf, 8, "%d", stepperRelativeSteps);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //STEPSIZE
    case 6:
      lcd.setFont(c64enh);
      lcd.printStr(ALIGN_CENTER, 0, "            ");
      lcd.printStr(ALIGN_CENTER, 0, "Step size");
      lcd.setFont(Term9x14);
      snprintf(buf, 8, "%d", stepperStepsize);
      lcd.printStr(10, 2, buf);
      break;//-------------------------------
    //HOMING
    case 7:
      lcd.setFont(c64enh);
      lcd.printStr(ALIGN_CENTER, 0, "            ");
      lcd.printStr(ALIGN_CENTER, 0, "Homing");
      lcd.setFont(Term9x14);
      snprintf(buf, 8, "%d", stepper.currentPosition());
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //GOTO
    case 8:
      lcd.setFont(c64enh);
      lcd.printStr(ALIGN_CENTER, 0, "            ");
      lcd.printStr(ALIGN_CENTER, 0, "Go To...");
      lcd.setFont(Term9x14);
      snprintf(buf, 8, "%d", stepperGoToLocation);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //PINGPONG - STEP
    case 9:
      lcd.setFont(c64enh);
      lcd.printStr(ALIGN_CENTER, 0, "            ");
      lcd.printStr(ALIGN_CENTER, 0, "Ping-Pong-s");
      lcd.setFont(Term9x14);
      snprintf(buf, 8, "%d", stepperPingPongSteps);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //PINGPONG - LIMIT
    case 10:
      lcd.setFont(c64enh);
      lcd.printStr(ALIGN_CENTER, 0, "            ");
      lcd.printStr(ALIGN_CENTER, 0, "Ping-Pong-l");
      //lcd.setFont(Term9x14);
      //snprintf(buf, 8, "%d", stepperGoToLocation);
      //lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //RESET
    case 11:
      lcd.setFont(c64enh);
      lcd.printStr(ALIGN_CENTER, 0, "            ");
      lcd.printStr(ALIGN_CENTER, 0, "Set zero");
      break;
  }
  //"Global" value
  lcd.setFont(c64enh);
  snprintf(buf, 8, "%s", "Pos: ");
  lcd.printStr(0, 5, buf);
  snprintf(buf, 8, "%d", stepperPosition);
  lcd.printStr(30, 5, buf);
  //
  menuChanged = false; //next loop() iteration will not enter again
}

void updateValue()
{
  switch (menuCounter) //this checks the value of the counter (0, 1, 2 or 3)
  {
    //Max speed menu
    case 0:
      stepper.setMaxSpeed(stepperMaxSpeed);
      lcd.setFont(Term9x14);
      lcd.printStr(10, 2, "        ");
      snprintf(buf, 8, "%d", stepperMaxSpeed);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //Acceleration menu
    case 1:
      stepper.setAcceleration(stepperAcceleration);
      lcd.setFont(Term9x14);
      lcd.printStr(10, 2, "        ");
      snprintf(buf, 8, "%d", stepperAcceleration);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //Absolute movement - moveTo()
    case 2:
      lcd.setFont(Term9x14);
      lcd.printStr(10, 2, "        ");
      snprintf(buf, 8, "%d", stepperAbsoluteSteps);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //Relative movement - move()
    case 3:
      lcd.setFont(Term9x14);
      //Command values
      lcd.printStr(10, 2, "        "); //block 8 in line 2
      snprintf(buf, 8, "%d", stepperRelativeSteps);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //Rotary encode movement - step by step
    case 4:
      lcd.setFont(Term9x14);
      lcd.printStr(10, 2, "        ");
      snprintf(buf, 8, "%d", stepperPosition); //location
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //Button movement - step by step
    case 5:
      lcd.setFont(Term9x14);
      lcd.printStr(10, 2, "        ");
      snprintf(buf, 8, "%d", stepperPosition); //location
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //Step size menu - increment-decrement
    case 6:
      lcd.setFont(Term9x14);
      lcd.printStr(10, 2, "        ");
      snprintf(buf, 8, "%d", stepperStepsize);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //GoTo menu - increment-decrement location ID
    case 8:
      lcd.setFont(Term9x14);
      lcd.printStr(10, 2, "        ");
      snprintf(buf, 8, "%d", stepperGoToLocation);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //Ping-Pong - Step-based
    case 9:
      lcd.setFont(Term9x14);
      lcd.printStr(10, 2, "        ");
      snprintf(buf, 8, "%d", stepperPingPongSteps);
      lcd.printStr(10, 2, buf);
      break;
    //-------------------------------
    //Ping-Pong - LimitSwitch-based
    case 10:
      //empty
      break;
      //-------------------------------
  }
  //"Global" values
  //this is always shown on the bottom of the display
  lcd.setFont(c64enh);
  lcd.printStr(30, 5, "        ");
  snprintf(buf, 8, "%d", stepperPosition);
  lcd.printStr(30, 5, buf);
  //
  valueChanged = false; //next loop() iteration will not enter again
}

void updatePosition()
{
  lcd.setFont(c64enh);
  lcd.printStr(30, 5, "        ");
  snprintf(buf, 8, "%d", stepperPosition);
  lcd.printStr(30, 5, buf);
}

void updateSelection()
{

  lcd.setFont(Term9x14); //Large font size for the cursor (same as of the values)

  if (stepperMaxSpeed_Selected == true)
  {
    snprintf(buf, 8, "%s", ">"); //print the ">" as the cursor, the ">" indicates the selection/activation
    lcd.printStr(0, 2, buf); //put it on the display: 2nd line 0 pixel
  }
  else if (stepperAcceleration_Selected == true)
  {
    snprintf(buf, 8, "%s", ">");
    lcd.printStr(0, 2, buf);
  }
  else if (stepperAbsoluteSteps_Selected == true)
  {
    snprintf(buf, 8, "%s", ">");
    lcd.printStr(0, 2, buf);
  }
  else if (stepperRelativeSteps_Selected == true)
  {
    snprintf(buf, 8, "%s", ">");
    lcd.printStr(0, 2, buf);
  }
  else if (rotaryStepping_Selected == true)
  {
    snprintf(buf, 8, "%s", ">");
    lcd.printStr(0, 2, buf);
  }
  else if (buttonStepping_Selected == true)
  {
    snprintf(buf, 8, "%s", ">");
    lcd.printStr(0, 2, buf);
  }
  else if (stepperStepsize_Selected == true)
  {
    snprintf(buf, 8, "%s", ">");
    lcd.printStr(0, 2, buf);
  }
  else if (stepperhoming_Selected == true)
  {
    snprintf(buf, 8, "%s", "Homing");
    lcd.printStr(0, 2, buf);
  }
  else if (stepperGoTo_Selected == true)
  {
    snprintf(buf, 8, "%s", ">");
    lcd.printStr(0, 2, buf);
  }
  else if (pingpongStep_Selected == true)
  {
    snprintf(buf, 8, "%s", ">");
    lcd.printStr(0, 2, buf);
  }
  else if (pingpongLimit_Selected == true)
  {
    snprintf(buf, 8, "%s", "Start!");
    lcd.printStr(0, 2, buf);
  }
  else if (stepperSetZero_Selected == true)
  {
    snprintf(buf, 8, "%s", "Reset!"); //we indicate that the position was reset to 0 by printing a message
    lcd.printStr(0, 2, buf);
    stepper.setCurrentPosition(0); //set the current position to zero
    stepperSetZero_Selected = false; //we reset this here
  }
  else
  {
    snprintf(buf, 8, "%s", " ");
    lcd.printStr(0, 2, buf);
  }
  updateValueSelection = false; //next loop() iteration will not enter
}

void UpButtonPressed()
{
  if (digitalRead(UpButton) == 1) //1 or 0, depends on your wiring also!
  {
    if (millis() - previousInterrupt > 300) //debounce, sort of. If another click comes within 300 ms, we don't care about it
    {
      if (stepperMaxSpeed_Selected == true) //runSpeed()
      {
        //Runs the motor "indefinitely", until it stopped by the user
        stepper.move(999999999); //some really large value, for demonstration
      }

      else if (stepperRelativeSteps_Selected == true) //move()
      {
        stepper.move(stepperRelativeSteps);   //start relative stepping
      }

      else if (stepperAbsoluteSteps_Selected == true) //moveTo()
      {
        stepper.moveTo(stepperAbsoluteSteps); //start absolute stepping
      }

      else if (stepperhoming_Selected == true)
      {
        //homing part - negative direction
        while (digitalRead(LimitSwitch_1) == 1) //0 or 1, depends on the wiring!
        {
          stepper.setSpeed(-400);
          stepper.runSpeed();
        }

        //parking part - positive direction
        while (digitalRead(LimitSwitch_1) == 0) //0 or 1, depends on the wiring!
        {
          stepper.setSpeed(200);
          stepper.runSpeed();
        }

        stepper.setCurrentPosition(0); //reset the position to 0

        //Message to the user about the completion of the homing process
        lcd.setFont(Term9x14);
        snprintf(buf, 8, "%s", "Parked!");
        lcd.printStr(0, 2, buf);

        //Note: Homing is only implemented for one limit switch because it does not make sense to have 2 home positions

      }

      else if (stepperGoTo_Selected == true)
      {
        //List of programatically predefined positions
        //The positions are always measured from the limit switch-defined home position
        switch (stepperGoToLocation)
        {
          case 0:
            stepper.moveTo(4300); //~ 4.3 cm from home
            break;
          case 1:
            stepper.moveTo(7500); //~ 7.5 cm from home
            break;
          case 2:
            stepper.moveTo(15400); //~ 15.4 cm from home
            break;
          case 3:
            stepper.moveTo(22300); //~ 22.3 cm from home
            break;
          case 4:
            stepper.moveTo(25400); //~ 25.4 cm from home
            break;

          default:
            //
            break;
        }
        //Note: with the ball-screw+linear rail, 1 turn results in 4 mm linear displacement
        //1 turn = 400 steps (with my MS setting), so 1 mm = 100 steps.
      }

      else if (pingpongStep_Selected == true)
      {
        pingpongStep_Enabled = true; //Enable stepping based ping-pong demo
        pingpongInitialPosition = stepper.currentPosition();
        pingpongFinalPosition = pingpongInitialPosition + stepperPingPongSteps;
      }

      else if (pingpongLimit_Selected == true)
      {
        stepper.move(999999999); //some really large value, for demonstration
        pingpongLimit_Enabled = true;
      }

      previousInterrupt = millis(); //update the value so the counting from 300 ms is started over from 0
    }
    else if (buttonStepping_Selected == true)
    {
      //buttonstepping does not care about the 300 ms time limit
      //a little more explanation in the DownButtonPressed() function
      stepperButtonSteps = stepper.currentPosition() + stepperStepsize; //increase the target position by 1 stepsize
      stepper.moveTo(stepperButtonSteps);
    }
  }
}

void DownButtonPressed()
{
  if (digitalRead(DownButton) == 1) //1 or 0, depends on your wiring also!
  {
    if (millis() - previousInterrupt > 300)
    {
      if (stepperMaxSpeed_Selected == true) //runSpeed()
      {
        stepper.stop(); //stop and note down the position (next line)
        stepperPosition = stepper.currentPosition();
      }

      else if (stepperRelativeSteps_Selected == true) //move()
      {
        stepper.stop();
        stepperPosition = stepper.currentPosition();
      }

      else if (stepperAbsoluteSteps_Selected == true) //moveTo()
      {
        stepper.stop();
        stepperPosition = stepper.currentPosition();
      }

      else if (stepperhoming_Selected == true) //Homing (interrupted)
      {
        stepper.stop();
        stepperPosition = stepper.currentPosition();
      }

      else if (pingpongStep_Selected == true)
      {
        pingpongStep_Enabled = false;
        stepper.stop();
        stepperPosition = stepper.currentPosition();
      }

      else if (pingpongLimit_Selected == true)
      {
        pingpongLimit_Enabled = false;
        stepper.stop();
        stepperPosition = stepper.currentPosition();
      }

      else if (stepperGoTo_Selected == true)
      {
        stepper.stop();
        stepperPosition = stepper.currentPosition();
      }

      previousInterrupt = millis(); //update the value so the counting from 300 ms is started over from 0

    }
    else if (buttonStepping_Selected == true)
    {
      //Optionally, this part should also be in a timing part, but then the time between two steps is limited.
      //On the other hand, that can be good if you just want one step per buttonclick
      //Otherwise, pressing the button continuously will still move the motor slowly, but even 1 click will cause several steps

      //We always want to move 1 stepsize away from the current position
      stepperButtonSteps = stepper.currentPosition() - stepperStepsize; //decrease the target position by 1 stepsize
      stepper.moveTo(stepperButtonSteps);
    }
  }
}

void LimitSwitchPressed()
{
  if (pingpongLimit_Enabled == false)
  {
    //--- Limit switch 1
    if (digitalRead(LimitSwitch_1) == 0) //0 or 1, depends on the wiring!
    {
      if (millis() - previousInterrupt > 300)
      {
        limitSwitchTempPosition = stepper.currentPosition(); //we save the position where the limit switch was hit
        stepper.stop(); //"soft" stop - decelerates to 0.
        stepper.runToPosition();
        previousInterrupt = millis();
        //Alternatively we can make it to go to a specific place:
        //stepper.moveTo(0); //This goes back to absolute 0, which is technically the homing

        while (digitalRead(LimitSwitch_1) == 0) //0 or 1, depends on the wiring!
        {
          stepper.setSpeed(-200);
          stepper.runSpeed();
        }
        stepper.setCurrentPosition(limitSwitchTempPosition); //we set the position where the limit switch was hit
      }
    }

    //--- Limit switch 2
    if (digitalRead(LimitSwitch_2) == 0) //0 or 1, depends on the wiring!
    {
      if (millis() - previousInterrupt > 300)
      {
        limitSwitchTempPosition = stepper.currentPosition(); //we save the position where the limit switch was hit
        stepper.stop(); //"soft" stop - decelerates to 0.
        stepper.runToPosition();
        previousInterrupt = millis();
        //Alternatively we can make it to go to a specific place:
        //stepper.moveTo(0); //This goes back to absolute 0, which is technically the homing

        while (digitalRead(LimitSwitch_2) == 0) //0 or 1, depends on the wiring!
        {
          stepper.setSpeed(200);
          stepper.runSpeed();
        }
        stepper.setCurrentPosition(limitSwitchTempPosition); //we set the position where the limit switch was hit
      }
    }
  }
  else
  {
    //do nothing
  }
}

void PingPongStep()
{
  if (pingpong_CW == false) //CW rotation is not yet done
  {
    stepper.moveTo(pingpongFinalPosition); //set a target position, it should be an absolute. relative (move()) leads to "infinite loop"

    if (stepper.distanceToGo() == 0) //When the above number of steps are completed, we manipulate the variables
    {
      pingpong_CW = true; //CW rotation is now done
      pingpong_CCW = false; //CCW rotation is not yet done - this allows the code to enter the next ifs
    }
  }

  if (pingpong_CW == true && pingpong_CCW == false) //CW is completed and CCW is not yet done
  {
    stepper.moveTo(pingpongInitialPosition); //Absolute position

    if (stepper.distanceToGo() == 0) //When the number of steps are completed
    {
      pingpong_CCW = true; //CCW is now done
      pingpong_CW = false; //CW is not yet done. This allows the code to enter the first if again!
    }
  }
}


void PingPongLimit()
{
  if (digitalRead(LimitSwitch_1) == 0)
  {
    stepper.move(-99999999999);
  }

  if (digitalRead(LimitSwitch_2) == 0)
  {
    stepper.move(99999999999);
  }
}


Printable models for the linear carriage

Notice: There is a chance that the stepper motor and other parts you salvaged from the DVD rom differs from the one I used.


Previous
Previous

Coil winder project - Part 3 - Proof of concept

Next
Next

AS5048A 14-bit magnetic position encoder