Go Down

Topic: (Solved)Steppermotor and serial.print and read combined (Read 20293 times) previous topic - next topic

backbone

Apr 24, 2014, 11:09 am Last Edit: Aug 03, 2014, 08:07 am by backbone Reason: 1
Curious what I do wrong.

Button currrently controls up and down movement of the stepper motor.
Code works but as soon as I uncomment a line related to serial.print it also stops to work correctly.

I need the combination as I use the stepper to compress a spring and measure the force and the current position which need to be send to a PC application.

Am I asking to much from this combo?

Paco

Code: [Select]

#include <HX711.h>

// HX711.DOUT - pin #A1
// HX711.PD_SCK - pin #A0

HX711 scale(A1, A0); //load cell libary


int Analog2 = 16;           // Use analog port on shield for push button to control movement and Direction
int Analog3 = 17;           // Use analog port on shield for push button to control movement and Direction
int Distance = 0;           // Record the number of steps the motor has done
int delayTimeUp = 300;
int delayTimeDown = 100;
int Position = 1000;
boolean MotorStopDown = 0;
boolean MotorStopUp = 0;
boolean flagStartInit = 0;
boolean flagRecording = 0;
boolean flagReturn = 0;
const int DirPin = 8;
const int StepPin =9;
const int EnablePin = 10;
int IncomingData = 0;
int outByte = 0;
int cmdTemp = 0;
int MaxTravel = 0;
boolean LeftTurn = 0;
boolean RightTurn = 0;

void setup()
{
 pinMode(DirPin, OUTPUT);
 pinMode(StepPin, OUTPUT);
 pinMode(EnablePin, OUTPUT);
 pinMode(Analog2,INPUT);   // set the A2 pin to input
 pinMode(Analog3,INPUT);   // set the A3 pin to input
 digitalWrite(DirPin, LOW);
 digitalWrite(StepPin, LOW);
 digitalWrite(EnablePin, HIGH);
 Serial.begin(9600);
}

void loop()
{  
 
 LeftTurn = digitalRead(Analog2);
 RightTurn = digitalRead(Analog3);  

 if (LeftTurn == 1)  // upwards
 {
   digitalWrite(EnablePin, LOW);
   digitalWrite(DirPin, LOW);
   digitalWrite(StepPin, HIGH);
   delayMicroseconds(delayTimeUp);          
   digitalWrite(StepPin, LOW);
   delayMicroseconds(delayTimeUp);
   digitalWrite(EnablePin, HIGH);
   digitalWrite(DirPin, LOW);
   Distance = Distance + 1;   // record this Step as part of a rotation where 900 is a quarter rotation
   if (Distance == 900);
   {
     digitalWrite(EnablePin, HIGH);
     digitalWrite(DirPin, LOW);
     Distance = 0;   // reset the Motorstep counter to 0
     Position = Position - 1;
   }
 }
 else if (RightTurn == 1)  // downwards
 {
   digitalWrite(EnablePin, LOW);
   digitalWrite(DirPin, HIGH);
   digitalWrite(StepPin, HIGH);
   delayMicroseconds(delayTimeDown);          
   digitalWrite(StepPin, LOW);
   delayMicroseconds(delayTimeDown);
   digitalWrite(EnablePin, HIGH);
   digitalWrite(DirPin, LOW);
   Distance = Distance + 1;   // record this Step as part of a rotation where 900 is a quarter rotation
   if (Distance == 900);
   {
     digitalWrite(EnablePin, HIGH);
     digitalWrite(DirPin, LOW);
     Distance = 0;   // reset the Motorstep counter to 0
      Position = Position + 1;
   }
 }
 else
 {
   digitalWrite(EnablePin, HIGH);
   digitalWrite(DirPin, LOW);
 }

 //Serial.print ("A,");
 //Serial.println (Position); // keeps score of the steps done to calulate the movement in 0.5 mm + or -
 //Serial.print ("B,");
 //Serial.println(scale.read()/100); //raw loadcell data devided by 100
}


Never to old to learn and I learn every day

knut_ny

Take a look at the stepper libraries  (also see accelstepper)
This will require a rewrite, but may solve your problem
Ny

MarkT


Curious what I do wrong.

Button currrently controls up and down movement of the stepper motor.
Code works but as soon as I uncomment a line related to serial.print it also stops to work correctly.

You need to explain what working correctly and working incorrectly means for
your code, and to tell us which line you commented/uncommented.

Quote

Am I asking to much from this combo?

Perhaps explaining exactly what you want would be a start.

And why are you setting the enable pin of the stepper motor low - it would normally
just be tied to 5V and left that way.

Is there a reason you forgo the AccelStepper library btw?
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

backbone

#3
Apr 24, 2014, 05:50 pm Last Edit: Apr 24, 2014, 06:14 pm by backbone Reason: 1
OK appolgies for the info.

If I uncomment the line with serial print the stepper motor runs in a very very low speed.

The reason I did not used the accelstepper lib was that I used a piece of sample code that was not refering to it.
So now I for test used this piece of code and added the serial.print in this demo just to see if it keeps the stepper motor speed and a serial value is written. It keeps the speed but serial.print doesnt show anything happening in the serial monitor.

Enable is used on the easy driver too de-energize the coils if the stepper motor is in rest position. There is no need to keep them alive as this only heats up the driver IC.

Code: [Select]
#include <AccelStepper.h>

// Define a stepper and the pins it will use
AccelStepper stepper(1, 9, 8);

int pos = 900;
int Position = 1000;

void setup()
{  
 stepper.setMaxSpeed(3000);
 stepper.setAcceleration(1000);
}

void loop()
{
 if (stepper.distanceToGo() == 0)
 {
   delay(500);
   pos = -pos;
   stepper.moveTo(pos);
 }
 stepper.run();
 
 Serial.print ("A,");
}


What I need in the end...........
I need to measure a force by a spring on a weigth sensor and the compression in 0.5 mm steps.
Sensor and HX711 IC works fine and with serial.print I send the raw value to the PC.
All other weight calculation and calibration is done in the PC.
I compress the spring step by step and need to measure the force that belong to it.
The coil spring is for example 70 mm long. So I need to compress it to 35 mm. No need to compress it more.
I start a measurement sequence.
Pulse is send from PC to arduino. The rest of the sequence need to be done in the Arduino UNO.
Reset the force value to zero in PC is done too.
The pulse gives the stepper motor the action to start rotate downwards.
As soon as the PC sens a force larger (>) then 1 gram we reset the force and reset the counter of the stepper motor and start to write the data to a file in the PC. So each certain compression value gets it force value. So each 0.5 mm step has a force value.
As soon as the 35 mm is reached the motor rewinds upwards and decompress the spring untill it is 2mm above the point where the measurement of the force started.
We repeat the same sequence and after 5 in row we stop and show the dat in a graph and tabellist.
During the compression the data is shown in a graph too.

I had it all working fine but that was with a H bridge stepper using the L298 IC.
But for easier use I like to use the easy stepper driver but ran in the problem of the serial.print problem,

I hope my intentions with this project are clear if not shoot the questions to me. :-)
Never to old to learn and I learn every day

Robin2

You have forgotten to put Serial.begin() in setup()

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

backbone

oohps forgot the Serial.begin(9600); to add to the setup.
Now we have serial data out but again the stepper motor slows down.

Paco
Never to old to learn and I learn every day

backbone

OK,
I did some more homework.
But I still am having problems to finalize the project.
1] I used the accelstep lib instead of stepper.h
2] I added an encoder disk to the output shaft of the steppermotor as the accuracy was not good enough and I use the interrupts to measure the steps on pin 2 and 3

But it looks like the arduino cant cope with sending the force data to the PC in the voidloop and simultanously controlling the stepper motor and sending the encoder data. I changed the serialprint to a place where it is updated if 1 mm displacement has been measured. But this way I am missing the continously measurement of the force. it is the HX711 slowing down when it serialprint action is placed in the VOID LOOP root.

I had to raise the baudrate to 57600 too.

In this part of the project I cant use a DC motor as the stepper motor is part of the mechanism I have to use.

Would it be better to run the stepper board on its own Arduino so it only drives the Stepper as DC motor?

Code: [Select]
#include <HX711.h>
#include <AccelStepper.h>

// HX711.DOUT - pin #A1
// HX711.PD_SCK - pin #A0

HX711 scale(A1, A0); // 24 bit load cell amplifier

int LeftTurnUp = 0;
int RightTurnDown = 0;
int incomingByte = 0;   // for incoming serial data
int enablePin10 = 10;  // switch off coils when not in use to reduce heat build up
int dirPin = 8;
int stepPin = 9;
int StepCounter =0;

AccelStepper stepper(4,4,5,6,7); // set up the stepper as 4 wire bipolair on pin 4,5,6,7
//AccelStepper stepper(1,8,9); // set up the stepper as 4 wire bipolair on pin 8,9 for EASY DRIVER

/////
enum PinAssignments
{
  encoderPinA = 2,   // rigth
  encoderPinB = 3,   // left
};

volatile unsigned long encoderPos = 100000;  // a starting counterposition for the encoder
long encoderPosOld = 100000;
unsigned int lastReportedPos = 1;   // change management
static boolean rotating=false;      // debounce management

// interrupt service routine vars
boolean A_set = false;             
boolean B_set = false;
//////


void setup()
{
  Serial.begin(57600);
  StepCounter = 500;
  stepper.setMaxSpeed(1000);
  stepper.setSpeed(1000);
  stepper.setAcceleration(800); // do not remove!

  pinMode(enablePin10, INPUT);

  // encoder disk part
  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  // turn on pullup resistors
  digitalWrite(encoderPinA, HIGH);
  digitalWrite(encoderPinB, HIGH);
  // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, CHANGE);
  // encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderB, CHANGE);

}

void loop()
{

  //Serial.print ("A,");
  //Serial.println (encoderPos,DEC); // keeps score of the steps done to calulate the movement in mm + or -
  //Serial.print ("B,");
  //Serial.println(scale.read()/100); //raw loadcell data devided by 100

  if (Serial.available() > 0)
  {
    incomingByte = Serial.read();
    {
      if (incomingByte == '1')
      {
        digitalWrite(enablePin10, HIGH);
        LeftTurnUp = 1;
        RightTurnDown = 0;
      }

      if (incomingByte == '2')
      {
        digitalWrite(enablePin10, HIGH);
        RightTurnDown = 1;
        LeftTurnUp = 0;
      }

      if (incomingByte == '3')
      {
        LeftTurnUp = 0;
        RightTurnDown = 0;
        stepper.moveTo(0);
        digitalWrite(enablePin10, LOW);
      }
    }
  }

  if (LeftTurnUp == 1)  //left turn
  {
    stepper.moveTo(1000000); //move many steps - more then mechanical needed
  }

  if (RightTurnDown == 1)  //right turn
  {
    stepper.moveTo(-1000000); //move many steps - more then mechanical needed
  }


  //if (encoderPos >= (encoderPosOld + 1081)) //if the value is 1081 higher then last known send data to PC
  if ((encoderPos >= (encoderPosOld + 1081)) || (encoderPos <= (encoderPosOld - 1081))) //if the value is 1081 higher or 1081 lower then last known send data to PC
  {
    encoderPosOld = encoderPos;
    Serial.print ("A,");
    Serial.println (encoderPos,DEC); // keeps score of the steps done to calulate the movement in mm + or -
    Serial.print ("B,");
    Serial.println(scale.read()/100); //raw loadcell data devided by 100
  }

  stepper.run();


// Interrupt on A changing state
void doEncoderA(){
  // debounce
  if ( rotating );  //removed the 1 us delay // wait a little until the bouncing is done

  // Test transition, did things really change?
  if( digitalRead(encoderPinA) != A_set ) {  // debounce once more
    A_set = !A_set;

    // adjust counter + if A leads B
    if ( A_set && !B_set )
      encoderPos += 1;

    rotating = false;  // no more debouncing until loop() hits again
  }
}

// Interrupt on B changing state, same as A above
void doEncoderB(){
  if ( rotating ); //removed the 1 us delay
  if( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if( B_set && !A_set )
      encoderPos -= 1;

    rotating = false;
  }
}

Never to old to learn and I learn every day

Robin2

#7
May 24, 2014, 11:38 pm Last Edit: May 24, 2014, 11:39 pm by Robin2 Reason: 1
If the stepper motor is missing steps then you need to sort that out - not add an encoder as a band-aid.

You have a lot of Serial.print() commads - they take a lot of time.

You should calculate how often it is necessary for the motor to step (how many millisecs or microsecs between steps) so you can then figure out if loop() repeats sufficiently often.

You could have stepper.run two or three times within loop().

I presume data is only received occasionally - like once per second or less.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

backbone

Hi Robin,

Thanks for the response.
I know the encoder is a band-aid but if I use the stepper and send a pulse to rotate 180 degree and then 180 degree reverse the encoder shows me we are not returning at the same spot. I have no idea how to use the stepper without encoder to get accurate position, no idea what is causing it or how to solve it.

I need the force to be measured and send to the PC constantly so that is why the serialprint was originally in the void loop root.
The reason for this constant measurement is that I send the motor down in constant motion and as soon as I starts to measure a force it has to start recording. the value of the force and the mm steps. During recording I can reduce the measurement of the force with each 1 mm step and put the serial print in:
Code: [Select]
//if (encoderPos >= (encoderPosOld + 1081)) //if the value is 1081 higher then last known send data to PC
  if ((encoderPos >= (encoderPosOld + 1081)) || (encoderPos <= (encoderPosOld - 1081))) //if the value is 1081 higher or 1081 lower then last known send data to PC
  {
    encoderPosOld = encoderPos;
    Serial.print ("A,");
    Serial.println (encoderPos,DEC); // keeps score of the steps done to calulate the movement in mm + or -
    Serial.print ("B,");
    Serial.println(scale.read()/100); //raw loadcell data devided by 100
  }


If there any other options to try or sort out let me know.
Eager to get this going.
I will make a picture of the current set up of the compression device so it more clear of its purpose.

Thanks, Paco
Never to old to learn and I learn every day

backbone

#9
May 25, 2014, 09:34 am Last Edit: May 25, 2014, 09:38 am by backbone Reason: 1
Attached the picture of the unit.
At the bottom the loadcell at the top the stepper motor with the attached encoder.
The encoder is 300LPI, the steppermotor is a NEMA 17 version.
I control the up and down motion by PC.

I hope this give a visualisation of the project,
BTW the stepper is not used to send to a certain location like a CNC machine.
The unit gives information in return (force) which a CNC is not doing.
When only using the stepper you always have to presume the stepper is at the correct position at the time you need to measure the force, there is no feedback from the stepper it is.
Correct me if I am wrong.
Other issue is that the measurement needs to be done in a fluent motion not in start/stop steps.

Any help is appriciated.
If I have to break down the project in parts and test and enhance it for eaxmple the stepper accuracy, no problem.

Paco
Never to old to learn and I learn every day

Robin2

I can only think of 2 reasons why the stepper is missing steps - either the load against it is too great (need a bigger motor?) or the pulses are being sent too quickly and the motor can't keep up.

Are you using the motor in micro-stepping mode? If so it will have much less torque than in full step mode.

I have the impression that the core of your project involves a loop of code like this

Code: [Select]
void loop() {
   readLoadCell();
   readEncoder();
   analyzeData();
   moveMotorOneStep();
}


I don't immediately see why all that can't be done.

How often do you need to step the motor (milli/microsecs between steps)?
How many steps does the motor need to make (in one direction) during a test?

Are you sure the motor is working at its full permitted coil current?
Have you posted the specifications for the motor, driver and power supply?

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

backbone

#11
May 25, 2014, 12:16 pm Last Edit: May 25, 2014, 11:26 pm by backbone Reason: 1
Robin,

Motor is rated for 1,5 amps per coil.
Powersupply is rated 4 amps at 18 volts.

I do full steps (as far as I am aware of) I currently use the L298 board (fullbridge) and I cant set microstep.
I am await new Easy driver boaards as I blew up two of them. The L298 board is more forgiving.
Or this is done in the accelstep.h file but I did not alter it.

I did some more testing with below code and found out that the the accelstepper.h lib the stepper is accurate enough.
Before it was missing 20 to 30 steps now it is accurate on the step or +/- 2.
It might have to do that the accelstep lib use accellaration where the stepper.h does not.
Even without compressing the spring in previous setups the tolerance was 20 to 30 steps and very irragular.
So motor is strong enough.

Each half rotation of the motor is 100 steps is 180 degree is 1 mm displacment of the upper plate to compress the spring.
I need to measure upto 70 mm displacement so we are talking about 70 x 100 motor steps in total.

For below code I have to find a way to switch off the coils after each step because I will smoke this board too..
Switching off currently it only happens when I send "3" to the serial monitor.
After sending "1" or "2" the coils should be automaticly de-energized.
Have tried several options but did not worked.

Code: [Select]

// debug code for steppermotor using serial monitor to control 100 steps in either direction
#include <AccelStepper.h>

int LeftTurnUp = 0;
int RightTurnDown = 0;
int incomingByte = 0;   // for incoming serial data
int enablePin10 = 10;  // switch off coils when not in use to reduce heat build up
int dirPin = 8;
int stepPin = 9;
int StepCounter =0;

AccelStepper stepper(4,4,5,6,7); // set up the stepper as 4 wire bipolair on pin 4,5,6,7
//AccelStepper stepper(1,8,9); // set up the stepper as 4 wire bipolair on pin 8,9 for EASY DRIVER

/////
enum PinAssignments
{
 encoderPinA = 2,   // rigth
 encoderPinB = 3,   // left
};

volatile unsigned long encoderPos = 100000;  // a starting counterposition for the encoder
long encoderPosOld = 100000;
unsigned int lastReportedPos = 1;   // change management
static boolean rotating=false;      // debounce management

// interrupt service routine vars
boolean A_set = false;              
boolean B_set = false;
//////


void setup()
{
 Serial.begin(57600);
 StepCounter = 500;
 stepper.setMaxSpeed(1000);
 stepper.setSpeed(1000);
 stepper.setAcceleration(800); // do not remove!

 pinMode(enablePin10, INPUT);

 // encoder disk part
 pinMode(encoderPinA, INPUT);
 pinMode(encoderPinB, INPUT);
 // turn on pullup resistors
 digitalWrite(encoderPinA, HIGH);
 digitalWrite(encoderPinB, HIGH);
 // encoder pin on interrupt 0 (pin 2)
 attachInterrupt(0, doEncoderA, CHANGE);
 // encoder pin on interrupt 1 (pin 3)
 attachInterrupt(1, doEncoderB, CHANGE);

}

void loop()
{


 if (Serial.available() > 0)
 {
   incomingByte = Serial.read();
   {
     if (incomingByte == '1')
     {
       LeftTurnUp = 1;
     }

     if (incomingByte == '2')
     {
       RightTurnDown = 1;
     }

     if (incomingByte == '3')
     {
       LeftTurnUp = 0;
       RightTurnDown = 0;
       stepper.moveTo(0);
       digitalWrite(enablePin10, LOW);
     }
   }
 }

 if ((LeftTurnUp == 0) && (RightTurnDown == 0))
 {
   digitalWrite(enablePin10, LOW);
 }

 if ((LeftTurnUp ==1) || (RightTurnDown == 1))
 {
   digitalWrite(enablePin10, HIGH);
 }

 if (LeftTurnUp == 1)  //left turn
 {
   stepper.moveTo(100); //move 100 steps  = 180 degree = 1mm
 }

 if (RightTurnDown == 1)  //right turn
 {
   stepper.moveTo(-100); //move 100 steps  = 180 degree = 1mm
 }



 if ((encoderPos >= (encoderPosOld + 1081)) || (encoderPos <= (encoderPosOld - 1081)))
 {
   encoderPosOld = encoderPos;
   Serial.print ("A,");
   Serial.println (encoderPos,DEC); // keeps score of the steps done to calulate the movement in mm + or -
 }

 stepper.run();
}  

// Interrupt on A changing state
void doEncoderA(){
 // debounce
 if ( rotating );  //removed the 1 us delay // wait a little until the bouncing is done

 // Test transition, did things really change?
 if( digitalRead(encoderPinA) != A_set ) {  // debounce once more
   A_set = !A_set;

   // adjust counter + if A leads B
   if ( A_set && !B_set )
     encoderPos += 1;

   rotating = false;  // no more debouncing until loop() hits again
 }
}

// Interrupt on B changing state, same as A above
void doEncoderB(){
 if ( rotating ); //removed the 1 us delay
 if( digitalRead(encoderPinB) != B_set ) {
   B_set = !B_set;
   //  adjust counter - 1 if B leads A
   if( B_set && !A_set )
     encoderPos -= 1;

   rotating = false;
 }
}



void loop() {
  readLoadCell(); >correct
  readEncoder(); >correct
  analyzeData(); > done in PC
  moveMotorOneStep(); >correct
}
Way of working:
1] PC button start click reset all values including force value to 0 and starts motor
2] Motor starts to turn downwards unconditional.
3] If we measured force > 1 gram
reset stepcounter
start recording
4] If stepcounter is > then the preset value (for example 35 mm)
Stop recording
Reverse motor
As we know how far we have to compress the spring we might use the steppermotor steps to calculate the steps we have dne and reverse the motor

5] If steps are 2 mm past starting point
Stop motor
Reset all and prepare for next automatic measurement.
Never to old to learn and I learn every day

backbone

Did some more test.

As soon as I put any a serial.print in the "void loop" root it slows down the stepper motor.

I tried to put the "stepper.run();" into a "blinkwithout delay" so it would be called in a lesser tempo but that slows down the stepper too. When I set 1 millisecond it still is to slow even.
So it is the combination of the serial.print with the stepper control but why???

Paco

Never to old to learn and I learn every day

Robin2

I haven't time to study all of your 2 posts now. Will try later.

An Easydriver isn't able to provide the current for a 1.4 amp motor. You need a BigEasydriver or equivalent.

What voltage is your power supply. With a BigEasydriver it could usefully be 20v or more. But you couldn't use that with an L298. Also the L298 has significant voltage losses so you may not be getting the voltage you expect across the motor coils.

You really need a proper stepper motor driver board and an high voltage power supply to ensure you get the full motor torque.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

backbone

#14
May 25, 2014, 10:06 pm Last Edit: May 25, 2014, 11:28 pm by backbone Reason: 1
Let the accuracy or motor amp be the last hurdle to sort out.

I recode and thought I found a bypass/reroute around the serial.print for detecting for a change in the initial start start stage.

I thought I was clever and use
"ForceValueTemp = (scale.read()/100);"
in the VOID LOOP and use it inside the arduino to send a reset signal to the PC.
Same problem as with the serial.print line added.stepper runs step by step in low rate.
If I comment the line high speed stepper again.

The HX711 amplifier is used with a lib. do not know if there is something inside that blocks.

Paco

BTW I have a video of the unit moving. (without serial prints in the void loop) :-)
https://www.youtube.com/watch?v=JCNRUc1FTfo&feature=youtu.be

Never to old to learn and I learn every day

Go Up