AccelStepper homing with Hall Sensor full rotation

I'm new with stepper control and am trying to use AccelStepper instead of the standard Arduino stepper controls. I am using AccelStepper because I have read that it is very efficient especially when controlling multiple steppers - which this project will eventually have. I have read through the AccelStepper documentation but do not have a good understanding yet -still trying to learn.

Project: I have a NEMA 17 stepper with a 20T pulley driving a 1120T pulley. There is a Hall effect sensor that will detect a magnet in the 1120T pulley as it passes over.
When running for the first time, I need to home the 1120T pulley so that the magnet is centered in the middle of the Hall effect sensor range.
I can rotate with standard stepper code (below) so I know the Hall effect digital pin goes High over the expected range which is 25 steps on the stepper motor.

// testing a stepper motor with a Pololu A4988 driver board or equivalent
// on an Uno the onboard led will flash with each step
// this version uses delay() to manage timing

byte directionPin = 2;
byte stepPin = 3;
int numberOfSteps = 10000;
byte ledPin = 13;
int pulseWidthMicros = 10;  // microseconds
int millisbetweenSteps = 10; // milliseconds - or try 1000 for slower steps

#define Hall_Sensor A0          //A0 used with analog output, A5 with digital output
#define Hall_Sensor_D A5



int Val1=0,Val2=0;     


void setup() { 

  Serial.begin(9600);
  Serial.println("Starting StepperTest");
  digitalWrite(ledPin, LOW);
  
  delay(2000);

  pinMode(directionPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(Hall_Sensor_D,INPUT);
  
 
  digitalWrite(directionPin, HIGH);
  for(int n = 0; n < numberOfSteps; n++) {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(pulseWidthMicros); // this line is probably unnecessary
   Serial.print(n);
   Serial.print(",");
   Val1=analogRead(Hall_Sensor);            //We read both values and display them raw on the serial monitor
   Serial.print(Val1);
   Val2=digitalRead(Hall_Sensor_D);
   Serial.print(",");
   Serial.println(Val2);

   
   
    digitalWrite(stepPin, LOW);
    
    delay(millisbetweenSteps);
    
    digitalWrite(ledPin, !digitalRead(ledPin));
   

  }
  
  delay(3000);
  

  digitalWrite(directionPin, LOW);
  for(int n = 0; n < numberOfSteps; n++) {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(pulseWidthMicros); // this line is probably unnecessary
   Serial.print(n);
   Serial.print(",");
   Val1=analogRead(Hall_Sensor);            //We read both values and display them raw on the serial monitor
   Serial.print(Val1);
   Val2=digitalRead(Hall_Sensor_D);
   Serial.print(",");
   Serial.println(Val2);

   
   
    digitalWrite(stepPin, LOW);
    
    delay(millisbetweenSteps);
    
    digitalWrite(ledPin, !digitalRead(ledPin));
   

  }
}

void loop() { 
  

}

I am using the code above as a reference for AccelStepper to do a similar task but when I run it, the stepper doesn't move. How can I use AccelStepper to increment the steps one at a time and report (serial monitor) the step number and value (0/1) of the Hall sensor? Once this is resolved, I'll need to capture the step number when it goes HIGH the again when I goes LOW and determine the step number in the middle so the stepper can rotate to that position. I'm trying to solve the rotation issue first. Or find a better way of homing with this setup.

/*Example sketch to control a stepper motor with A4988 stepper motor driver, AccelStepper library and Arduino: continuous rotation. More info: https://www.makerguides.com */

// Include the AccelStepper library:
#include <AccelStepper.h> 
#include <MultiStepper.h>

// Define stepper motor connections and motor interface type. Motor interface type must be set to 1 when using a driver:
#define dirPin 2
#define stepPin 3
#define motorInterfaceType 1
#define Hall_Sensor_S A0          //A0 used with analog output, A5 with digital output
#define Hall_Sensor_D_S A5

int Val1=0,Val2=0;     


// Create a new instance of the AccelStepper class:
AccelStepper stepper = AccelStepper(motorInterfaceType, stepPin, dirPin);
int numberOfSteps = 1145;


void setup() {
  // Set the maximum speed in steps per second:
  stepper.setMaxSpeed(1000);

  Serial.begin(9600);
  Serial.println("Starting StepperTest");
  
  delay(2000);

  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(Hall_Sensor_D_S,INPUT);
  
  stepper.move (numberOfSteps) ;
  for(int n = 0; n < numberOfSteps; n++) {
   Serial.print(n);
   Serial.print(",");
   Val1=analogRead(Hall_Sensor_S);            //We read both values and display them raw on the serial monitor
   Serial.print(Val1);
   Val2=digitalRead(Hall_Sensor_D_S);
   Serial.print(",");
   Serial.println(Val2);
    }

}

void loop() {
 
}

Any guidance would be appreciated. I haven't been able to find examples similar to this task yet.
Thank you,
G

You don't need these lines of code if you are using accelStepper.

AccelStepper lib requires you to call the run() method as fast as possible. It computes when to make the next step so you should call it until you reach the desired position and not a fixed number of times.

1 Like

This should work...mostly (I didn't test). However, with the serial rate set to 9600 you are not going to call stepper.run() fast enough to reach a speed of 1000SPS.

  while (stepper.run()) {
   Serial.print(n);
   Serial.print(",");
   Val1=analogRead(Hall_Sensor_S);            //We read both values and display them raw on the serial monitor
   Serial.print(Val1);
   Val2=digitalRead(Hall_Sensor_D_S);
   Serial.print(",");
   Serial.println(Val2);
    }

You might get different answers from the sensor whether you were moving CW or CCW. I'd start with the Accelstepper bounce example, and add your code to track the hall sensor and print out stepper.currentPosition() as you bounced over the magnet.

Maybe guard to printing only once per step with:

static long last = 0;
if(stepper.currentPosition() != last){
  last = stepper.currentPosition();
  Serial.print("pos:";
  Serial.print(last);
  ...
  // read and print your hall measurements
  ...
  Serial.println();
}

Then you could see the values on the serial monitor and serial plotter and choose a good threshold.

I eliminated the two OUTPUT lines.

I will increase the serial rate.

I set the:

int numberOfSteps = 1145;

to ensure that it made a complete rotation (and a little extra) of the big pulley at least once so that I could identify both ends of the Hall Sensor HIGH area and be able to calculate the center of the Hall Sensor HIGH area for homing.

I get an error when compiling that "n" was not declared - "Compilation error: 'n' was not declared in this scope"

I think I need a way to increment the step value "n" with each step so I can capture the step value when Hall sensor goes from LOW->HIGH and then HIGH-LOW.

DaveX - I need to be able to identify the center of the Hall HIGH area automatically. I see the code identifying the current position but how does increment to the next position?

Accelstepper doesn't necessarily step every time stepper.run() is called. Based on acceleration and time stepper.run() calculates whether it needs to step or not. That is why @DaveX suggested comparing stepper.currentPosition() to the last position to determine if a step actually took place.

1 Like

Exactly. Iā€™d expect a series of step positions counting up and down and the hall readings for each position. If you formatted them as

Pos:123 hallA:12 hallD:0

Then serial plotter would identify the traces with the names and let you hover over times on the graph to see the hallA signal and what step it was.

You could also do it as

if(stepper.runSpeed()){ā€¦..}

That returns true or false depending on whether it did a step or not

I have a rough idea of the process that you all are describing using AccelStepper but to me it seems like it might be easier to use the standard approach and capture step values when Hall sensor goes from LOW->HIGH and then HIGH-LOW using single stepping as in the first code example. I could implement AccelStepper for stepper movement once the home position is determined.

Ah. I thought you were trying to use accelstepper to do its thing, and take data on the edge as it passed by.

Accelstepper takes care of remembering position and stepping in the background in a non-blocking form like the BlinkWithoutDelay example. In your code, the stepper.move(numberOfSteps) command quickly( sub-millisecond) leaves a note to itself to move to a place 1145 steps forward, and then starts the 1145 iteration-long for loop and prints out the readings as fast as it can print. The motor is not moving because the CPU never gets to follow up on the note and actually take a step before the loop runs out. If you were to put a stepper.run() in the loop, it would only have 1145 chances to take a step before the for loop expired.

To force Accelstepper to do blocking and take single steps, you could use a stepper.move(1) command with a stepper.runToPosition() to do a blocking 1-step move. Or you could combine them with a blocking stepper.runToNewPosition(stepper.currentPosition()+1); Either of those schemes should work with your code, but you'd have to move those commands inside your for loop.

On the other hand, you could leave the stepper.move(1145) in setup, and down in loop() put in a stepper.run() and some code to detect and print out any state-change on the Hall sensor:

Something like this untested code:

int lastHallState =0; // some memory of past state

void loop(void){
    stepper.run(); // move stepper as needed
    
    // check hall sensor for change:
    int hallState = digitalRead(Hall_Sensor_D_S);
    if(hallState != lastHallState){ // something changed
       lastHallState = hallState; 
       int n = stepper.currentPosition();
       Serial.print(n);
       Serial.print(",");
       Val1=analogRead(Hall_Sensor_S);            //We read both values and display them raw on the serial monitor
       Serial.print(Val1);
       Val2=digitalRead(Hall_Sensor_D_S);
       Serial.print(",");
       Serial.println(Val2);
    }
}

DaveX - thank you for the guidance. I'm understanding AccelStepper more and more. I reconfigured the code last night using the traditional stepper library and it seem to be working well to find the edges and logging them. Unfortunately the movement is not as smooth or quiet as when using AccelStepper. I'll experiment with both methods to see the results. Thank you

1 Like

Just a followup note about the stepper noise and speed. I increased the serial rate from 9600 to 115200 and it reduced the noise and increased the speed significantly.

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