Controlling Position of Stepper Motor without Limit Switches

Hello,

I'm currently working on a project where I am using a photoresistor into port A0 on the Arduino to understand light which then will cause a stepper motor to complete 3 full rotations in either the clockwise or counterclockwise direction based on the sensor value and then stop after the 3 rotations. I have attached the code I have so far and would appreciate any help you can provide. Thanks!

#include <Stepper.h> 
const int stepsPerRevolution = 2048;  // change this to fit the number of steps per revolution
const int rolePerMinute = 17;         // Adjustable range of 28BYJ-48 stepper is 0~17 rpm
// initialize the stepper library on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);

 int A = 0;
void setup() 
{
 myStepper.setSpeed(rolePerMinute);
  // initialize the serial port:
Serial.begin(9600);
analogReference(DEFAULT); // This line is not necessary: already set to 0-5V

}
void loop() 
 {
int val = analogRead(0); // define new variable
if (val < 500)
{
  Serial.println("Clockwise");
  myStepper.step(stepsPerRevolution*3);
  delay(500) ; 
}
else if (val > 500)
{
  Serial.println("Counterclockwise");
  myStepper.step(-stepsPerRevolution*3);
  delay(500) ;
}
else
{
  
}

Welcome.

What does the code do? How is that different from what you want?

That is likely to be too fast. Try a slower speed, like 10 RPM and work up till it starts to slow down from missing steps.

Thank you for posting your code properly. Very few new users bother to read the forum guidelines. Usually we have to take the time to ask them to read the rules.

use

pinMode(A0 , INPUT) // in void setup

int val = analogRead(A0); // in void loop

@shubhamsaini, setting the pinMode to INPUT is not necessary. The analogRead() function sets the pin up.

Using the Ax notation for analog inputs is a good idea. It enhances the readability of the code. But using just the channel number (0) is OK. The analogRead() function translates A0 to channel 0.

Hi, @ceredard
Welcome to the forum.

Have you built and tried your project?
You should add a Serial.print to print the value of val, so you can follow the input through the IDE monitor.

Tom.. :smiley: :+1: :coffee: :australia:

Yes, currently we have it rotating for 1 rotation and then continuing to rotate clockwise when the readout of the photoresistor is greater than 500. What I am trying to do is get it so that it will only rotate 3 times and stop and then when the value goes below 500, I need it to rotate 3 times counterclockwise and then stop. Hope that gives better clarification!

Thank you for commenting on this, I'm just starting to get my hands on Arduino code so I figured it's better to get help early so I know how to fix this later on.

When I was looking at documentation on the Stepper.h library, an example that was used was this where it references rolePerMinute as basically a RPM if I remember correctly. And changing that would change how fast or slow the rotation of the Stepper motor would be. We were able to tune it to about 9 for our liking for this project, but cannot seem to get it to stop after rotating 3 times. What instead happens currently is that it does 3 full rotations and then if you take the light off, it will do 3 the other direction but then keep rotating with no end in sight. Please let me know if you have any tips on how to stop a stepper motor rotation without limit switches. Thanks!

Instead of doing the action when the light level is different (the level is light or dark), do the action when the light changes from light to dark or from dark to light. Look at the state change detection tutorial and my companion state change for active low inputs tutorial. In those programs you look for the transitions not the levels. Those are for push buttons (digital signals), but it is easy to make the concept work for analog signals. Here is example code that does that. On light to dark the motor will rotate 3 revolutions CW and stop. Then on the dark to light transition the motor spins CCW 3 revolutions and stops. This code has been tested on real hardware, my Uno with ULN2003 driver, 28BYJ stepper and LDR (Light Dependent Resistor). I use the internal pull up resistor on the analog input as the load resistor for the LDR minimize part count.

// analog state change with stepper and LDR
// by c goulding AKA groundFungus
// in the public domain, please credit groundFungus

#include <Stepper.h>
const int stepsPerRevolution = 2048;  // change this to fit the number of steps per revolution
const int rolePerMinute = 10;         // Adjustable range of 28BYJ-48 stepper is 0~17 rpm
// initialize the stepper library on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);

const byte sensorPin = A0;

const int threshold = 500;

bool sensorState = true;
bool lastSensorState = true;

void setup()
{
   Serial.begin(115200);
   Serial.println("starting light sensitive stepper");
   pinMode(sensorPin, INPUT_PULLUP); // enable internal pullup as LDR load resistor
   myStepper.setSpeed(rolePerMinute);
}

void loop()
{
   static unsigned long timer = 0;
   unsigned long interval = 200;
   if (millis() - timer >= interval)
   {
      timer = millis();
      
      int sensorReading = analogRead(sensorPin);
      Serial.print("analog input = ");
      Serial.println(sensorReading);
      if (sensorReading < threshold)
      {
         sensorState = true;
      }
      else if (sensorReading > threshold)
      {
         sensorState = false;
      }

      if (sensorState != lastSensorState)
      {
         if (sensorState == true)
         {
            Serial.println("Clockwise");
            myStepper.step(stepsPerRevolution * 3);
         }
         else
         {
            Serial.println("Counterclockwise");
            myStepper.step(-stepsPerRevolution * 3);
         }
         lastSensorState = sensorState;
      }
   }
}

Also, I do not use delay for timing. I use the millis() function for non-blocking timing. It does not matter much in this simple code because the stepper library is blocking, but it is a good thing to do as a matter of course.
Non-blocking timing tutorials:
Blink without delay().
Beginner's guide to millis().
Several things at a time.

I consider learning to use millis() one of the most important concepts for embedded programming.

I'd tend to use a standard mechanical limit switch or infrared slot optical sensor as a digital input.

I don't know for sure, but it seems that the original code is for something like a sunshade, hence the LDR. I don't see why a limit switch would be needed at all if the 3 revolutions (by step count) positions the stepper properly.

A limit switch may be needed to "home" the stepper at initialization, but the OP did not mention anything about that. The mechanical system may position the stepper properly.

If the stepper motor continues to run in one direction, then it is not properly connected.

The sketch should act according to the state it is in. It has two states: when it is in 'home' position, and when it has rotated. groundFungus uses global variables for that. I use a global variable 'alreadyRotated' to know the state that the sketch is in.

I also added some hysteresis, but I don't know if you need that in your project.

This might be closer to your own sketch:

// For: https://forum.arduino.cc/t/controlling-position-of-stepper-motor-without-limit-switches/1037813/
// This Wokwi project: https://wokwi.com/projects/344507517244539474

#include <Stepper.h>

const int pinLDR = A0;
const int hysteresis = 10;
bool alreadyRotated = false;          // true when rotated clockwise

const int stepsPerRevolution = 2048;  // change this to fit the number of steps per revolution
const int rolePerMinute = 17;         // Adjustable range of 28BYJ-48 stepper is 0~17 rpm

// initialize the stepper library on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11);


void setup()
{
  // initialize the serial port:
  Serial.begin(9600);

  myStepper.setSpeed(rolePerMinute);
}


void loop()
{
  int val = analogRead(pinLDR);         // define new variable
  if (val < (500 - hysteresis) && !alreadyRotated)
  {
    Serial.println("Clockwise");
    myStepper.step(stepsPerRevolution * 3);   // do the steps and wait until ready
    alreadyRotated = true;
  }
  else if (val > (500 + hysteresis) && alreadyRotated)
  {
    Serial.println("Counterclockwise");
    myStepper.step(-stepsPerRevolution * 3);
    alreadyRotated = false;
  }

  delay(500);                          // slow down the sketch
}

The sketch in Wokwi simulation:

Start the simulation and then click on the LDR module to change it.

Is that a geared stepper motor ? Normally is 200 steps. I can not set the Wokwi simulator to 2048 steps, so I made it 2000 for a full rotation (2000 for the simulated hardware, 2048 in the sketch).

If the simulation works, try to replace the pin numbers with your pin numbers:

Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);

Then it does not work anymore.

Yes exactly, it is for a sunshade rod. I'm trying to test out some cool ways to use the photoresistor value read by it to then translate the rod in either a clockwise or counterclockwise direction 3 rotations, but then stop after and not move until another light change occurs. I've tested the code I have now and it works based on the photoresistor value, I just can't seem to find documentation to stop the rotation after three rotations. Would appreciate insight on how to stop the stepper without a limit switchw

Did you try the code that I posted? It does work, I know because I built the circuit that I posted and tested it on real hardware.

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