Just looking for some guidance - lcd hall effect

Hi there,
Just want to preface this by expressing my admiration and gratitude at how much, and how quickly, help is offered here. I certainly don't want to come off as abusing the community - that being said after many many hours there's only so much frustration one can take :slight_smile: I realize I could spend more and get to the finish line on my own but ya...

Anyways, I am trying to use a hall effect sensor to merely display the RPMs of a DC treadmill motor on a 16x2 LCD display - both modules are the ones included in the amazon kit(elegoo? adafruit? cant remember....) I realize that the former treadmill came with a sensor/lcd speed display but this is merely a learning exercise as I am relatively new to programming and have no formal background in it. i tried to see how far I could get on my own and then proceeded to cheat here and there and snipe others' ideas/code. The result - or rather an iteration of many is below. I don't really care about polling rate or anything like that and anything withing +-5-10% would be fine with me. So far, a slightly different version of my program seemed wildly inaccurate at lower RPMs (if I had to guess ~0-1500 or so. Motor's spec label claims up to 7500 but never ran it over half chooch) and wouldn't even start registering revolutions on the display until a few seconds after movement had begun.

Currently my program doesn't read at all. If I am being honest though, I have so many saved versions it's hard for me to keep track so that's on me. I know that there is some utility somewhere in the abortion field, though :slight_smile:

Display currently displays merely the setup instructions and doesn't change. What I did differently, as far as I remember since it's been about a week, is try to implement a function - seemed reasonable since I wasn't confident with the other results and I'd never created/implemented the idea on my own. Also, I wasn't full grasping the logic behind the snippets I had frankansteined together that were somehow producing the quasi-acceptable results I mentioned prior. Anyways, here's the code in its entirety. I'm sure there's plenty of work to be done - open to any and all suggestions including things outside the immediate scope of this task.

int hallIN = 8;
float hall_thresh = 100.0;
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int rpm_post;

void  setup() {
  Serial.begin(115200);
  pinMode(hallIN, INPUT);
  lcd.begin(16,2);
  lcd.print("RPM:  ****");
  rpm_post = 0;
}

bool equalityCheck(int a, int b)
  {
    int difference = a-b;
    if (difference < 10) return true;
    if (difference > -5) return true;
  }
  
void  loop() {
  float hall_count = 1.0;
  float start = micros();
  bool on_state = false;
  
  while (true) {
    if (digitalRead(hallIN) == 0) {
      if (on_state == false) {
        on_state = true;
        hall_count += 1.0;
        delay(1);
      }
    } else {
      on_state = false;
    }

    if (hall_count >= hall_thresh) {
      break;
    }
  }

  float end_time = micros();
  float time_passed = ((end_time - start) / 1000000.0);
  int rpm_val = (hall_count / time_passed) * 60.0;
  
  lcd.setCursor(6,0); 
  bool almostEqual = equalityCheck(rpm_val, rpm_post);
  Serial.print(almostEqual);
  if (almostEqual == 1) {
    lcd.print("----");
  }
  else {
    lcd.print(rpm_val);
  }

  rpm_post = rpm_val;
  
  delay(1);
}
while (true) {

Don't do that. loop() already loops.

Don't use floating point numbers as counters. That is hard work for a little Arduino. Treat it like a child who is very good at counting on its fingers.

Also don't use float for time. Use unsigned long. (Fingers+toes.)

Look for an example online which uses interrupts. Copy that. Do the LCD part later.

So found a lot wrong with the code I uploaded earlier. I think it was one of the bastard children of a hail mary combination of a few different things. I need to keep better track or find a better system. Anyways, I ended up with something ok for now. I am somewhat confused by the code though.

byte hall_pin = 8;
byte hall_thresh = 100;
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void  setup() 
    {
    Serial.begin(115200);
    pinMode(hall_pin, INPUT);
    lcd.begin(16,2);
    lcd.print("RPM:  ----");
    }
  
void  loop() 
    {
    byte hall_count = 1;
    unsigned long start_time = micros();
    bool on_state = false;
  
    while (true) 
    {
        if (digitalRead(hall_pin) == 0) 
        {
            if (on_state == false) 
            {
            on_state = true;
            hall_count += 1;
            delay(1);
            }
        }
        
        else
        {
            on_state = false;
        }
        
        if (hall_count>=hall_thresh)
        {
            break;
        }
    }

  unsigned long end_time = micros();
  float elapsed_interval = ((end_time - start_time) / 1000000.0); 
  int rpm_val = (hall_count / elapsed_interval) * 60.0;
  
  lcd.clear();
  lcd.print("RPM:");
  lcd.setCursor(6,0); 
  lcd.print(rpm_val);
  delay(1);
}

specifically this part:

while (true) 
    {
        if (digitalRead(hall_pin) == 0) 
        {
            if (on_state == false) 
            {
            on_state = true;
            hall_count += 1;
            delay(1);
            }
        }
        
        else
        {
            on_state = false;
        }
        
        if (hall_count>=hall_thresh)
        {
            break;
        }
    }

How can the condition for while just be "true". What is true? IMy understanding is that anything in the scope of the while loop will be continuously executed in order until the initial condition returns a false boolean value. Don't you have to have something more substantive like on_state = true?

Then the hall_pin == 0 part. in my setup hall_pin is declared an input pin. Shouldn't the relative return query be LOW or is 0 the same thing syntactically speaking?

A minor closing question (for now! haha) I have seen the suggestions to use unsigned long instead of float regarding timing related matters but if that level of accuracy isn't necessary - like with the elapsed_interval variable is it still prudent to declare it unsigned long?

Again I apologize it's hard for me to try and keep all the diction coherent - I keep finding many mistakes in my wording, not to mention snippets lol.

Look for an example online which uses interrupts. Copy that. Do the LCD part later.

^^^ this.

The hall effect sensor is most likely putting out a very short duration pulse, so you cannot reliably read that using digitalRead, because you would have to read the input at exactly the same time as the pulse occurred. Using an interrupt will cause a section of code to run based on the state of an input pin, without having to actively monitor the pin inside your code, so an interrupt routine can have a counter accumulating pulses, or measuring the time between pulses, and then your code can periodically read that data and display it.

I was wondering about that....seemed very likely to me that pin states would fail to coincide with the execution of the code. What you said makes (general) sense to me and I'll certainly pursue that.

Any advice regarding my confusion with the while loop? Even if I am not going to use it it bothers me to not get it.

the general form of a while loop is

while (condition) {
// loop of code to execute
}

normally (condition) will be evaluating some condition, such as the value of a variable, state of an input pin, etc, and at some point it will become false. The condition is checked at the start of each loop, so when the condition does become false, you leave the loop and continue with the next line of code following the loop. The statement "while (true)" will always evaluate as true, and will never leave the loop, which doesn't seem to make much sense, but it does have some uses. Often you will see it where someone wants to completely stop execution of their code, in which case it takes the form of " while (true) {};" and sits and does nothing forever. There are also ways to break out of a loop even when the condition is always true, but I don't recall seeing that done very often.

The void (loop) section of code basically acts the same as a while (true), so its seen as a bit redundant.

most of that makes sense and maybe it's trivial but i don't see how it's logical that true will always evaluate as such. Doesn't that presuppose things are true?

The (condition) in a while statement can only evaluate to true or false. Generally it will be a test of something, such as a comparison, but you can explicitly state the condition as true or false.

The code you posted earlier does have a fairly good reason for using the while (true), it is a very tight loop that is repeatedly reading the input pin connected to the hall effect sensor, so that there is less of a chance of missing the pulse coming from the sensor. There is a counter, hall_count, that is incremented when the input goes LOW ( == 0 ), and the variable on_state is used to make sure you only increment the counter once per pulse. When the counter reaches a pre-set value ( hall_thresh ), it then executes a break instruction, which terminates the while loop and continues on with the code that follows it.

The time was saved immediately prior to the while loop, and then again immediately afterwards, so that the RPM can be calculated from the amount of time that it took to count a specific number of pulses coming from the hall effect sensor.

The reason they used while(true) is that the code needs to repeatedly read the input pin to the exclusion of everything else in order to get an accurate count, although they could just have easily used "while (hall_count < hall_thresh)" and left out the last if statement.

while (true)  {
        if (digitalRead(hall_pin) == 0) {
            if (on_state == false) {
            on_state = true;
            hall_count += 1;
            delay(1);
            }
        } else {
            on_state = false;
        }
        
        if (hall_count>=hall_thresh) {
            break;
        }
    }

thank you so much for the response! I was going to ask exactly what you mention at the end - why not use while (hall_count < hall_thresh)" and leave out the last if statement. I had an inkling a while ago but assumed the random internet creator of the sniped code I was using was probably more likely to be right. I realize there's many ways to screw a chicken but ya. Thanks haha.