Stepper motor runs continuously

I want to make my stepper revolve 3 or 4 times when the light sensor detects light intensity below 400, but with my code it runs continuously. If I put the step function in void setup instead of loop, it causes it to malfunction and not run properly.

#include <Stepper.h>
int StepsPerRevolution=2048;
int motSpeed=9;
int dt=500;
int lightPen=A0;
int lightVal;
int dv=500;
Stepper myStepper(StepsPerRevolution,9,11,10,12);
void setup() {
  
 
  Serial.begin(9600);
  myStepper.setSpeed (motSpeed);
  pinMode(lightPen,INPUT);
  
  }

void loop() {

lightVal=analogRead(lightPen);
Serial.println(lightVal);
delay(dv);
if(lightVal<400)
  {
    myStepper.step(StepsPerRevolution);
    return 0;
   }


}

That does not make sense. You can put that code in setup() and then it will only run one time. What stepper motor are you using and how are you powering it?

Also, you speed is really low (motSpeed=9). Try out the stepper OneRevolution example to see if it is your hardware or your code (File->examples->stepper->OneRevolution)

If i put the if condition outside void loop its not able to get the value of "lightVal" as its defined inside void loop. And I kind of want it to run only once but once the light intensity gets high again I want it to do it again when the lightval value decreses below threshold

Please explain exactly when your stepper should turn. Because loop() - as the name suggests :wink: - loops, it will start your motor with every loop() cycle again - as long as your light value is below 400.
You need to remember in a variable that it already has been started so you will not start it again with the next loop() cycle.
I suppose it should start again once your light value has increased above 400 and after that falls below again?

Here is a demo code that will have the stepper turn one revolution when the light to the LDR is blocked. If the light is unblocked it must be blocked again to get the motor to move. The code uses a variation of the state change detection method. Also hysteresis is used so the output will not chatter near the threshold. The MobaTools library can be installed using the IDE library manager. I use the internal pullup on the analog input as the load resistor for the LDR so an external resistor is not needed.

//  by groundFungus aka c.goulding

#include <MobaTools.h>

const byte sensorPin = A0;

const byte pin1 = 4;
const byte pin2 = 5;
const byte pin3 = 6;
const byte pin4 = 7;

const byte ledPin = 13;

const int threshold = 400;
const int hysteresis = 50;

bool sensorState = true;
bool lastSensorState = true;

const unsigned int motorStepsPerRev = 2048;
MoToStepper stepper(motorStepsPerRev, FULLSTEP);

void setup()
{
   Serial.begin(115200);
   pinMode(sensorPin, INPUT_PULLUP);
   pinMode(ledPin, OUTPUT);

   stepper.attach(pin1, pin2, pin3, pin4);
   stepper.setSpeed(90); // rpm /10
   stepper.setRampLen(2);
   stepper.setZero();
}

void loop()
{
  int sensorReading = analogRead(sensorPin);
   if (sensorReading < threshold - hysteresis)
   {
      sensorState = true;
   }
   else if (sensorReading > threshold + hysteresis)
   {
      sensorState = false;
   }

   if (sensorState != lastSensorState)
   {
      if (sensorState == LOW)
      {
         Serial.println("moving");
         stepper.move(motorStepsPerRev);
      }
      
      lastSensorState = sensorState;
   }
}

Tested on real hardware.

While @groundFungus has given you an example using the MobaTools here is one using the Stepper library:

/*
  Forum: https://forum.arduino.cc/t/stepper-motor-runs-continuously/1183191
  Wokwi: https://wokwi.com/projects/379848824166403073
*/

#include <Stepper.h>

/*
 The Wokwi Simulation uses a stepper motor with 200 steps/revolution @ 100 rpm
  Comment the line "#define SIMULATION" out to use 2048 steps/revolution @ 9 rpm instead

*/
//#define SIMULATION


#ifdef SIMULATION
constexpr int StepsPerRevolution = 200;
constexpr int motSpeed = 100;  // rpm
#else
constexpr int StepsPerRevolution = 2048;
constexpr int motSpeed = 9;
#endif

// The sensor value must be below rotateThreshold to start rotation
constexpr int rotateThreshold = 400;
// The sensor must provide a value above the resumeThreshold
// before the next rotation may start (Hysteresis to avoid switching between
// both situations close around the value of rotateThreshold)
constexpr int resumeThreshold = 450;


constexpr byte lightPen = A0;
constexpr unsigned long measurementInterval = 500L;

// Define the no of rotations after rotation is triggered
constexpr unsigned long noOfRotations = 2L;

Stepper myStepper(StepsPerRevolution, 9, 11, 10, 12);

// Store the time in millis() when the measurement was done
// and use it to measure every measurementInterval [msec] only
unsigned long lastMeasurement = 0;
int lightVal;

// The variable triggerRotationAllowed is set to false in the beginning 
// so that the motor will NOT rotate when the sketch starts with the 
// light value below rotateThreshold! The light value has to exceed
// resumeThreshold once to allow to trigger rotations.
boolean triggerRotationAllowed = false;
// Holds the no of steps that the motor shall move
unsigned long noOfSteps = 0;

void setup() {
 Serial.begin(9600);
 myStepper.setSpeed (motSpeed);
}

void loop() {
 checkLightSensor();
 rotateIfRequired();
}


void checkLightSensor() {
 // Does a measurement every measurementInterval [msec]
 if (millis() - lastMeasurement >= measurementInterval) {
   lastMeasurement = millis();
   lightVal = analogRead(lightPen);
   Serial.println(lightVal);
   // If value below the threshold AND rotation is allowed)
   if (lightVal < rotateThreshold && triggerRotationAllowed) {
     // then set noOfSteps to the steps required von noOfRotation revolutions
     noOfSteps = noOfRotations * StepsPerRevolution;
     // Store that the rotation is triggered now
     // to avoid running into this part of the code
     // while lightVal stays below resumeThreshold
     triggerRotationAllowed = false;
   }
   // In case that lightVal is above resumeThreshold
   // and any earlier rotations are done (noOfSteps == 0)
   // allow
   if (lightVal > resumeThreshold && noOfSteps == 0) {
     triggerRotationAllowed = true;
   }
 }
}

void rotateIfRequired() {
 // If noOfSteps is greater than zero make one step
 if (noOfSteps > 0 ) {
   // Do one step
   myStepper.step(1);
   // Reduce the number of steps by 1 to get the remaining steps
   noOfSteps--;
 }
}

You may check it out on Wokwi: https://wokwi.com/projects/379848824166403073

It includes a lot of comments to explain how it works.

The main problem with the Stepper lib used here is that the function myStepper.step(noOfSteps) blocks for the whole time it takes to move noOfSteps. The controller cannot do anything else during this time. In some applications you don't care but in a lot of application it matters ...

That's where the MobaTools come into play: They can handle stepper motors much better and "in the background" so that the functions are non blocking. And that's why it is usually recommended to use this really advanced library.

Good luck!

Hi @harkha
this is your code with better formatting and some comments
about what your code does

[code]
#include <Stepper.h>
int StepsPerRevolution = 2048;
int motSpeed = 9;
int dt = 500;
int lightPen = A0;
int lightVal;
int dv = 500;

Stepper myStepper(StepsPerRevolution, 9, 11, 10, 12);


void setup() {
  Serial.begin(115200);
  myStepper.setSpeed (motSpeed);
  pinMode(lightPen, INPUT);
}


void loop() {
  // TOP-OF-LOOP
  // repeated code-execution starts here again and again and again
  lightVal = analogRead(lightPen);
  Serial.println(lightVal);
  delay(dv);
  
  if (lightVal < 400) {
    myStepper.step(StepsPerRevolution);
    // return 0; not nescessary to do a return because 
    // at the end of function loop() code-execution
    // jumps up to TOP-OF-LOOP
  }

} // if this line is reached code-execution jumps up to TOP-OF-LOOP
[/code]

Do you know the WOKWI-Simulator?
Often very helpful
Here is your code as WOKWI-simulation with additional printing to the serial monitor

The printing to the serial monitor makes visible what you code is really doing

best regards Stefan

Yes exactly! I want to do 2 rotation when light value falls below 400 and then stop, and once it gets above 400 it should do 2 rotation in negative direction and so on and so forth

@harkha

if your arduino uno measures a value below 400. Does this mean
bright light
or
darkness?

darker below 400 and brighter above 400

OK so here is the code from groundfungus with additional explanations and some variable-names changed so that the names are more self-explaining.

//  by groundFungus aka c.goulding

#include <MobaTools.h>

const byte sensorPin = A0;

const byte pin1 = 4;
const byte pin2 = 5;
const byte pin3 = 6;
const byte pin4 = 7;

const byte ledPin = 13;

const int threshold = 400;
const int hysteresis = 50;

#define darkness    true  // use the word "darkness" in the code for  boolean value "true"
#define brightLight false // use the word "brightLight" in the code for  boolean value "false"

bool sensorDarkOrBright     = darkness;
bool lastSensorDarkOrBright = darkness;

const unsigned int motorStepsPerRev = 2048;
MoToStepper stepper(motorStepsPerRev, FULLSTEP);

void setup() {
   Serial.begin(115200);
   Serial.println("Setup-Start");
   
   pinMode(sensorPin, INPUT_PULLUP);
   pinMode(ledPin, OUTPUT);

   stepper.attach(pin1, pin2, pin3, pin4);
   stepper.setSpeed(90); // rpm /10 90 means in 10 seconds 90 rotations = 90 / 10 = 9 rotations per second
   stepper.setRampLen(2);
   stepper.setZero();
}

void loop() {
  int sensorReading = analogRead(sensorPin);

  // check if value from sensor is REALLY below threshold
   if (sensorReading < threshold - hysteresis) {
      sensorDarkOrBright = darkness; // transform ADC-number into value "darkness"
   }
   // check if value from sensor is REALLY above threshold 
   else if (sensorReading > threshold + hysteresis) {
      sensorDarkOrBright = brightLight; // transform ADC-number into value "brightLight"
   }

   // check if sensor-state has CHANGED 
   if (sensorDarkOrBright != lastSensorDarkOrBright) {
      // check if value of sensor is "darkness"
      if (sensorDarkOrBright == darkness) {
         Serial.println("moving");
         stepper.move(motorStepsPerRev);
      }      
      lastSensorDarkOrBright = sensorDarkOrBright; // update variable lastSensorDarkOrBright 
   }
}

best regards Stefan

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.