Stepper Motor and LDR counter: rate of reading LDR value

Need help with software or code, this is your place.
Hi, for my first ever project I'm trying to construct an LDR/laser counter mounted on a linear stepper motor (basically counting things that get in the way between the LDR/laser pair while scanning a given sheet). I've wired everything together quite successfully, I get linear motion back and forth via the EasyDriver, and my laser is nice and bright, and the LDR gives a nice readout. The things work as intended when ran separately, but now I'm trying to connect all into one, and I run into code problems (no JayZ puns, please).

To cut a long story short, the LDR readout rate can be controlled in its own sketch, but when directly cut-and-pasted with the motor code, it fails (see below). I have a hunch it's due to the multiple delay functions, as they stop the entire loop, not just the part, I realise this.

Could anyone suggest a workaround to this problem? Essentially I want to be able to control LDR printout rate independently to any other code in the sketch.

Cheers

Code: Select all
//How to control LDR printout rate?

#define LDRpin A0
int dirpin = 2;
int steppin = 3;
int LDRValue = 0;

void setup()
{
Serial.begin(9600);
pinMode(dirpin, OUTPUT);
pinMode(steppin, OUTPUT);

}
void loop()
{
//LDR bit

LDRValue = analogRead(LDRpin); // read the value from the LDR
  Serial.println(LDRValue);      // print the value to the serial port
  delayMicroseconds(1);                    // wait a little

//motor bit
 
  int i;
  digitalWrite(dirpin, LOW);     // Set the direction. also delay time for the return motion
  delay(1);

  for (i = 0; i<2500; i++)       // Iterate for 2500microsteps.
  {
    digitalWrite(steppin, LOW);  // This LOW to HIGH change is what creates the
    digitalWrite(steppin, HIGH); // "Rising Edge" so the easydriver knows to when to step.
    delayMicroseconds(200);      // This delay time is close to top speed for this
  }                              // particular motor. Any faster the motor stalls.

  digitalWrite(dirpin, HIGH);    // Change direction.
  delay(1);


  for (i = 0; i<2500; i++)       // Iterate for 4000 microsteps
  {
    digitalWrite(steppin, LOW);  // This LOW to HIGH change is what creates the
    digitalWrite(steppin, HIGH); // "Rising Edge" so the easydriver knows to when to step.
    delayMicroseconds(200);      // This delay time is close to top speed for this
  }                              // particular motor. Any faster the motor stalls.
 
 
}

undo
 
Posts: 4
Joined: Thu Nov 24, 2016 3:59 pm

Sorry for the late reply. I was on vacation.

This is a pretty simple problem with a complicated fix.

So... The code used to rotate the stepper motor is "blocking". The functions use delay and delayMicroseconds in them. These functions block any other code from being run during that process. Also, because it is in a big loop "for (i = 0; i<2500; i++)" it will complete all of that before it ever reads the LDR values. So it will read the LDR values, then move the motor completely then read the LDR values again and so on.

What you need is code that can mode the motor and read the LDR at the same time.

You could either put your LDR read code in the for loop like this:
Code: Select all
  for (i = 0; i<2500; i++)       // Iterate for 2500microsteps.
  {
    digitalWrite(steppin, LOW);  // This LOW to HIGH change is what creates the
    digitalWrite(steppin, HIGH); // "Rising Edge" so the easydriver knows to when to step.
    delayMicroseconds(200);      // This delay time is close to top speed for this
   
    Serial.println(LDRValue);      // print the value to the serial port
  }                              // particular motor. Any faster the motor stalls.



Or you need to move your code to use a library that will make this all work like magic for you. For that, check out this article (you can use your exact same board and setup with this code): http://bildr.org/2012/11/big-easy-driver-arduino/

Just a note about your current code. This line delayMicroseconds(1); // wait a little basically does not do anything. delayMicroseconds(1) will tell the system to pause for 1/1,000,000 of a second. Basically the arduino will just ignore it. If you do delay(100) it will pause for 1/100 of a second.

undo wrote:Hi, for my first ever project I'm trying to construct an LDR/laser counter mounted on a linear stepper motor (basically counting things that get in the way between the LDR/laser pair while scanning a given sheet). I've wired everything together quite successfully, I get linear motion back and forth via the EasyDriver, and my laser is nice and bright, and the LDR gives a nice readout. The things work as intended when ran separately, but now I'm trying to connect all into one, and I run into code problems (no JayZ puns, please).

To cut a long story short, the LDR readout rate can be controlled in its own sketch, but when directly cut-and-pasted with the motor code, it fails (see below). I have a hunch it's due to the multiple delay functions, as they stop the entire loop, not just the part, I realise this.

Could anyone suggest a workaround to this problem? Essentially I want to be able to control LDR printout rate independently to any other code in the sketch.

Cheers

Code: Select all
//How to control LDR printout rate?

#define LDRpin A0
int dirpin = 2;
int steppin = 3;
int LDRValue = 0;

void setup()
{
Serial.begin(9600);
pinMode(dirpin, OUTPUT);
pinMode(steppin, OUTPUT);

}
void loop()
{
//LDR bit

LDRValue = analogRead(LDRpin); // read the value from the LDR
  Serial.println(LDRValue);      // print the value to the serial port
  delayMicroseconds(1);                    // wait a little

//motor bit
 
  int i;
  digitalWrite(dirpin, LOW);     // Set the direction. also delay time for the return motion
  delay(1);

  for (i = 0; i<2500; i++)       // Iterate for 2500microsteps.
  {
    digitalWrite(steppin, LOW);  // This LOW to HIGH change is what creates the
    digitalWrite(steppin, HIGH); // "Rising Edge" so the easydriver knows to when to step.
    delayMicroseconds(200);      // This delay time is close to top speed for this
  }                              // particular motor. Any faster the motor stalls.

  digitalWrite(dirpin, HIGH);    // Change direction.
  delay(1);


  for (i = 0; i<2500; i++)       // Iterate for 4000 microsteps
  {
    digitalWrite(steppin, LOW);  // This LOW to HIGH change is what creates the
    digitalWrite(steppin, HIGH); // "Rising Edge" so the easydriver knows to when to step.
    delayMicroseconds(200);      // This delay time is close to top speed for this
  }                              // particular motor. Any faster the motor stalls.
 
 
}

ameyer
Founder
 
Posts: 3323
Joined: Thu Jan 21, 2010 11:59 pm
Location: The Bay Area

thanks, I was just reading about libraries! I'll give it a go soon and report findings
undo
 
Posts: 4
Joined: Thu Nov 24, 2016 3:59 pm

awkay, I've tried it with the library AccelStepper.h, it truly works wonders, very smooth control over the stepper. Now I've combined the LDR/laser with it in a code as follows:
Code: Select all
#include "AccelStepper.h"
int motorSpeed = 9000;
int motorAccel = 15000;
int motorDirPin = 2; //digital pin 2
int motorStepPin = 3; //digital pin 3
AccelStepper stepper(1, motorStepPin, motorDirPin);

//ldr vars
int sensA;                     
int thresh;
int ctr=0;

void setup(){
 Serial.begin(9600);
 stepper.setMaxSpeed(motorSpeed);
 stepper.setSpeed(motorSpeed);
 stepper.setAcceleration(motorAccel);
 stepper.moveTo(1500);
}

void loop(){

//ldr bit

sensA = analogRead(A0);                     
thresh = 110;                                 
if(sensA<thresh)
{
ctr=ctr+1;                                     
}
else{ctr=ctr;
}


Serial.print("sensA is :  ");                       
Serial.print(sensA   );
Serial.print("     counter :  ");
Serial.print(ctr);
Serial.print("  thresh val : ");
Serial.println(thresh);

//stepper bit
 if (stepper.distanceToGo() == 0){
  stepper.moveTo(-stepper.currentPosition());
 }
 stepper.run();
}


When ran, this gives a good rate of LDR readout, however the motor now twitches and does not really move. I've tried playing with the speed and accel, but it just changes the rate/strength of twitching with no real movement. What's up with that? Any advice again will be greatly appreciated.
undo
 
Posts: 4
Joined: Thu Nov 24, 2016 3:59 pm

The reason for it is the serial print. The serial prints take a long time (long time for computers at least ). So in between each step it stops and does all that printing and that messes it up.

The faster/ less often it prints the smoother the motor will move. Not printing at all would be the best but that isn't often possible when debugging.

There are a few things you can do to speed up the print.
-Change the baud rate to 115200 in the sketch and in the serial window.
-Print fewer characters. See if you can remove any of the data you are printing
-print only when needed
-print less often

I put together a sketch that will do most of that so you can see how I would do it. I am using the concept of timers, and that can be confusing at first. But read it over a few times and see if it clicks. Basically I'm just tracking (timing) how long the sketch is running, and every 1000ms I print out the value.

I'm also flipping a switch when the LDR goes above the threshold and then I turn it off when it goes below it. So when the laser scans the sheet, it will only trigger once per notch even if it runs really slow. We are then printing the values when that triggers even if it hasn't been 1000ms yet.

Again, read it over about 5 times and see if it makes sense - I changed all the variables to be very legible. This is actually how I often code professionally so it is easier to debug.

You will need to change the baud rate to 115200 on the serial window.
Code: Select all
#include "AccelStepper.h"
int motorSpeed = 9000;
int motorAccel = 15000;
int motorDirPin = 2; //digital pin 2
int motorStepPin = 3; //digital pin 3
AccelStepper stepper(1, motorStepPin, motorDirPin);

//ldr vars
int sensorAValue;
int sensorAThreshold;
int sensorACount = 0;

long lastStoredMillis = 0;
bool isTheLDRAlreadyBelowTheThreshold;

void setup() {
  Serial.begin(115200); //using a higher baud rate will help it print faster
 
  stepper.setMaxSpeed(motorSpeed);
  stepper.setSpeed(motorSpeed);
  stepper.setAcceleration(motorAccel);
  stepper.moveTo(1500);

  lastStoredMillis = millis(); //millis it the ammount of milliseconds the sketch has been running
}

void loop() {

  //ldr bit
  sensorAThreshold = 110; //unless this changes you should move this out of the loop
  sensorAValue = analogRead(A0);

  //only do this if we are not below the threshold already
  if (sensorAValue <= sensorAThreshold && isTheLDRAlreadyBelowTheThreshold == false) {

    isTheLDRAlreadyBelowTheThreshold = true; //so we don't do this again until we go above again
    sensorACount ++; // this is the same as  sensorACount = sensorACount + 1;
    printData(); //always print the data when the LDR triggers
   
  } else if (sensorAValue > sensorAThreshold) { // are we above the threshold now?
    isTheLDRAlreadyBelowTheThreshold = false;
  }

  //print every 1000ms (AKA 1 second)
  //checks if millis() minus the last stored value of millis is greater than 1000
  if (millis() - lastStoredMillis > 1000) {
    printData();
    lastStoredMillis = millis(); //reset this value
  }

  //stepper bit
  if (stepper.distanceToGo() == 0) {
    stepper.moveTo(-stepper.currentPosition());
  }
  stepper.run();
}

void printData() {
  Serial.print("sensA is :  ");
  Serial.print(sensorAValue   );
  Serial.print("     counter :  ");
  Serial.print(sensorACount);
  Serial.print("  thresh val : ");
  Serial.println(sensorAThreshold);
}





undo wrote:awkay, I've tried it with the library AccelStepper.h, it truly works wonders, very smooth control over the stepper. Now I've combined the LDR/laser with it in a code as follows:
Code: Select all
#include "AccelStepper.h"
int motorSpeed = 9000;
int motorAccel = 15000;
int motorDirPin = 2; //digital pin 2
int motorStepPin = 3; //digital pin 3
AccelStepper stepper(1, motorStepPin, motorDirPin);

//ldr vars
int sensA;                     
int thresh;
int ctr=0;

void setup(){
 Serial.begin(9600);
 stepper.setMaxSpeed(motorSpeed);
 stepper.setSpeed(motorSpeed);
 stepper.setAcceleration(motorAccel);
 stepper.moveTo(1500);
}

void loop(){

//ldr bit

sensA = analogRead(A0);                     
thresh = 110;                                 
if(sensA<thresh)
{
ctr=ctr+1;                                     
}
else{ctr=ctr;
}


Serial.print("sensA is :  ");                       
Serial.print(sensA   );
Serial.print("     counter :  ");
Serial.print(ctr);
Serial.print("  thresh val : ");
Serial.println(thresh);

//stepper bit
 if (stepper.distanceToGo() == 0){
  stepper.moveTo(-stepper.currentPosition());
 }
 stepper.run();
}


When ran, this gives a good rate of LDR readout, however the motor now twitches and does not really move. I've tried playing with the speed and accel, but it just changes the rate/strength of twitching with no real movement. What's up with that? Any advice again will be greatly appreciated.
ameyer
Founder
 
Posts: 3323
Joined: Thu Jan 21, 2010 11:59 pm
Location: The Bay Area

Thank you so much, this is now working and giving me counts at an enourmous rate while still maintaining the motor speed.
The motor, however, still slows down when I deliberately obstruct the laser - the serial.print goes haywire, but this won't be a problem in the actual application, I don't think, so there's no need for a workaround at the moment.

Additionally, I have noticed that as the motor acts, it dims my laser a bit, likely due to current drop. Do you think I should power my Arduino externally, then?

One more thing, could you help me optimise the code for a display for my read count on a 4 digit 7segment display? I'm using the TM1637 and got a library for it, the TM1637Display.h (see the below). It works, but I'm sure that has quite a few things that are unneccessary.

Ideally, I'd love the whole setup to include a button and operate as follows:
1. Press the button
2. Laser and ldr take the background value and record it as threshold
3. motor takes the Laser/ldr head to a set area
4. proceed to scan over an area of 20 mm
5. display the value of counted things on the 7seg
6. Motor returns head to start position

The physical setup of the device then becomes important and I'm happy to optimise that myself ad infinitum as long as the code does the abovementioned tasks.

Code: Select all
#include <TM1637Display.h>

#include "AccelStepper.h"
int motorSpeed = 9000; //maximum steps per second (about 3rps / at 16 microsteps)
int motorAccel = 15000; //steps/second/second to accelerate

int motorDirPin = 2; //digital pin 2
int motorStepPin = 3; //digital pin 3
//set up the accelStepper intance
//the "1" tells it we are using a driver
AccelStepper stepper(1, motorStepPin, motorDirPin);

//ldr vars
int sensorAValue;                      //    Declaring VARIABLES
int sensorAThreshold;
int sensorACount=0;
long lastStoredMillis = 0;
bool isTheLDRAlreadyBelowTheThreshold;

// 7seg vars
const int CLK = 9; //Set the CLK pin connection to the display
const int DIO = 8; //Set the DIO pin connection to the display
TM1637Display display(CLK, DIO);
void setup(){
//LDR setup
Serial.begin(115200);
 lastStoredMillis = millis(); //millis it the ammount of milliseconds the sketch has been running
//Motor setup
 stepper.setMaxSpeed(motorSpeed);
 stepper.setSpeed(motorSpeed);
 stepper.setAcceleration(motorAccel);
 stepper.moveTo(1500); //move 32000 steps (should be 10 rev)


//7seg setup

  display.setBrightness(0x0a);  //set the diplay to maximum brightness

}
//serial setup

void printData() {
  Serial.print("sensA is :  ");
  Serial.print(sensorAValue   );
  Serial.print("     counter :  ");
  Serial.print(sensorACount);
  Serial.print("  thresh val : ");
  Serial.println(sensorAThreshold);
}

void loop(){
//ldr bit
sensorAValue = analogRead(A0);    // READ SENSOR A AND B
sensorAThreshold = 80;    // !!!! CHANGE THE VALUE OF THRESHOLD ACCORDING TO THE AMBIENT LIGHT
if(sensorAValue <= sensorAThreshold && isTheLDRAlreadyBelowTheThreshold == false)

{
  sensorACount ++;
  printData();
 display.showNumberDec(sensorACount);
}
else if (sensorAValue > sensorAThreshold)
{
  isTheLDRAlreadyBelowTheThreshold = false;
}
if (millis() - lastStoredMillis > 1000) {
    printData();
    lastStoredMillis = millis(); //reset this value
  }


 //stepper bit
 
 //if stepper is at desired location
 if (stepper.distanceToGo() == 0){
  //go the other way the same amount of steps
  //so if current position is 400 steps out, go position -400
  stepper.moveTo(-stepper.currentPosition());
 }
 //these must be called as often as possible to ensure smooth operation
 //any delay will cause jerky motion
 stepper.run();
}
undo
 
Posts: 4
Joined: Thu Nov 24, 2016 3:59 pm

Try this on for size. You need to connect a button switch from digital 4 to ground. When you press it, it will threshold, then start moving and take readings. When it gets to the end it will return home without counting or printing anything.

If you press the button again after it finishes it will reset and do it all again.

I broke the code out into a few functions to make it easier to read. Try reading it over a few times to see if it makes sense

Because I don't have any of the hardware to try this with there is a 90% chance this won't work as it should. Let me know what needs fixing

Code: Select all
#include <TM1637Display.h>
#include "AccelStepper.h"

int motorSpeed = 9000; //maximum steps per second (about 3rps / at 16 microsteps)
int motorAccel = 15000; //steps/second/second to accelerate

int motorDirPin = 2; //digital pin 2
int motorStepPin = 3; //digital pin 3
int sensorAPin = A0;
int buttonPin = 4; //button placed on digital pin 4

long endPositionForMotor = 1500; //change this as needed
long homePositionForMotor = 0; //probably do not need to change

//set up the accelStepper intance
//the "1" tells it we are using a driver
AccelStepper stepper(1, motorStepPin, motorDirPin);

//ldr vars
int sensorAValue;
int sensorAThreshold = 80;
int sensorACount = 0;
long lastStoredMillis = 0;
bool isTheLDRAlreadyBelowTheThreshold;
bool started;
bool finished;

// 7seg vars
const int CLK = 9; //Set the CLK pin connection to the display
const int DIO = 8; //Set the DIO pin connection to the display
TM1637Display display(CLK, DIO);


void setup() {
  Serial.begin(115200);
  lastStoredMillis = millis(); //millis it the ammount of milliseconds the sketch has been running

  pinMode(buttonPin, INPUT);
  digitalWrite(buttonPin, HIGH);
 
  //Motor setup
  stepper.setMaxSpeed(motorSpeed);
  stepper.setSpeed(motorSpeed);
  stepper.setAcceleration(motorAccel);
 

  //7seg setup
  display.setBrightness(0x0a);  //set the diplay to maximum brightness

}
//serial setup

void loop() {
  sensorAValue = analogRead(sensorAPin); // Read LDR
  stepper.run();

  if(digitalRead(buttonPin) == LOW && started == false){
    started = true;
    sensorACount = 0;
    display.showNumberDec(0);
    setThreshold(); //read LDR to threshold
    stepper.moveTo(endPositionForMotor);
  }


  //only do this on the way out, but not the way back home
  if(started == true && finished == false){
    checkLDR();
    checkIfPrintNeeded();
  }


  if (stepper.distanceToGo() == 0) { //we are at the end
   
    if(finished == false){
      //we need to return home
      stepper.moveTo(homePositionForMotor);
      finished = true; 
    }else{
      //we are back home, allow for a reset
      started = false;
    }
  }
}

void setThreshold(){
  sensorAThreshold = analogRead(sensorAPin);
}

void checkIfPrintNeeded(){
  if (millis() - lastStoredMillis > 1000) {
    printData();
    lastStoredMillis = millis(); //reset this value
  }
}

void checkLDR(){
  if (sensorAValue <= sensorAThreshold && isTheLDRAlreadyBelowTheThreshold == false){
    sensorACount ++;
    printData();
    display.showNumberDec(sensorACount);
  }else if (sensorAValue > sensorAThreshold){
    isTheLDRAlreadyBelowTheThreshold = false;
  }
 
}
void printData() {
  Serial.print("sensA is :  ");
  Serial.print(sensorAValue   );
  Serial.print("     counter :  ");
  Serial.print(sensorACount);
  Serial.print("  thresh val : ");
  Serial.println(sensorAThreshold);
}








undo wrote:Thank you so much, this is now working and giving me counts at an enourmous rate while still maintaining the motor speed.
The motor, however, still slows down when I deliberately obstruct the laser - the serial.print goes haywire, but this won't be a problem in the actual application, I don't think, so there's no need for a workaround at the moment.

Additionally, I have noticed that as the motor acts, it dims my laser a bit, likely due to current drop. Do you think I should power my Arduino externally, then?

One more thing, could you help me optimise the code for a display for my read count on a 4 digit 7segment display? I'm using the TM1637 and got a library for it, the TM1637Display.h (see the below). It works, but I'm sure that has quite a few things that are unneccessary.

Ideally, I'd love the whole setup to include a button and operate as follows:
1. Press the button
2. Laser and ldr take the background value and record it as threshold
3. motor takes the Laser/ldr head to a set area
4. proceed to scan over an area of 20 mm
5. display the value of counted things on the 7seg
6. Motor returns head to start position

The physical setup of the device then becomes important and I'm happy to optimise that myself ad infinitum as long as the code does the abovementioned tasks.

Code: Select all
#include <TM1637Display.h>

#include "AccelStepper.h"
int motorSpeed = 9000; //maximum steps per second (about 3rps / at 16 microsteps)
int motorAccel = 15000; //steps/second/second to accelerate

int motorDirPin = 2; //digital pin 2
int motorStepPin = 3; //digital pin 3
//set up the accelStepper intance
//the "1" tells it we are using a driver
AccelStepper stepper(1, motorStepPin, motorDirPin);

//ldr vars
int sensorAValue;                      //    Declaring VARIABLES
int sensorAThreshold;
int sensorACount=0;
long lastStoredMillis = 0;
bool isTheLDRAlreadyBelowTheThreshold;

// 7seg vars
const int CLK = 9; //Set the CLK pin connection to the display
const int DIO = 8; //Set the DIO pin connection to the display
TM1637Display display(CLK, DIO);
void setup(){
//LDR setup
Serial.begin(115200);
 lastStoredMillis = millis(); //millis it the ammount of milliseconds the sketch has been running
//Motor setup
 stepper.setMaxSpeed(motorSpeed);
 stepper.setSpeed(motorSpeed);
 stepper.setAcceleration(motorAccel);
 stepper.moveTo(1500); //move 32000 steps (should be 10 rev)


//7seg setup

  display.setBrightness(0x0a);  //set the diplay to maximum brightness

}
//serial setup

void printData() {
  Serial.print("sensA is :  ");
  Serial.print(sensorAValue   );
  Serial.print("     counter :  ");
  Serial.print(sensorACount);
  Serial.print("  thresh val : ");
  Serial.println(sensorAThreshold);
}

void loop(){
//ldr bit
sensorAValue = analogRead(A0);    // READ SENSOR A AND B
sensorAThreshold = 80;    // !!!! CHANGE THE VALUE OF THRESHOLD ACCORDING TO THE AMBIENT LIGHT
if(sensorAValue <= sensorAThreshold && isTheLDRAlreadyBelowTheThreshold == false)

{
  sensorACount ++;
  printData();
 display.showNumberDec(sensorACount);
}
else if (sensorAValue > sensorAThreshold)
{
  isTheLDRAlreadyBelowTheThreshold = false;
}
if (millis() - lastStoredMillis > 1000) {
    printData();
    lastStoredMillis = millis(); //reset this value
  }


 //stepper bit
 
 //if stepper is at desired location
 if (stepper.distanceToGo() == 0){
  //go the other way the same amount of steps
  //so if current position is 400 steps out, go position -400
  stepper.moveTo(-stepper.currentPosition());
 }
 //these must be called as often as possible to ensure smooth operation
 //any delay will cause jerky motion
 stepper.run();
}
ameyer
Founder
 
Posts: 3323
Joined: Thu Jan 21, 2010 11:59 pm
Location: The Bay Area


Return to Software Help

Who is online

Users browsing this forum: No registered users and 1 guest