Stepper motor control panel with keypad
In this video I continue the development of the universal stepper motor control panel. The most important feature is that I added a 16-key keypad to the control panel, so now it is much more easier to enter the input parameters. Because of the new part, I redesigned the front panel and I also designed a 3D-printable enclosure. If you have any suggestions for further features, please let me know in the comment section.
Schematics
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 //this is optional! //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 //i2c / Wire #include <Wire.h> //Defining pins const int RotaryCLK = PB8; //CLK pin on the rotary encoder const int RotaryDT = PB9; //DT pin on the rotary encoder const int RotarySW = PB0; //SW pin on the rotary encoder (Button function) 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 //Keypad variables float buttonTimer = 0; //Timer for avoiding multiple keypresses at a time bool buttonPressed = false; //status which keeps track if a key was pressed char fullString[16] = ""; //concatenated string char pressedCharacter[8] = ""; //recently pressed character on the keypad bool stringAdded = false; //keeps track of the concatenation int convertedNumber = 0; //final number. fullString converted into integer void setup() { //Serial.begin(9600); //i2C - for the I/O module Wire.begin(); Wire.setClock(400000L); //Definition of the pins, remember where you need pinMode(PB9, INPUT); //CLK pinMode(PB8, INPUT); //DT pinMode(PB0, INPUT_PULLUP); //SW 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(RotaryDT), 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.setContrast(58); //Adjusting the contrast of the LCD lcd.clrScr(); //clear the whole display printLCD(); //print the welcome message delay(3000); // show the welcome message for 3 seconds 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(); readKeyPad(); //read the keypad checkInput(); //do something if a key was pressed - UP and DOWN functions are also handled within this function LimitSwitchPressed(); //If you do not have the limit switches connected to the MCU, it will cause it hang!!! /* //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 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 readKeyPad() { if (buttonPressed == true) { if (millis() - buttonTimer > 300) { //The button pressed is only set back to false after 300 ms, so we cannot press a button twice quickly buttonTimer = millis(); buttonPressed = false; } else { //do nothing } } else { //B11101111 Wire.beginTransmission(0x20); //00100000 Wire.write(B11101111); //[P7]B11101111[P0] -> [P7]1110[P4] - activates first row, [P3]1111[P0] - Sets all pins high on the MUX Wire.endTransmission(); Wire.requestFrom(0x20, 1); switch (Wire.read()) { //11101110 - P0 pin went low after pressing the button -> 1 was pressed case 238: //Button 1 //Serial.print("1"); pressedCharacter[0] = '1'; buttonPressed = true; stringAdded = true; break; //11101101 - P1 pin went low after pressing the button -> 2 was pressed case 237: //Button 2 //Serial.print("2"); pressedCharacter[0] = '2'; buttonPressed = true; stringAdded = true; break; //11101011 - P2 pin went low after pressing the button -> 3 was pressed case 235: //Button 3 //Serial.print("3"); pressedCharacter[0] = '3'; buttonPressed = true; stringAdded = true; break; //11100111 - P3 pin went low after pressing the button -> A was pressed case 231: //Button A //Serial.println("A"); pressedCharacter[0] = 'A'; buttonPressed = true; break; } //------------------------------------------- //B11011111 Wire.beginTransmission(0x20); //00100000 Wire.write(B11011111); //[P7]B11011111[P0] -> [P7]1101[P4] - activates second row, [P3]1111[P0] - Sets all pins high on the MUX Wire.endTransmission(); Wire.requestFrom(0x20, 1); switch (Wire.read()) { //11011110 - P0 pin went low after pressing the button -> 2 was pressed case 222: //Button 4 //Serial.print("4"); pressedCharacter[0] = '4'; buttonPressed = true; stringAdded = true; break; case 221: //Button 5 //Serial.print("5"); pressedCharacter[0] = '5'; buttonPressed = true; stringAdded = true; break; case 219: //Button 6 //Serial.print("6"); pressedCharacter[0] = '6'; buttonPressed = true; stringAdded = true; break; case 215: //Button B //Serial.println("B"); pressedCharacter[0] = 'B'; buttonPressed = true; break; } //------------------------------------------- //B10111111 Wire.beginTransmission(0x20); //00100000 Wire.write(B10111111); Wire.endTransmission(); Wire.requestFrom(0x20, 1); switch (Wire.read()) { case 190: //Button 7 //Serial.print("7"); pressedCharacter[0] = '7'; buttonPressed = true; stringAdded = true; break; case 189: //Button 8 //Serial.print("8"); pressedCharacter[0] = '8'; buttonPressed = true; stringAdded = true; break; case 187: //Button 9 //Serial.print("9"); pressedCharacter[0] = '9'; buttonPressed = true; stringAdded = true; break; case 183: //Button C //Serial.println("C"); pressedCharacter[0] = 'C'; buttonPressed = true; break; } //------------------------------------------- //B01111111 Wire.beginTransmission(0x20); //00100000 Wire.write(B01111111); Wire.endTransmission(); Wire.requestFrom(0x20, 1); switch (Wire.read()) { case 126: //Button * //Serial.print("."); pressedCharacter[0] = '.'; buttonPressed = true; break; case 125: //Button 0 //Serial.print("0"); pressedCharacter[0] = '0'; buttonPressed = true; stringAdded = true; break; case 123: //Button # - this is used as the minus sign symbol for negative steps..etc //Serial.print("#"); pressedCharacter[0] = '-'; buttonPressed = true; stringAdded = true; break; case 119: //Button D //Serial.println("D"); pressedCharacter[0] = 'D'; buttonPressed = true; break; } buttonTimer = millis(); } } void checkInput() { //Note: Since we use char arrays, the code is sensitive to the "" and '' differences. We have to use single quote marks if (buttonPressed == true) //if a button was pressed... { if (stringAdded == true) //if a numerical button was pressed... { strcat(fullString, pressedCharacter); //concatenate //strcat(target array, array to be added to the target) //Print the string on the LCD lcd.setFont(Term9x14); lcd.printStr(10, 2, " "); //erase entire the previous content snprintf(buf, 8, "%s", fullString); lcd.printStr(10, 2, buf); //print the new content stringAdded = false; //don't let the code enter this part again } if (pressedCharacter[0] == 'A') //if we pressed the A on the keypad... { //When A is pressed, we want to pass the entered number to the actual menu's value (if it is a valid menu item) convertedNumber = atoi(fullString); //string to integer. Everything is an integer here (no half steps...etc) //Do something based on the menu position switch (menuCounter) { case 0: //maximum speed stepperMaxSpeed = convertedNumber; break; case 1: //acceleration stepperAcceleration = convertedNumber; break; case 2: //absolute steps stepperAbsoluteSteps = convertedNumber; break; case 3: //relative steps stepperRelativeSteps = convertedNumber; break; case 4: //rotary-based steps, we do not operate the buttons break; case 5: //do nothing break; case 6: //Step size stepperStepsize = convertedNumber; break; case 7: //Homing //do nothing break; case 8: //Go To //do nothing break; case 9: //Ping-pong steps stepperPingPongSteps = convertedNumber; break; case 10: //Ping-pong limit switch //Do nothing break; case 11: //Set new zero point //do nothing break; } //Print the string on the LCD lcd.setFont(Term9x14); lcd.printStr(10, 2, " "); //erase the entire previous content snprintf(buf, 8, "%s", fullString); lcd.printStr(10, 2, buf); //print the new content memset(fullString, 0, sizeof fullString); //zero the array convertedNumber = 0; //reset the number to 0 pressedCharacter[0] = ' '; //reset pressed character (this part will be skipped in further iterations until A is pressed again) } else if (pressedCharacter[0] == 'D') //D = delete { if (strlen(fullString)) //if the size is not zero { fullString[strlen(fullString) - 1] = '\0'; //print an "empty character" over the n-1 part of the non-zero item } lcd.setFont(Term9x14); lcd.printStr(10, 2, " "); //erase the previous content snprintf(buf, 8, "%s", fullString); lcd.printStr(10, 2, buf); //print the new, shortened content pressedCharacter[0] = ' '; } //-------------------------------------------------------------------------------------------------------- else if (pressedCharacter[0] == 'B') //up/start button { 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); break; case 1: stepper.moveTo(7500); break; case 2: stepper.moveTo(15400); break; case 3: stepper.moveTo(22300); break; case 4: stepper.moveTo(25400); 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; } 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); } } //-------------------------------------------------------------------------------------------------------- else if (pressedCharacter[0] == 'C') //down/stop button { 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(); } 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 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); } }
Enclosure and front panel 3D files
Disclaimer: Dimensional inaccuracies might occur which might cause slight misalignments or imprecise fit. I do not take any responsibilities for these issues.