Stepper motor, load cell and serial communication

Hello Guys,

I need your help a little bit. I have been reading a lot in the recent weeks about non-blocking codes and other stuff to understand how to build up my code. But, I am still struggling.

I am trying to move a stepper motor and read a load cell simultaneously. The idea is to move the stepper motor continuously until I interrupt the action through the serial connection. I do not need very quick acquisition rate, 10 Hz is more than enough. So, the load cell's circuit (HX711) is not the issue.

My issue is probably is that I am sending data through the terminal "frequently". Here is the loop():

void loop(){
if (Serial.available() > 0) { 

char cin = Serial.read(); //read in the command letter 


  if(cin == 's'){ //if the command is 's' = START
     cin1 = Serial.parseFloat(); //value for speed from the terminal

  while (Serial.read() != 'n') { //while we dont STOP through the serial this loop will run.
   
      if (micros() > time_now1 + cin1) 
      {
        counter++; //counter for the steps
        
        divider = 1000000/(cin1*counter); //the divider will tell the number of samples per second. more samples will slow down the arduino.

        time_now1 = micros();
        
        
        if (divider == 1) //div = 4, 4 samples / second. this is still OK for serial and stepper.
        {
         if(scale.is_ready() == true){
         dataAdc = scale.read();}

         Serial.print(micros());
         Serial.print('\t');
         //Serial.write(scale.get_units());
        Serial.print(dataAdc);
          counter = 0;
         Serial.print('\n');
        
        }

        digitalWrite(stepper, HIGH);
        
      }
    
      if (micros() > time_now2 + cin1) {
        //counter2++;
        time_now2 = micros();

        digitalWrite(stepper, LOW);
      }
}

        
      
      
    }//end of WHILE 'n' loop
  }//end of IF 's' loop

So, as you can see, the Arduino is waiting for a command, a letter (s) through the terminal, and an integer to start the inner while loop. Then, while an other (n) letter is not coming through the terminal, the stepper motor (driven by and EasyDriver) is running based on cin1 which is basically the delay between a digitalWrite. During the run of the motor, I want to get periodic readouts from the HX711 circuit, for example at every 5000th cycle. I made a "divider" where I manually calculated my desired values and I check if I get that value. In this case, I have a "magic number", 1000000, so, when the speed (cin1) is at 200, I will get a readout at every 5000th step of the motor which is 1 value/second. . Based on this number, I can tell the program to enter a small sub-loop to send some data to my PC through the serial.

The problem comes here. When I send something through serial while the motor is running, there will be a small, periodic "knocking" noise coming from the motor. Since my delay (cin1) is in us, and the 4 serial.print takes roughly 1200 us, I suspect that the motor skips or stops for a short time, so the normal rhythm of the rotation gets interrupted for a very short time which will cause the noise.

I think this can be problematic, because if I run the the motor for a long time, I might have some problems with my accuracy due to the accumulated delays caused by the printing.

I tried to print on the second part of the stepping, to make the delays symmetric, but the problem is the order of magnitude time difference between the delays for the motor and the delay caused by the serial communication. I commented out the serial.print() part and ran the motor just as it is, without sending anything through the terminal, and there was no noise. I also tried to send some random characters, but NOT reading the hx711, and the noise was still there. So I suspect that the serial somehow blocks my motor.

I need to run this motor relatively fast, thus the microsecond region for delay is unavoidable. (I am using the stepper motor with a 1:100 gear setup, because I need torque, a lot.)

Is there anything that can be improved with my code (of course yes) and make the serial communication more efficient? I don't need very high speeds, 10 Hz is enough, but at slower speeds of the motor, even 1-2 Hz is OK.

Thank you for your help, and please let me know if you want me to provide more details.

Don't intermingle the code for reading the serial input and the code for driving the motor and reading the scale. Have a look at how there is a separate function for receiving data in Serial Input Basics - simple reliable ways to receive data. Also not that is it is "listening" all the time while the rest of the program goes about its business. It is only when a complete new message has arrived that the rest of the program needs to take note.

And similarly, create separate functions for reading the scale and controlling the motor. See Planning and Implementing a Program

...R
Stepper Motor Basics
Simple Stepper Code

I'm afraid you'll need to read the whole number character-by-character into a buffer with non-blocking code,
not using parseFloat.

Then when you have the whole number available in the buffer, use atof() to convert to a float.

This will avoid any blocking.

Afternatively if you know exactly how many characters are needed for parseFloat to complete, don't call parseFloat() until there are that many available()

MarkT:
I'm afraid you'll need to read the whole number character-by-character into a buffer with non-blocking code,
not using parseFloat.

Then when you have the whole number available in the buffer, use atof() to convert to a float.

This will avoid any blocking.

Afternatively if you know exactly how many characters are needed for parseFloat to complete, don't call parseFloat() until there are that many available()

Thank you for your answer! My problem is not the receiving of the data on the Arduino. I send all the data once from my computer to the Arduino in the beginning, without doing anything, then based on that data, the Arduino will do something. So, other than the interrupting data, which is one simple character, nothing will come to the Arduino during the run of the stepper motors and the readout of the HX711.

Robin2:
Don't intermingle the code for reading the serial input and the code for driving the motor and reading the scale. Have a look at how there is a separate function for receiving data in Serial Input Basics - simple reliable ways to receive data. Also not that is it is "listening" all the time while the rest of the program goes about its business. It is only when a complete new message has arrived that the rest of the program needs to take note.

And similarly, create separate functions for reading the scale and controlling the motor. See Planning and Implementing a Program

...R
Stepper Motor Basics
Simple Stepper Code

Thank you for your help! I went through those tutorials, they are very helpful and they are very well constructed.

I changed several things in my code now, so I separated the things into functions...etc.

My problem still persists, whenever I try to send out something from the Arduino to my PC, there is a small clicking noise coming from the stepper motor, when the Arduino writes to the serial port. So, the reading of the HX711 and the running of the motor goes well simultaneously, but when I try to send back the from the HX711, the stepper motor hangs for that ~1,2 us.

bice90:
I changed several things in my code now, so I separated the things into functions...etc.

My problem still persists,

You need to post the latest version of your program. And please don't overwrite any earlier versions that you have already posted.

...R

Robin2:
You need to post the latest version of your program. And please don't overwrite any earlier versions that you have already posted.

...R

Loop:

void loop()
{
//check the serial input, what to do and how, mode, number of steps and speed, respectively.

checkSerial();

//run the motor
continuousRun(receivedDelay);


//retrieve the value of the scale
scaletoSerial();


//generate the data to send to the PC

}

Serial:

void checkSerial()
{
     if (Serial.available() > 0)
     {
        receivedCommand = Serial.read();
        newData = true;
     }
     if (newData == true) {

      if(receivedCommand == 's')
      {
        receivedDelay = Serial.parseFloat();
        
        Serial.println("s"); 
        Serial.println(receivedDelay); 
       
      }
}
              

    newData = false;
    }
  
}

Motor:

void continuousRun(float receivedDelay)
{
    if(receivedCommand == 's')
      {
  digitalWrite(direct, HIGH); //set direction
 
        while(Serial.read() != 'n')//ramp up the motor to the max speed
        {
        counter++;  
        digitalWrite(stepper, HIGH);
        digitalWrite(stepper, LOW);      
        delayMicroseconds(receivedDelay);
        if(counter > 10000)
        {
          Serial.println(scale.read());
          counter =0;
        }
       
        }  
      
       
      }else{return;}
}

Scale:

void scaletoSerial()
{
  if(receivedCommand == 's')
      {
  if (millis() > convtime + convtimer)
  {     
        convtime = millis();
      if(scale.is_ready() == true){
     dataAdc = scale.read();}
         Serial.print(micros());
         Serial.print('\t');         
         Serial.print(dataAdc);
         Serial.print('\n');  
         
         
  }
  receivedCommand = "";
      }else{return;}
}

Please don't be mad at my coding skills. :slight_smile: This stuff works, except I still get the clicking due to the terminal communication.

Please just post the whole program as one piece. Simpler for you and better for us.

...R

Sorry, here's the code. There are two versions written to run the motor.

#include <HX711.h>

int counter;
float time_now1; //time for stepper delay 
unsigned long convtimer = 500; //read the scale every 500 ms
unsigned long convtime = 0;

//---
char receivedCommand; 
float receivedDelay;
bool newData;//---

float dataAdc;

const int stepper = 9; //pin for stepping the motor
const int direct = 8; // pin for defining the direction of the step

HX711 scale(A0, A1); //Pins for clk and out


//----------------------------------------------------------------------------------
void continuousRun(float receivedDelay)
{
    if(receivedCommand == 's')
      {
  digitalWrite(direct, HIGH); //set direction
 
        while(Serial.read() != 'n')
        {
        counter++;  
        digitalWrite(stepper, HIGH);
        digitalWrite(stepper, LOW);      
        delayMicroseconds(receivedDelay);
        //another way to get readouts from the scale
        if(counter > 10000)
        {
          Serial.println(scale.read());
          counter = 0;
        }
        
        }
        
      
       
      }else{return;}
}
//----------------------------------------------------------------------------------
void continuousRun2(float receivedDelay)
{
  if (micros() > time_now1 + receivedDelay) 
  {   
   digitalWrite(stepper, HIGH);
   digitalWrite(stepper, LOW);  
   time_now1 = micros();
  }
  
}
//----------------------------------------------------------------------------------
void scaletoSerial()
{
  if(receivedCommand == 's')
      {
  if (millis() > convtime + convtimer)
  {     
        convtime = millis();
      if(scale.is_ready() == true){
     dataAdc = scale.read();}
         Serial.print(micros());
         Serial.print('\t');
         Serial.print(dataAdc);
         Serial.print('\n');  
         
         
  }
  receivedCommand = "";
      }else{return;}
}



//----------------------------------------------------------------------------------
void checkSerial()
{
     if (Serial.available() > 0)
     {
        receivedCommand = Serial.read();
        newData = true;
     }
     if (newData == true) {

      if(receivedCommand == 's')
      {
        receivedDelay = Serial.parseFloat();
        
        Serial.println("s"); 
        Serial.println(receivedDelay); 
       
      }
      

         newData = false;
    }
  
}

//----------------------------------------------------------------------------------

void setup()
{
  pinMode(stepper, OUTPUT);  //define stepper pin as an output
  pinMode(direct, OUTPUT);  //define direction pin as an output
  Serial.begin(115200);

  digitalWrite(direct, LOW); //set the direction 
 
 Serial.println(scale.read());
 Serial.println("ready");
  


}

void loop()
{
//check the serial input, what to do and how, mode, number of steps and speed, respectively.

checkSerial();

//run the motor
continuousRun(receivedDelay);
//run the motor #2
continuousRun2(receivedDelay);


//retrieve the value of the scale
scaletoSerial();

//generate the data to send to the PC

}//end main LOOP

You now have a function to get the Serial data so why are you reading Serial data in the function continuousRun()?

The program should be organised so that loop() repeats very frequently - considerably faster than the step rate and each time continuousRun() is called it should make at most one step. Most of the time it probably won't need to make any step. And don't use delayMicroseconds() as that blocks everything until it completes,

If you don't use delayMicroseconds() the printing can take place between steps.

Using a higher baud rate may also help. My normal rate for communication with my PC is 500000 baud.

Don't read the scale in the same statement that does the printing. It may well be that the slow-down is due to the time taken to read the scale. There have been other Threads which lead me to believe the library code for reading the scale is blocking code. It should be possible to instruct the scale to start a reading and call back later to get the value.

...R

Thank you very much, you are right!

I put an additional condition into the serial. So if I want to stop the motor, I send a character which will flip a bool variable. And this bool variable is checked in the contonuousRun() function. When it is not true anymore, the function will exit and the code will wait for a new command.

At the same time, I got rid of the delayMicroseconds() and I only use the micros()/millis() functions, as in the continuousRun2() function.

I used higher baud rate, as you suggested, but still it is lagging. I have noticed that printing to the serial can take quite a long time, longer than the period time of the stepper motor. Especially at lower micros(), when I use something like 100-200 us between two steps, the clicking is very noticeable. If I move up to the millis() range, then the stepper motor's delay becomes longer than the printing (and readout) and the clicking disappears or becomes barely noticeable.

I tested this with only using printing simple text. Like several lines of "Serial.println("TestText");". I measured the time, and for example, four lines of this text can take around 630 us. This is 3x larger than my stepping time and I also hear the clicking noise. So, not only the reading of the HX711, but the writing to the serial can be an issue as well. I will look at this in more details.

I was also wondering, what if, I connect a Nano to the Uno that I am using now and handle the stepper motor with the Nano. So the Nano's responsibility would only be the stepping and the Uno would do the reading and communication.

bice90:
I was also wondering, what if, I connect a Nano to the Uno that I am using now and handle the stepper motor with the Nano. So the Nano's responsibility would only be the stepping and the Uno would do the reading and communication.

That's an option, but then you have the added complexity of communication between the Arduinos.

Try breaking up the printing so you don't try to do it all in the interval for a single step.

...R