Arduino Missing Hall Sensor Pulses - Interrupt Not Helping

I am using a hall effect sensor to detect positions as a wheel rotates. The speed is roughly 2 RPM so considerably slow. There are 12 positions, and the wheel is 6 inches in diameter. So, the magnets are not interfering with one another as there is plenty of space between them.

The Hall Sensor I am using has a built in LED and blinks each time a magnet is detected, I can see that the sensor is not missing the pulses, however the Arduino seems to be. I want it to move 12 positions and then stop in this example. However, it often moves 16-18 instead. I tried using an interrupt to ensure the sensor is read, however this still did not seem to help.

I think I have stared at this issue too long and just cannot find the issue. If anyone with a fresh set of eyes and better understanding than myself is able to help that would be wonderful!

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20, 4);  // set the LCD address to 0x3F for a 16 chars and 2 line display

volatile int hallFlag;

const int hallSensor = 19;
int hallSensorState;
int hallSensorPrevState;
int hallSensorCount = 0;

const int motorPin1 = 11;
const int motorPin2 = 13;
const int pwm = 12;
int motorSpeed = 100;

#define CLK 3
#define DT 4
#define SW 2
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir = "";
unsigned long lastButtonPress = 0;

int currentPosition;
int prevPosition = 1;
int currentRotation;
int prevRotation;

void setMotor(int dir, int motorSpeed, int pwm) {
  currentRotation = dir;
  analogWrite(pwm, motorSpeed);
  if (dir == 1) {
    digitalWrite(motorPin1, HIGH);
    digitalWrite(motorPin2, LOW);
  } else if (dir == -1) {
    digitalWrite(motorPin1, LOW);
    digitalWrite(motorPin2, HIGH);
  } else {
    digitalWrite(motorPin1, LOW);
    digitalWrite(motorPin2, LOW);
  }
}

void autoHome() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Homing....");
  delay(1000);
  hallSensorState = digitalRead(hallSensor);
  while (hallSensorState != LOW) {
    setMotor(1, motorSpeed, pwm);
    hallSensorState = digitalRead(hallSensor);
    Serial.println();
    if (hallSensorState == HIGH) {
      Serial.print("Hall Sensor State: HIGH");
    } else if (hallSensorState == LOW) {
      Serial.print("Hall Sensor State: LOW");
    } else {
      Serial.print("Hall Sensor State: Unknown");
    }
  }
  setMotor(0, motorSpeed, pwm);
  currentPosition = 1;
  delay(2000);
}

void ISR_hallSensor() {
  hallFlag = 1;
}

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.clear();
  lcd.backlight();  // Make sure backlight is on

  attachInterrupt(digitalPinToInterrupt(19), ISR_hallSensor, FALLING);

  pinMode(hallSensor, INPUT);
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(pwm, OUTPUT);

  // Set encoder pins as inputs
  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
  pinMode(SW, INPUT_PULLUP);

  // Setup Serial Monitor
  Serial.begin(9600);

  // Read the initial state of CLK
  lastStateCLK = digitalRead(CLK);  // put your setup code here, to run once:
  autoHome();
}

void loop() {

  int btnState = digitalRead(SW);
  if (btnState == LOW) {
    currentPosition = 1;
  }
  hallSensorState = digitalRead(hallSensor);
  Serial.print("Current Poistion is: ");
  Serial.print(currentPosition);
  Serial.print("  Hall Sensor Previous State is: ");
  Serial.print(hallSensorPrevState);
  Serial.print("  Flag State is: ");
  Serial.print(hallFlag);
  Serial.print("      Hall Sensor State is: ");
  Serial.println(hallSensorState);



  while (currentPosition < 13) {
    Serial.print("Current Poistion is: ");
    Serial.print(currentPosition);
    Serial.print("  Hall Sensor Previous State is: ");
    Serial.print(hallSensorPrevState);
    Serial.print("  Flag State is: ");
    Serial.print(hallFlag);
    Serial.print("      Hall Sensor State is: ");


    Serial.println(hallSensorState);
    setMotor(1, motorSpeed, pwm);
    hallSensorState = digitalRead(hallSensor);
   // currentRotation = -1;
    if (hallFlag == 1 && hallSensorState != hallSensorPrevState) {
      // if (hallSensorState == LOW && hallSensorState != hallSensorPrevState) {
      //   currentPosition = currentPosition + 1;
      if (currentRotation == 1) {
        currentPosition = currentPosition + 1;
      } else if (currentRotation == -1) {
        currentPosition = currentPosition - 1;
      }
      hallSensorPrevState = hallSensorState;
      hallFlag = 0;
    }
    
  }
  setMotor(0, motorSpeed, pwm);
}

Which arduino ?
How are things wired ?

Should have specified since I am talking about interrupt pins. It is an Arduino Mega.

As for wiring, what is the best way to represent this? I can put something together

Just a pencil and paper simple drawing of used pins then snap a picture and post it here

and a link to your hardware besides the mega could be useful too (eg does the hall sensor require a pull-up ?)

Have you tried a super simple code with a button library for example and just polling since the rotation pace is super slow ?

(You have tons le print which are using interrupts and possibly making you miss some hits)

Below are some references, including the components used and a wiring sketch. I am away from home at the moment so the sketch is from memory but is simple enough I am fairly certain its accurate.

Could you elaborate on the polling with a button question?

HiLetgo 5pcs Hall Effect Magnetic Sensor Module 3144E A3144 Hall Effect Sensor DC 5V for Arduino PIC AVR Smart Cars: Amazon.com: Industrial & Scientific

Amazon.com: WayinTop 5pcs 360 Degree Rotary Encoder Module KY-040 Brick Sensor Development Board with Push Button for Arduino : Electronics

Try:
pinMode(hallSensor, INPUT_PULLUP);

I can give it a shot tomorrow when I am back home, but when I simplified the code to only showing the state of the Hall Sensor it does behave as expected and not erratically.

I don't believe a floating pin is the issue. Seems like the code just misses the signal. Also removed all Serial.prints and unfortunately the problem persisted.

First, if hallSensorState = digitalRead(hallSensor) is LOW, autoHome does nothing, no "homing".
You should start the motor anyway and stop at first falling edge.

Next, the right place for setMotor(1, motorSpeed, pwm); (line 128 in my copy) is inside this if true clause :

  if (btnState == LOW) {
    currentPosition = 1;
  }

(line 103-104 in my copy) although it doesn't cause a malfunction.
==>

  int btnState = digitalRead(SW);
  if (btnState == LOW) {
    currentPosition = 1;
    setMotor(1, motorSpeed, pwm); // <-- RELOCATED
  }

Next :
if (hallFlag == 1 && hallSensorState != hallSensorPrevState) {
Note that hallSensorPrevState has not been previously initialized.
and using both hallFlag and hallSensorState is weird. You should choose one or the other.
Actually, hallSensorState != hallSensorPrevState detects a change of state (rising or falling edge) and hallFlag detects a falling edge (because the flag is reset right after the test)
In my opinion, you should choose

if (hallFlag == 1) {
.
.
.

Next :

      if (currentRotation == 1) {
        currentPosition = currentPosition + 1;
      } else if (currentRotation == -1) {
        currentPosition = currentPosition - 1;
      }

Simplify :

currentPosition += currentRotation;

Moving the set motor function prevented it from running so I placed back to the original location. But changing the way the flag is checked fixed the missing pulses (it seems)!

Confirmed this solved the problem! A week later and still consistent

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