"Manual" direction control for 3 steppers, UNO

Hello everybody!

I built up a tabletop CNC and would like to control the movements manually since it takes a lot of time to make a program for a small job. I need to be able to move each axis forward and backwards with a (continuous) push of a button. X+, X-, Y+. Y-, Z+ and Z- would be the buttons and the machine would move accordingly. A possibility to push simultaneously for example X+ and Y+ for a 45° move would be neat too. And if there would be a pot to adjust the speed (for all directions only one pot) it would be perfect.

I have absolutely no idea of coding so I'm asking now if someone could have already a solution for this probably a very simple code, or a good start point from where I could really manage to make it.

Unlike coding I can do wiring. I used Arduino Uno and Easydrivers for this GRBL driven CNC. Now I want to rewire the Arduino for the manual feed system. So basically everything is ready but the code missing.

I was planning to use 2-channels push buttons for this so that one channel would control the direction signal and the other the step signal, and the Arduino would just constantly output the "reverse direction signal" that I would then just direct to the required motor (with or without the dir signal, depending the button). Otherwise a good idea but feels a bit dorky when there is all the possibility to use basic PCB-buttons and a proper program.

Thanks a million for the help in advance!

In principle it is easy to cause a sequence of steps to happen while a button is pressed. Different buttons can have different effects. It would also be easy to read the position of a potentiometer (or 3) to set the speeds.

Do you want to replace the entire GRBL program with your own code, or do you want to be able to use your buttons to control GRBL ? I don't know how to do that. It may be easier to write a PC program that would take button presses (or mouse clicks) from the screen and send data to GRBL.

If you are thinking of replacing the GRBL code with your own code have a look at this simple stepper code - particularly the 2nd version that does not use delay() - and at stepper motor basics and planning and implementing a program

What is the point of motorizing a mill, if you are going to control them manually?

Robin2, I was just reading your Planning and Implementing an Arduino Program-tutorial :smiley:

Yes, I want to replace the GRBL with manual control program. The reason is to have a stand alone CNC for the simplest unique works, like putting a slot in a piece of aluminum. The fully manual solution would require me pushing the spindle by hand or replace the motors with cranks that are sloooow and then I could not swap the GRBL back in if needed.

Yes, I want to replace the GRBL with manual control program.

Have a go at writing your own program. If it does not work post your code and explain what it does and what it should do.

Break the project into small parts. Start with one motor and two push-buttons.


Ok, I got onto it and modified your code, Robin2. Basically just tripled it for 3 motors and 6 buttons. here it is:

//Manual control for a CNC with the same stepper driver pinout(step&dir) as for GRBL

byte directionPinX = 5;
byte stepPinX = 2;

byte directionPinY = 6;
byte stepPinY = 3;

byte directionPinZ = 7;
byte stepPinZ = 4;

byte buttonCWXpin = 8;
byte buttonCCWXpin = 9;

byte buttonCWYpin = 10;
byte buttonCCWYpin = 11;

byte buttonCWZpin = 12;
byte buttonCCWZpin = 13;

boolean buttonCWXpressed = false;
boolean buttonCCWXpressed = false;

boolean buttonCWYpressed = false;
boolean buttonCCWYpressed = false;

boolean buttonCWZpressed = false;
boolean buttonCCWZpressed = false;

unsigned long curMillis;
unsigned long prevStepMillis = 0;
unsigned long millisBetweenSteps = 25; // milliseconds

void setup() {


pinMode(directionPinX, OUTPUT);
pinMode(stepPinX, OUTPUT);

pinMode(buttonCWXpin, INPUT_PULLUP);
pinMode(buttonCCWXpin, INPUT_PULLUP);

pinMode(directionPinY, OUTPUT);
pinMode(stepPinY, OUTPUT);

pinMode(buttonCWYpin, INPUT_PULLUP);
pinMode(buttonCCWYpin, INPUT_PULLUP);

pinMode(directionPinZ, OUTPUT);
pinMode(stepPinZ, OUTPUT);

pinMode(buttonCWZpin, INPUT_PULLUP);
pinMode(buttonCCWZpin, INPUT_PULLUP);


void loop() {

curMillis = millis();


void readButtons() {

buttonCCWXpressed = false;
buttonCWXpressed = false;

if (digitalRead(buttonCWXpin) == LOW) {
buttonCWXpressed = true;
if (digitalRead(buttonCCWXpin) == LOW) {
buttonCCWXpressed = true;

buttonCCWYpressed = false;
buttonCWYpressed = false;

if (digitalRead(buttonCWYpin) == LOW) {
buttonCWYpressed = true;
if (digitalRead(buttonCCWYpin) == LOW) {
buttonCCWYpressed = true;

buttonCCWZpressed = false;
buttonCWZpressed = false;

if (digitalRead(buttonCWZpin) == LOW) {
buttonCWZpressed = true;
if (digitalRead(buttonCCWZpin) == LOW) {
buttonCCWZpressed = true;


void actOnButtons() {
if (buttonCWXpressed == true) {
digitalWrite(directionPinX, LOW);
if (buttonCCWXpressed == true) {
digitalWrite(directionPinX, HIGH);

if (buttonCWYpressed == true) {
digitalWrite(directionPinY, LOW);
if (buttonCCWYpressed == true) {
digitalWrite(directionPinY, HIGH);

if (buttonCWZpressed == true) {
digitalWrite(directionPinZ, LOW);
if (buttonCCWZpressed == true) {
digitalWrite(directionPinZ, HIGH);

void singleStep() {
if (curMillis - prevStepMillis >= millisBetweenSteps) {
prevStepMillis += millisBetweenSteps;
digitalWrite(stepPinX, HIGH);
digitalWrite(stepPinX, LOW);

if (curMillis - prevStepMillis >= millisBetweenSteps) {
prevStepMillis += millisBetweenSteps;
digitalWrite(stepPinY, HIGH);
digitalWrite(stepPinY, LOW);

if (curMillis - prevStepMillis >= millisBetweenSteps) {
prevStepMillis += millisBetweenSteps;
digitalWrite(stepPinZ, HIGH);
digitalWrite(stepPinZ, LOW);

The problem is that when I push the buttons, the motors only jerk a little instead of turning for real. They turn a tiny bit per push, and there is some weird phenomenom also that not every push produces the same action. If I do nothing for a couple of seconds the movement of the motors is bigger when pushing a button again- they turn more. I write motorS because there are 2 per axis.

What I am happy with is that the required motors turn with the right buttons, hooray :slight_smile:

For me this looks like there is some other problem than coding error, maybe just some wiring is wrong. Once I touched the pins of the switches I used for testing, and all of the motors started to turn slowly. I have a very noisy power supply and a weak less noisy one to test the things now, so maybe they are the ones to blame.

What do you guys think?

Please post your program using the code button </>

so it looks like this

It makes it much easier to copy to a text editor.

Every time readButtons() is called it sets the state of (eg) buttonCCWXpressed back to false. That means that you must keep your finger on the button for a series of steps to happen. Maybe that is what you want.

You need a separate singleStep() function for each motor, You also need to use different variables for each motor - eg

void singleStepX() {
    if (curMillis - prevStepMillisX >= millisBetweenStepsX) {
      prevStepMillisX += millisBetweenStepsX;
      digitalWrite(stepPinX, HIGH);
      digitalWrite(stepPinX, LOW);

Alternatively you could write a single function that takes a parameter to identify which motor to move.


What is the point of motorizing a mill, if you are going to control them manually?

You always have jog controls on a CNC machine.

All right... After more careful look all the motors are infact twitching at the same time when I push the buttons. And yes, my idea is to have the eg motors moving when holding down a button. I will put those lines in the code and try again when back at the workshop. What do you mean I need different variables to move different motors? So it's not enough to name the pins and buttons and fuctions separately like I did?

I guess it was too much to hope for that it could be this simple...

In my CNC there is no jog when unplugged from my laptop. Never :smiley:

I guess it was too much to hope for that it could be this simple...

It is not difficult but it does require a bit of thought. The big problem is that computers are really stupid and have to be told every single detail.