1

For my speedometer application, I want to count the number of pulses occurring every second and convert it to speed. So for that, I need to count the number of pulses occurring at a given input pin for one second. This count is used to convert to speed. The count should go to zero and start over after every second. But I don't get how to set a limit of one second and start over every second. So far I have come up to this:

int count = 0;
float lasttime = 0;
float currenttime = 0;
const int speedpin = 52;

void setup()
{
    Serial.begin(115200);
    pinMode(speedpin, INPUT);
}

void loop()
{
    currenttime = millis();
    if (currenttime - lasttime <= 1000)
    {
        if (digitalRead(speedpin == HIGH))
        {
            count = count + 1;
        }
        else
        {
            count = count;
        }
    }
    else
    {
        count = 0;
    }
    Serial.println(count);
}
  • 1
    You may want to have a look at attachInterrupt(), which is often used for these things, because with this you will not miss a pulse. Then you should also read more about interrupts, so that you understand, what you are coding with it. – chrisl Apr 29 at 8:08
  • @chrisl It starts missing pulses much faster than using external clock source to the counter. Depends on how many mcu cycles takes to handle whole ISR. – KIIV Apr 29 at 8:20
  • 1
    @KIIV: if the pulses do not bounce (as mechanical switches do), attachInterrupt() is rather convenient and the counting itself is very fast. If there are less than 256 pulses/sec to expect, handling of this byte counter in the main loop is super easy as well. – DataFiddler Apr 29 at 9:36
  • How are you planning to display the output from your speedometer application? Will you use the serial monitor, a 7 segment display module, or maybe a 16 x 2 LCD? – VE7JRO Apr 29 at 10:23
  • 1
    Right now am seeing in the serial monitor. But in the application, I will display it in an LCD screen – Jackie Apr 29 at 10:24
1

As already explained in Michel Keijzers' answer, instead of counting how many times you find the input HIGH, you should count the LOWHIGH transitions. This requires saving the previous input state and counting a transition only when the input is HIGH while it was LOW last time you saw it:

static int last_pin_state = LOW;
int pin_state = digitalRead(SPEED_PIN);
if (pin_state == HIGH && last_pin_state == LOW) {  // rising edge
    count++;
}
last_pin_state = pin_state;

For displaying the count every second, use the technique explained in the Blink without delay Arduino tutorial:

static uint32_t last_time = 0;
if (millis() - last_time >= PERIOD) {
    Serial.println(count);
    count = 0;
    last_time += PERIOD;
}

Note that you have to reset the count every time you print it.

VE7JRO suggests using interrupts. Interrupts save you the pain of writing the edge detection. On the other hand, they also make things slightly more complicated, because you now have to worry about atomicity, race conditions, volatile variables and such. There is no need to use interrupts unless the pulses are so fast (or your loop() is so slow) that you may miss a pulse. If your loop() gets bigger and slower, and you risk missing pulses, then interrupts can really help. But the proper way to use them is to count the pulses in the ISR. If you use the ISR only to rise a flag, you loose the speed advantage of the interrupts, which is the whole point of this approach.

The interrupt service routine would then be like this:

volatile unsigned int count;

void count_pulse()
{
    count++;
}

Then, the loop only has to print and reset the count:

void loop()
{
    // Periodically print and reset the count.
    static uint32_t last_time = 0;
    if (millis() - last_time >= PERIOD) {
        noInterrupts();
        unsigned int count_copy = count;
        count = 0;
        interrupts();
        Serial.println(count_copy);
        last_time += PERIOD;
    }
}

Note that the variable count is shared between the ISR and the main loop. That's why it has to be declared volatile. Also, its access within loop() has to be protected within a critical section delimited by a noInterrupts()/interrupts() pair, otherwise you would have a race condition. Also note that, in order to keep the critical section as short as possible, the value of count is only copied within the critical section, and the actual printing is deferred to after the critical section is finished.

0

First align your code:

int count = 0;
float lasttime = 0;
float currenttime = 0;
const int speedpin = 52;

void setup()
{
  Serial.begin(115200);
  // put your setup code here, to run once:
  pinMode(speedpin, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  currenttime = millis();
  if(currenttime - lasttime <= 1000)
  {
    if (digitalRead(speedpin == HIGH))
    {
      count = count + 1;
    }
    else
    {
      count=count;
    }
  }
  else 
  {
    count=0;
  }
  Serial.println(count);
}

Than the statement count = count assigns a variable to itself which is not needed, and for increment you can use count++, so you get:

int count = 0;
float lasttime = 0;
float currenttime = 0;
const int speedpin = 52;

void setup()
{
  Serial.begin(115200);
  // put your setup code here, to run once:
  pinMode(speedpin, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  currenttime = millis();
  if(currenttime - lasttime <= 1000)
  {
    if (digitalRead(speedpin == HIGH))
    {
      count++;
    }
  }
  else 
  {
    count = 0;
  }
  Serial.println(count);
}

In the loop, you do not count pulses, but only the measurements where a pin is HIGH and when the pin is LOW anywhere within the second, it is set to 0.

What you instead want to do is (assuming you want to count the LOW -> HIGH transients: Make a global variable called e.g. previousState. Now, if the previousState == LOW and the current state of the GPIO is HIGH, only than increase count with one. Also, within the loop, each time update the previousState with the current state.

I leave the implementation to yourself.

  • But for one second I have to count the pulse. And after that one second is over count should go to zero and start counting again. Thus I need the number of pulses for each second. – Jackie Apr 29 at 10:11
  • Than you store after one second the number of measured pulses (count) in a separate variable to be processed in the next second (while count already measures the new pulses for that second). – Michel Keijzers Apr 29 at 10:15
0

Using attachInterrupt() and millis() you can calculate the frequency of the pulses. For any particular speed, there will be a specific frequency of pulses, and there should be a linear relationship between the two. Perhaps you could use the frequency and a bit of math to calculate vehicle speed.

Here's a simple test sketch that displays the frequency of the input signal.

EDIT

Based on your comment "...I will display it in an LCD screen", I've updated the sketch to work with an LCD.

// This line of code is needed for the Arduino 1.0.6 IDE.
#define NOT_AN_INTERRUPT -1

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

const byte interruptPin = 2;
unsigned long previousMillis = 0;
unsigned long previousMillisLCD = 0;
unsigned long lcdRefreshRate = 500;
unsigned long resetDisplayTimer = 1500;
volatile byte pulseDetected = 0;
float frequency = 0.0;

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup(){
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), pulse, FALLING);
  lcd.init();
  lcd.backlight();
}

void loop(){

  unsigned long currentMillis = millis();

  if(pulseDetected == 1){
    frequency = 1.0 / ((currentMillis - previousMillis) / 1000.0);
    previousMillis = currentMillis;
    pulseDetected = 0;
  }

  // No "pulse" detected for 1.5 seconds so reset the display to 0.
  if(currentMillis - previousMillis >= resetDisplayTimer){
    frequency = 0.0;
  }

  if(currentMillis - previousMillisLCD >= lcdRefreshRate){
    previousMillisLCD = currentMillis;
    lcd.setCursor(0, 0);
    lcd.print(frequency);
    lcd.print("                ");
  }
}

void pulse(){
  pulseDetected = 1;
}
  • This is timing the interval between consecutive pulses at the resolution of millis() (2 ms). If the pulse frequency is fast, the result will be quite noisy, and you may even miss pulses due to Serial.print() becoming slow when the serial buffer fills up. – Edgar Bonet Apr 29 at 10:12
  • Good point about Serial.print() becoming slow. I've asked the OP for clarification as to what type of "display" their speedometer app will have. Perhaps the use of micros() instead of millis() would be better IF the pulse frequency is fast. – VE7JRO Apr 29 at 10:31
  • The OP says they will be using a LCD screen to display the data, so Serial.print() slowing things down won't be an issue. – VE7JRO Apr 29 at 10:38
  • @Edgar Bonet: He can loose display, so he can display 10 then 12 and miss the "display" of 11 but he will NOT loose the count. – Peter Apr 29 at 11:53
  • @Peter: My comment was about the code in this answer, not about the one in the question. – Edgar Bonet Apr 29 at 12:10
0

If 'count' comes from a counter or device register, or is kept by an interrupt service routine, you don't ever want to zero the count. If a pulse arrives between the time you read the variable and the time you clear it, you will have failed to count that pulse.

Instead, make sure 'count' is of a suitable length to contain the largest number of pulses you could ever receive in one interval. Then, at each interval, save count to a temporary, say, 'tempCount'. Subtract the current 'tempCount' from the previous count, 'prevCount', in which you stored 'count' the previous time through. The difference will be the number of pulses counted in this interval. Use it or save it or whatever you need to do with it. Then store 'tempCount' into 'prevCount', and you're done.

No missed pulses, no overflow errors.

  • If count comes from an ISR, you can safely read it and clear it in the same critical section. Unless it's only 8 bits, you need that critical section just to read it anyway. If count comes from a hardware counter, then you are right, it should never be cleared. – Edgar Bonet Apr 30 at 19:59
  • Yes, if done in a critical section. But if background (ISR) code is collecting pulses and foreground code is calculating the counts, there would still be an opportunity for errors. Using a continuous counter (and it's only 1 byte wide), you could avoid the need to turn off interrupts again. – JRobert May 1 at 17:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.