Serial.println affecting code for stepper motor

Hi everyone. Long time lurker, first time poster. Please be gentle.

I make cider and have been using Arduino to automate. I have built a labelling machine that will apply both a main label and a neck label to bottles, using three stepper motors. One motor spins the bottle, the second turns the main label roll and the third turns the neck label roll.

The hardware works. I'm using a CNC shield to control the motors. The software also works - mostly. I based the code off of Robin2's examples, using millis() to control several motors at once, each running at different speeds.

The bug I can't understand is the effect that one specific Serial.println() is having. If I leave that line in, the code works. if I delete that one line (because I want the motors to run faster by eliminating the time taken up by the printing to the serial monitor), then the code stops working. Instead of running for the alloted time defined by the nested if functions, the motors each move one step and stop.

I am baffled. I could just leave that Serial.println() in - all that happens is that the machine runs a bit slower than I would like - but I cannot get my head around what is happening here and would like to tap the hive mind to see if I can get an explanation. Here is the code. The offending Serial.println is identified with the comment *** THIS IS THE LINE ***

byte directionPinBottleTurner = 5;
byte directionPinMainLabel = 6;
byte directionPinNeckLabel = 7;

byte stepPinBottleTurner = 2;
byte stepPinMainLabel = 3;
byte stepPinNeckLabel = 4;

byte buttonpin = A5;

boolean buttonpressed = false;

int stepperCounter = 0;
int buttonState = 0;
int lastButtonState = 0;
int buttonPushCounter = 1;

unsigned long curMillis;
unsigned long prevStepMillisBottleTurner = 0;
unsigned long prevStepMillisMainLabel = 0;
unsigned long prevStepMillisNeckLabel = 0;

unsigned long millisBetweenStepsBottleTurner = 1; // milliseconds
unsigned long millisBetweenStepsMainLabel = 1; // milliseconds
unsigned long millisBetweenStepsNeckLabel = 4; // milliseconds

void setup() { 

     Serial.begin(9600);

     pinMode(directionPinBottleTurner, OUTPUT);
     pinMode(directionPinMainLabel, OUTPUT);
     pinMode(directionPinNeckLabel, OUTPUT);
     
     pinMode(stepPinBottleTurner, OUTPUT);
     pinMode(stepPinMainLabel, OUTPUT);
     pinMode(stepPinNeckLabel, OUTPUT);
         
     pinMode(buttonpin, INPUT_PULLUP);
       
}

void loop() { 

   curMillis = millis();
    readButtons();
    actOnButtons();
    
}

void readButtons() 
   {
   
    buttonpressed = false;
    buttonState = digitalRead(buttonpin);
    
     if (buttonState != lastButtonState) 
     {
         // if the state has changed, increment the counter
         if (buttonState == LOW) 
           {
           buttonPushCounter++;
           } 
      }
  
  // save the current state as the last state, 
  //for next time through the loop
  lastButtonState = buttonState;
        Serial.println(buttonPushCounter);  // ***  THIS IS THE LINE  ***
    if (buttonPushCounter == 2) 
         {
         buttonpressed = true;
         }    
    }
    

void actOnButtons() {
    if (buttonpressed == true) 
  {    
   if (stepperCounter = 1199)
   {
    buttonPushCounter = 0;
   }
 
   digitalWrite(directionPinBottleTurner, LOW);
   digitalWrite(directionPinMainLabel, HIGH);
   digitalWrite(directionPinNeckLabel, HIGH);
    
     for (stepperCounter = 0; stepperCounter < 1200; stepperCounter++)
     { 
     curMillis = millis();
        Serial.println(stepperCounter);     
       if (stepperCounter == 0)
       {
        delay(500);
       }
       
        if (stepperCounter < 1200) // Bottle turner subroutine
        { 
           if (curMillis - prevStepMillisBottleTurner > millisBetweenStepsBottleTurner) 
                   {
                 
                   prevStepMillisBottleTurner = curMillis;
                   digitalWrite(stepPinBottleTurner, HIGH);
                   digitalWrite(stepPinBottleTurner, LOW);
                   }
        }
        if (stepperCounter > 263) // Main label subroutine
        {
           if (stepperCounter < 1050) // Will want the Main labeller to stop 1/4 rotation after the neck labeller
              {                
              if (curMillis - prevStepMillisMainLabel > millisBetweenStepsMainLabel) 
                    {
                    prevStepMillisMainLabel = curMillis;
                    digitalWrite(stepPinMainLabel, HIGH);
                    digitalWrite(stepPinMainLabel, LOW);
                    }
             }
        }
       if (stepperCounter < 650) //Neck label subroutine
          { 
           
           if (curMillis - prevStepMillisNeckLabel > millisBetweenStepsNeckLabel) 
                    {
                    prevStepMillisNeckLabel = curMillis;
                    digitalWrite(stepPinNeckLabel, HIGH);
                    digitalWrite(stepPinNeckLabel, LOW);
                    }
           }
       }
       }
       }
     
 

Oops

One thing you can do is only print the count when you change it.

The other is to upgrade the baud rate from that Fred Flintstone speed you're using :wink:

Ehhh. >= 1199 ?

Serial.begin(9600);...... What about Serial.begin (115200(; ???

Sorry, but your whole code doesn't work as you expect it to do. Your mixing of delay, for loop and millis doesn't really work. It only works because your serial prints create delays in your code because of buffer overflow. Without that, your for loop runs from step 1 to step1200 without any delay, and without giving the millis-if a chance to get true.

You have to rework it completely.

I would suggest turning your code into more of a state machine. Google "Finite State Machine" for all the details and examples.

Then, each time through loop, you check your buttons and do a single step and repeat. This may also inspire you: several things at the same time

Hi! 1199 was intentional, since the if loop goes to ”less than1200”. I wanted to be sure the counter reset itself.

Thanks, MicroBahner. I suspected something like this was happening but I don’t have the vocabulary to express it. Thanks. I have no background in programming.

I’ll check that out. Thanks!

Ha ha, I just kept using whatever they provided in the ”learn to Arduino” coding. I’ll try changing this, thanks!

Take another look at the code I highlighted.

The easiest way would be to stay with your blocking code design. Get rid of all millis() code, because this would need a complete redesign of the code structure. Insert a 'delay(stepTime)' into your for loop to define the stepspeed. ( Edit: In this simple variant step speed is the same for all 3 steppers )
Something like this:

byte directionPinBottleTurner = 5;
byte directionPinMainLabel = 6;
byte directionPinNeckLabel = 7;

byte stepPinBottleTurner = 2;
byte stepPinMainLabel = 3;
byte stepPinNeckLabel = 4;

byte buttonpin = A5;

boolean buttonpressed = false;

int stepperCounter = 0;
int buttonState = 0;
int lastButtonState = 0;
int buttonPushCounter = 1;

int stepTime = 2;

void setup() {

    Serial.begin(9600);

    pinMode(directionPinBottleTurner, OUTPUT);
    pinMode(directionPinMainLabel, OUTPUT);
    pinMode(directionPinNeckLabel, OUTPUT);

    pinMode(stepPinBottleTurner, OUTPUT);
    pinMode(stepPinMainLabel, OUTPUT);
    pinMode(stepPinNeckLabel, OUTPUT);

    pinMode(buttonpin, INPUT_PULLUP);

}

void loop() {

    readButtons();
    actOnButtons();

}

void readButtons()
{

    buttonpressed = false;
    buttonState = digitalRead(buttonpin);

    if (buttonState != lastButtonState)
    {
        // if the state has changed, increment the counter
        if (buttonState == LOW)
        {
            buttonPushCounter++;
        }
    }

    // save the current state as the last state,
    //for next time through the loop
    lastButtonState = buttonState;
    Serial.println(buttonPushCounter);  // ***  THIS IS THE LINE  ***
    if (buttonPushCounter == 2)
    {
        buttonpressed = true;
    }
}


void actOnButtons() {
    if (buttonpressed == true)
    {
        buttonPushCounter = 0;
        buttonpressed = false;

        digitalWrite(directionPinBottleTurner, LOW);
        digitalWrite(directionPinMainLabel, HIGH);
        digitalWrite(directionPinNeckLabel, HIGH);

        for (stepperCounter = 0; stepperCounter < 1200; stepperCounter++)
        {
            //Serial.println(stepperCounter);
            delay(stepTime);
            if (stepperCounter == 0)
            {
                delay(500);
            }

            //if (stepperCounter < 1200) // Bottle turner subroutine <<-- this is always true!!
            //{
                    digitalWrite(stepPinBottleTurner, HIGH);
                    //delay(1);
                    digitalWrite(stepPinBottleTurner, LOW);
            //}
            if (stepperCounter > 263) // Main label subroutine
            {
                if (stepperCounter < 1050) // Will want the Main labeller to stop 1/4 rotation after the neck labeller
                {
                        digitalWrite(stepPinMainLabel, HIGH);
                    //delay(1);
                        digitalWrite(stepPinMainLabel, LOW);
                }
            }
            if (stepperCounter < 650) //Neck label subroutine
            {
                    digitalWrite(stepPinNeckLabel, HIGH);
                    //delay(1);
                    digitalWrite(stepPinNeckLabel, LOW);
            }
        }
    }
}

When all the motors run at the same speed, I can see how this will work. But when I try different delays for the different motors, they still run at the same speed because the delay() s are cumulative. Am I missing something?

Yes, that's the drawback of this simple approach. In the end you have only one stepdelay ( the first delay in the for loop ) that is relevant for all steppers. If you add a delay for one special motor this will influence the loop time of the for loop and as such the steptime for all other motors too.

If you need different steptimes for your motors you need a complete redesign and must step over to a non blocking design of your sketch.
You could e.g. use my MobaTools library in this case, which simplifies driving the steppers. Steps are created in the background and in the sketch the motor is only started with the desired number of steps and speed. And you can start several motors at different times with different stepcount and speed without bothering about the Puls generating.

You can also just use the AccelStepper.h library. It has examples of running multiple stepper motors

Yes I know, AccelStepper is recommended here all the time. But the sketch design is much more critical with AccelStepper - especially when running several motors in parallel - because the steps are created in loop(). Any delay, even a short one, will interfere with stepping.
But of course @ciderman2021 can decide what is more easy for him.

Thanks bli64. I have been looking at AccelStepper, those functionalities look like they could work.

I am using a CNC shield to control the steppers. Are there any issues to combining AccelStepper with a CNC shield?

They will shurely work - with the correct sketch. I know AccelStepper quite well. And developed MobaTools especially to make things easier.

No.

Yeah, I was afraid of that. OK, so be it.

Is MobaTools compatible with a CNC shield? Can you point me to any example code using MobaTools that may be relevant for controlling multiple steppers at different speeds, starting and stopping independently?

Thanks, MicroBahner.

There is no difference to AccelStepper in this respect. Both simply create a direction output and a steppulse output. And of course there also drawbacks of MobaTools compared to Accelstepper. The biggest one is obviously that MobaTools is hardware dependent and does not run on all Arduino boards. This is because it uses timer interrupts to generate the step pulses.

The library contains examples for driving steppers, unfortunately mostly with one stepper. But because you don't have to create the pulses by yourself, using multiple steppers is as easy as using one stepper. Simply create a second stepper object and start the motor as appropriate. That will not interfere with the first one - no matter if it is running or not when you start the second one. You can even ask the step position of the first to start the second ( I think that is what you will need).

I'll see about creating a starting base for your requirements tomorrow. Maybe I get a new example for the library :wink: .