Blocking the code with the Serial.print function in arduino

Hi, I tried to program with arduino a stepper motor with Nextion display.
The program that I made work fine but the problem is when I tried to send the position of the stepper to the Nextion display each 1s to update it using Serial.print the code blocks maybe 10ms or something like this, and this make the stepper blocks a little when it is running.
To control the motor I am using the accelStepper library.
So my question is if there are any posibility to solve this issue and make the delay in Serial.print 0ms or this is imposible as the function needs this time to execute.

Thanks in advance.

Welcome to the forum

Please post your full sketch, using code tags when you do. Posting the sketch will enable us to see things such as the Serial baud rate that you are using, which should be as fast as possible

My crystal ball shows @ingyou is using software serial on an Uno to communicate to the display, making the problem described inevitable.

If correct, @ingyou needs to remember that most Arduino have only one CPU core and cannot truly perform more than one task at the same instant in time, such as driving a stepper motor and sending serial data to another device. What some Arduino do have is timer and UART circuits built into the chip which can perform their tasks independently of the CPU. If you can use those circuits, having only one CPU is no longer such a problem.

For example, with some stepper motor drivers, only 2 pins are required for DIRECTION and STEP. With this type, a timer circuit in the arduino can be used to send regular pulses to the STEP pin, freeing the CPU to communicate with the display.

Some types of Arduino have extra hardware UART ports (hardware serial) which could communicate to the display, freeing the CPU to drive the stepper motor.

Hi @ingyou

welcome to the arduino-forum:

The solution to this problem is to use a different stepper-library.
This library is called MobaTools and can be installed with the Arduino-IDE-library-manager.

The MobaTools use a different approach to create the step-pulses:
The step-pulses are created by a timer-interrupt. This means the step-pulses are created in the backround just-in-time for a smooth running stepper-motor.

If you have installed the library a PDF-documentation in english and in german is also stored in the MobaTools-subfolder. Ask here in the forum. Post your code but

If you have any problems in applying / modifying one of the example codes

important !

post your code as a code-section like described here

best regards Stefan

But if my crystal ball is reading correctly and @ingyou is using software serial to communicate to the display, software serial library may disable interrupts so that it can send data correctly?

Thank you for you reponse.
Here I attach my sketch. I am using Arduino Mega.

#include "Nextion.h"
#include <AccelStepper.h> //accelstepper library

AccelStepper motor1(1, 46, 49); // ..., pulses, direction

#define LED_monitoring 13

bool moveAxis1, dirAxis1, enableM1;
int speedMaxM1 = 3000;
int speedM1 = 1500;
int acelerationM1 = 2000;
char angleAxis1[5];

long timeReleased = 0, timeReleased2 = 0 ;

//creating buttons to control display
NexButton b0 = NexButton(1 , 2 , "b0"); //(page, id, objname)
NexButton b1 = NexButton(1 , 3 , "b1");
NexCheckbox c0 = NexCheckbox(1 , 17 , "c0");
NexText grM1 = NexText(1, 42 , "grM1");

NexTouch *components[] = {
  &b0, &b1, &c0, &grM1,
  NULL
};

void setup() {
  Serial.begin(9600);
  nexInit();  //9600
  dbSerial.println("Debug OK");
  motor1.setMaxSpeed(speedMaxM1);
  motor1.setAcceleration(acelerationM1);
  motor1.setEnablePin(48);
  
  //functions of buttons
  b0.attachPush(axis1ForwardPush, &b0); //axis1+
  b0.attachPop(axisButtonReleased, &b0);   //axis1+
  b1.attachPush(axis1ReversePush, &b1);   //axis1-
  b1.attachPop(axisButtonReleased, &b1);   //axis1-
  c0.attachPush(ENABLEm1, &c0);

  pinMode(LED_monitoring, OUTPUT);

  timeReleased = millis();  timeReleased2 = millis();
}

void loop() {

  nexLoop(components);
  
  if((millis()-timeReleased) >= 150) //this is to monitoring that the code is not blocked
    {
     digitalWrite(LED_monitoring,!digitalRead(LED_monitoring));
     timeReleased = millis(); 
    }
if(enableM1 == true)
{
  if(moveAxis1 == true)
   {
    if(dirAxis1 == true)
      {
        motor1.setSpeed(-speedM1);
        motor1.disableOutputs();
        motor1.runSpeed();
      }
      if(dirAxis1 == false)
      {
        motor1.setSpeed(speedM1);
        motor1.disableOutputs();
        motor1.runSpeed();
      }
   }
}
 if((millis()-timeReleased2) >= 1000) 
    {
     float calcAngleAxis1 = ((motor1.currentPosition()*360.00)/1600)/8; //gearbox is x8
     grM1.setText(dtostrf(calcAngleAxis1, 5, 2, angleAxis1));
     
     //This is what the library nextion does to send text to the display.
     
     //Serial2.print("grM1.txt=\""); Serial2.print(angleAxis1).toFloat()); Serial2.print("\"");
     //Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff);
  
     timeReleased2 = millis(); 
    }
}

//functions of buttons
void axis1ForwardPush(void *ptr)  
{
  dirAxis1 = LOW;
  moveAxis1 = HIGH;
}
void axis1ReversePush(void *ptr)  
{
  dirAxis1 = HIGH;
  moveAxis1 = HIGH;
}
void axisButtonReleased(void *ptr)  
{
  moveAxis1 = LOW;
}
void ENABLEm1(void *ptr)  
{  
  uint32_t dual_state;
  c0.getValue(&dual_state);
  if(dual_state) {
     motor1.disableOutputs();
     enableM1 = true;
  }else{
     motor1.enableOutputs();
     enableM1 = false;
  } 
}

Thank you for you reponse.

What do you mean by crystal ball?

Okey, I never knew this, Arduino Mega is one of those that can perform tasks independently of the CPU?
I am using it, and using two UART ports.

I would appreciate a example sketch to test it.

Start the Arduino-IDE
click Tools - manage libraries

Type MobaTools in the searchbox


then click install
there is a subfolder examples which has a subfolder _stepper


If you have any question about a demo-code ask the questions here in the forum
once you have learned how the library works it offers a lot of comfort for doing things
much easier as coding them from scratch by hand

best regards Stefan

It's a round about way of saying that there were some important facts you could have included in your original post that would have removed the need for forum members to take guesses.

Thank you.

Yes, you are right. The sound of the motor is better than using accelStepper library.

Finally it WORKED fine and the stepper doesn't blocks anymore. But now there is a little problem with other thing.
If I push or released the button at the same time when it is sending the data to display, it not detect the "change" because arduino is "busy" sending data. Here is what I did.

 if((millis()-timeReleased2) >= 200) 
    {
     float calcAngleAxis1 = ((myStepper.currentPosition()*360.00)/1600)/8; //gearbox is x8
     grM1.setText(dtostrf(calcAngleAxis1, 5, 2, angleAxis1));
     
     //This is what the library nextion does to send text to the display.
     
     //Serial2.print("grM1.txt=\""); Serial2.print(angleAxis1).toFloat()); Serial2.print("\"");
     //Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff);
  
     timeReleased2 = millis(); 
    }

And I have another question. In this library is Multisteppers function like accelStepper?

Best regards,

not enough information

Are you now using mobatools?

without the whole sketch I can't analyse what you have coded.

The mobaTools also offer functions for detecting buttons.
I haven't used the button-functions much but as the mobaTools were coded by a modelrailway enthusiast I guess that the mobatools button-functions work well together with the stepper-functions

You can drive multiple steppers but not in a completely synchronised way.

Sorry, Here is the code.
My buttons are in the Nextion touch screen and NOT physical ones.

#define MAX8BUTTONS // spart Speicher, da nur 4 Taster benötigt werden (saves RAM)
#include <MobaTools.h>
#include "Nextion.h"
// Pindefinitions - change to your needs
const byte dirPin       = 49;
const byte stepPin      = 46;
const byte enaPin       = 48;

#define LED_monitoring 13

bool moveAxis1, dirAxis1, enableM1;
char angleAxis1[5];
long timeReleased = 0, timeReleased2 = 0 ;

const int STEPS_REVOLUTION = 1600;
//Stepper einrichten ( 800 Schritte / Umdrehung - 1/4 Microstep )
MoToStepper myStepper( STEPS_REVOLUTION, STEPDIR );  // 800 Steps/ Umdrehung

NexButton b0 = NexButton(1 , 2 , "b0"); //(page, id, objname)
NexButton b1 = NexButton(1 , 3 , "b1");
NexCheckbox c0 = NexCheckbox(1 , 17 , "c0");
NexText grM1 = NexText(1, 42 , "grM1");

NexTouch *components[] = {
  &b0, &b1, &c0, &grM1,
  NULL
};

void setup()
{
  Serial.begin(9600);
  nexInit();
  dbSerial.println("Debug OK");
  
  myStepper.attach( stepPin, dirPin );
  myStepper.attachEnable( enaPin, 10, LOW );        // Enable Pin aktivieren ( LOW=aktiv )
  myStepper.setSpeed( 1500 );
  myStepper.setRampLen( 100 );                       // Rampenlänge 100 Steps bei 20U/min

  b0.attachPush(axis1ForwardPush, &b0); //axis1+
  b0.attachPop(axisButtonReleased, &b0);   //axis1+
  b1.attachPush(axis1ReversePush, &b1);   //axis1-
  b1.attachPop(axisButtonReleased, &b1);   //axis1-
  c0.attachPush(ENABLEm1, &c0);

  pinMode(LED_monitoring, OUTPUT);

  timeReleased = millis();  timeReleased2 = millis();
}

void loop() {

  nexLoop(components);
  
  if((millis()-timeReleased) >= 150) //this is to monitoring that the code is not blocked
    {
     digitalWrite(LED_monitoring,!digitalRead(LED_monitoring));
     timeReleased = millis(); 
    }
    
if(enableM1 == true)
{
  if(moveAxis1 == true)
   {
    if(dirAxis1 == true)
      {
        myStepper.rotate( 1 );          // Stepper dreht vorwärts
      }
      if(dirAxis1 == false)
      {
        myStepper.rotate( -1 );         // Stepper dreht rückwärts
      }
   }
}

 if((millis()-timeReleased2) >= 200) 
    {
     float calcAngleAxis1 = ((myStepper.currentPosition()*360.00)/1600)/8; //gearbox is x8
     grM1.setText(dtostrf(calcAngleAxis1, 5, 2, angleAxis1));
     
     //This is what the library nextion does to send text to the display.
     
     //Serial2.print("grM1.txt=\""); Serial2.print(angleAxis1).toFloat()); Serial2.print("\"");
     //Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff);
  
     timeReleased2 = millis(); 
    }
}

//functions of buttons
void axis1ForwardPush(void *ptr)  
{
  dirAxis1 = LOW;
  moveAxis1 = HIGH;
}
void axis1ReversePush(void *ptr)  
{
  dirAxis1 = HIGH;
  moveAxis1 = HIGH;
}
void axisButtonReleased(void *ptr)  
{
  moveAxis1 = LOW;
  myStepper.rotate(0);             // Stepper stoppt
}
void ENABLEm1(void *ptr)  
{  
  uint32_t dual_state;
  c0.getValue(&dual_state);
  if(dual_state) {
     enableM1 = true;
  }else{
     enableM1 = false;
  } 
}

I dont know if the function refers to "physical" buttons or I can use also buttons of touch screen.

No the nextion-display is connected with a serial interface.
The only thing a nextion-button has in common with a physical button is the name.
Everything else is different between nextion "buttons" and real physical buttons.

As a general hint: you will finish your project 5 hours faster if you always invest 5 minutes more to write detailed postings instead of short messages.

This is not WhatsApp, nor instagram nor snapchat for fun.
This is serious software-developping
Switch over and get used to detailed writing

I haven't used nextion displays much.
When I used I did not use the nextion-library

I read in the data sended from the nextion display straight forward directly from the serial interface and this worked very well in parallel with using stepper-motors.
best regards Stefan

I am sorry if I have not done it correctly, I know this is serious software-developping. It is my first post and I have tried to be as clear as possible.
There are other doubts that have been arising so I have been publishing/asking them.

I will try to get the data from the nextion display directly from the serial port.
Thank you very much for your help.

Best regards,

Not enough information to be sure understanding right

Your stepper-motor is running and if you send the angleAxis
with this line of code

grM1.setText(dtostrf(calcAngleAxis1, 5, 2, angleAxis1));

and if you press a display-button the display-button is not recognised?

Yes exactly, is not recognised.
Like I said: If I push or released the button at the same time when it is sending the data.

If I press the button while the code is executing this line of code (I attached it below), arduino does not recognise the state change of display-button in this precise moment.

Does not recognise in this precise moment mean after a short time the button-change IS detected or not detected at all?

Is NOT detected at all until you press it again.
For more details, when you press the button, it should never match with this line of code:

I guess this is the reason

void sendCommand(const char* cmd)
{
    while (nexSerial.available())
    {
        nexSerial.read();
    }

the sendcommand-function

void sendCommand(const char* cmd)
{
    while (nexSerial.available())
    {
        nexSerial.read(); // <<= take byte out of the receive buffer and throw away
    }
    
    nexSerial.print(cmd);
    nexSerial.write(0xFF);
    nexSerial.write(0xFF);
    nexSerial.write(0xFF);
}

when the sendCommand-function is called and right in that moment
if there are some bytes in the receive-buffer
then before sending a command
the function it reads from the serial receivebuffer and just throws away the received bytes.

don't know what the developper of this code thought what he is doing here.

So I guess if you switch over to direct serial-send and serial-receive it should work

You could try changing the baudrate from 9600 baud to 115200 baud
with the command bauds=115200
where bauds saves the baudrate to be the new standardbaudrate

For a more effective way of communicating you can use shorter commands
like "< B 1 >"

The meaning of the characters is
"<" start-character indicating a command starts
"B1" identifier to know which Nextion-element is pressed
">"-end-character indicating the end of a command

This requires a serial-receive-function that uses start and end characters

In the Nextion-editor you define it this way

Send Component ID DIS-abled

the serial receiving with start / endmarker works this way

// fetch data from receivebuffer
void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  // if bytes are inside the receivebuffer .available() > 0
  // .available() delivers the number of bytes in the buffer
  while (Serial2.available() > 0 && newData == false) {
    rc = Serial2.read();

    if (recvInProgress == true) { // if startmamrker is found set flag
      if (rc != endMarker) {      // if byte in variable "rc" is NOT the Endmarker 
        receivedChars[ndx] = rc;  // append byte in variable "rc to end of array
        ndx++;                    // increment indexcounter
        if (ndx >= numChars) {    // if byteseqwuence is too long
          ndx = numChars - 1;     // reduce indexcounter throw away the byte
        }
      } // if (rc != endMarker) {
      else {
        receivedChars[ndx] = '\0'; // terminate the string with trailing zero
        recvInProgress = false;    // set flag message received finished
        ndx = 0;
        newData = true;            // set flag command received completely 
      }
    } // if (recvInProgress == true) {

    else if (rc == startMarker) {  // if Byte in variable "rc" is the startmarker
      recvInProgress = true;       // set boolean flag 
    }
  } // while (Serial2.available() > 0 && newData == false) {
}

in your main loop you check if variable newData is true
if true
command is received completely and can be evaluated
and after copying the command from receive-array immidiately set newData to false to make receive-function ready to receive the next command

best regards Stefan