Bike speedometer

Why does it only input natural numbers on revolution rate? It never shows something like 0.23 rev/min...

/*
  Connect Vcc and Gnd of sensor to arduino, and the
  signal line to arduino digital pin 2.
*/
byte statusLed    = 13;
byte sensorInterrupt = 0;  // 0 = digital pin 2
byte sensorPin       = 2;
volatile byte pulseCount;
float revolutionsRate;
unsigned long ranKm;
unsigned long velocity;
unsigned long oldTime;

void setup()
{
  // Initialize a serial connection for reporting values to the host
  Serial.begin(38400);
  // Set up the status LED line as an output
  pinMode(statusLed, OUTPUT);
  digitalWrite(statusLed, HIGH);  // We have an active-low LED attached
  pinMode(sensorPin, INPUT_PULLUP);
  digitalWrite(sensorPin, HIGH);
  pulseCount        = 0;
  revolutionsRate   = 0.0;
  velocity          = 0;
  ranKm             = 0;
  oldTime           = 0;
  // The Hall-effect sensor is connected to pin 2 which uses interrupt 0.
  // Configured to trigger on a CHANGE state
  
  attachInterrupt(sensorInterrupt, pulseCounter, CHANGE);
}
void loop()
{
  if ((millis() - oldTime) >= 1000)   // Only process counters once per second
  {

  
    // Because this loop may not complete in exactly 1 second intervals we calculate
    // the number of milliseconds that have passed since the last execution and use
    // that to scale the output.

    revolutionsRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / 2;
    

    // Note the time this processing pass was executed. Note that because we've
    // disabled interrupts the millis() function won't actually be incrementing right
    // at this point, but it will still return the value it was set to just before
    // interrupts went away.
    oldTime = millis();

    
    velocity = revolutionsRate * 2.4 * 3.6;

    ranKm += velocity / 3.6;

    unsigned int frac;

    // Print the flow rate for this second in litres / minute
    Serial.print(" Revolutions Rate: ");
    Serial.print(int(revolutionsRate));  // Print the integer part of the variable
    Serial.print(".");             // Print the decimal point

    frac = (revolutionsRate - int(revolutionsRate)) * 10;
    Serial.print(frac, DEC) ;      // Print the fractional part of the variable
    Serial.print(" Rev/min ");
   
    Serial.print(" Velocity: ");             // Output separator
    Serial.print(velocity);
    Serial.print("Km/h");

  
    Serial.print("  Distance ran: ");             // Output separator
    Serial.print(ranKm);
    Serial.println("Mtrs");

    // Reset the pulse counter so we can start incrementing again
    pulseCount = 0;


  }
}

void pulseCounter()
{
  // Increment the pulse counter
  pulseCount++;
}

Why are you printing revolutionsRate in such a convoluted fashion? If you want just one digit after the decimal point:

   Serial.print(revolutionsRate, 1);

If you only want one digit after the decimal point, how can you expect to see 0.23 printed?

You can replace the name of a variable globally by highlighting it and pressing ctrl-F or "edit->find" though the menu. You will get a dialog box where the current name is. You enter the name you wish to replace it with, and choose "replace all". Sometimes it helps to deselect "ignore case".

This works with names like "waterFlow" but not with names like "x", which is a good reason not to use such names in the first place (not saying you did, but just noting in passing).

But it doesn't even show like 2.3...

But it doesn't even show like 2.3...

Send me the hardware, so I can see for myself what the code prints, since you don't seem inclined to share that (or to add more Serial.print() statements to see where the problem might be).

The hardware...

And the serial print... This time showed 3.5 but no other decimal number...

Posting pictures of text is a waste of bandwidth.

Are you still using your overly complex code for printing revolutionsRate? Or, are you using the simple code in reply #44?

That one didn't work either. Sorry for the bandwidth waste.

I'm done guessing what your code and serial output looks like. You can be proactive in sharing the code and serial output or you can fix your problems yourself.

Why are you mad? I can show you anything you need to help me, maybe I didn't understand what you needed... And I've shared the code many times. What do you want me to share?

Some code that looks like you wrote it, and pays heed to the detailed and well targeted advice in this thead from some of the most knowledgeable people on this forum. Go back to page one and read everything again. Everything.

I read everything again. Now I'm using the German code with some minor changes... But about the backlight control, I thought that since I'm going to run the speedometer on batteries, it's not a good idea to use a resistor's bridge (10k and the LDR), so, how can I set a single push button to select something like 4 values of brightness? And how can I set another push button to save and erase the run KM to the EEPROM? Also, what should be done to show the maximum speed reached? Thanks for the attention.

Here's the current code I'm using:

//Number of  Display columns (16)
#define LCD_WIDTH 16

//Number of Display lines (2)
#define LCD_HEIGHT 2

// Pin for hall sensor, digital 2 for Interrupt 0
#define REEDPIN 2

// Hardware interrupt for the Hall-Pin
#define REEDINTERRUPT 0

// Wheel size in mm
#define WHEEL 1540

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 6);

byte blueBack = 9;
int light = 0;
/////
const int numReadings = 400;
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;
float avgtemp = 0;                // the average
int inputPin = A2;

void setup() {
  analogWrite(blueBack, 255);
  lcd.begin(LCD_WIDTH, LCD_HEIGHT);
  lcd.print("By Caio M. Poit");
  delay(2479);
  pinMode(REEDPIN, INPUT_PULLUP); //connect hall sensor without resistance
  attachInterrupt(REEDINTERRUPT, reedISR, CHANGE);
  lcd.clear();
}

volatile byte reedCountSum;
volatile long reedMillisSum;
unsigned long lastReedMillis;
void reedISR()

{
  if (millis() - lastReedMillis >= 25) //25 ms corresponds to max. 40 wheel revolutions per second
  {
    reedCountSum++;                 // Count one revolution
    reedMillisSum += millis() - lastReedMillis; // add time
    lastReedMillis = millis();     // Remember time
  }
}
unsigned long totalRevolutions;

void speedometerDisplay()
{
  byte revolutions;
  unsigned long TIME;
  float kph, kilometer;
  char buffer[10];
  noInterrupts();            // Disable interrupts
  revolutions = reedCountSum; // Copy count variable
  reedCountSum = 0;        // Reset count variable to 0
  TIME = reedMillisSum;    // Copy time counter
  reedMillisSum = 0;       // Reset timer to 0
  interrupts();              // Allow interrupts again
  totalRevolutions += revolutions; // Summing all wheel revolutions
  kilometer = (float)totalRevolutions * (float)WHEEL / 1000000.0; // Bicycle Odometer
  if (revolutions > 0)
    kph = float(WHEEL) * (float)revolutions / (float)TIME * 3.6;
  else
    kph = 0.0;
  lcd.setCursor(0, 0);
  dtostrf(kilometer, 5, 3, buffer);
  lcd.print(buffer);
  lcd.print(" km");
  lcd.setCursor(0, 1);
  lcd.print("KM/h ");
  dtostrf(kph, 4, 1, buffer);
  lcd.print(buffer);
}
unsigned long lastSecond = 0;
void loop()
{
  unsigned long thisSecond = millis() / 800; // Speedometer display update rate
  if (lastSecond != thisSecond)

    speedometerDisplay();
  lastSecond = thisSecond;

  ////////
  ///  light = analogRead (A0);       //Backlight control via ldr

  //Serial.println(light);

  ///   analogWrite(blueBack, 255 - light / 4);
  ///////

  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = analogRead(inputPin);
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;

  // if we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;


    // calculate the average:
    average = total / numReadings;

    // send it as ASCII digits
    lcd.setCursor(10, 1);
    avgtemp = (float(average)) * 5 / (1023) / 0.01 - 1.2;  // minus calibration
    lcd.print("C");
    lcd.print((char)223);
    lcd.println(avgtemp);
  }
}

Does anyone know why this
}
if (pressLength_ms >= 1000) {
SavedkMs = EEPROM.readFloat(0);
kilometer = SavedkMs;
Serial.println ("end");
Serial.println (kilometer, 3);
}
doesn't make the display show the saved kms? It appears in the serial but it doensn't in the display...

#define LCD_WIDTH 16  //Number of  Display columns (16)
#define LCD_HEIGHT 2  //Number of Display lines (2)
#define REEDPIN 2  // Pin for hall sensor, digital 2 for Interrupt 0
#define REEDINTERRUPT 0  // Hardware interrupt for the Hall-Pin
#define WHEEL 1540  // Wheel size in mm

#include <EEPROMex.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 6);

byte blueBack = 9;
byte interval = 50;
const int numReadings = 200;
float avgtemp = 0;              // the average
float kph;
float kilometer;
float MaxVel;
int light = 0;
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;
int inputPin = A3;
unsigned long previousMillis = 0;

//////////////////////////////

unsigned int pressLength_ms = 0;
unsigned long LastPress = 0;
float SavedkMs;

/////////////////////////////////////////////

void setup() {

  Serial.begin(9600);
  EEPROM.setMaxAllowedWrites(25);
  analogWrite(blueBack, 255);
  lcd.begin(LCD_WIDTH, LCD_HEIGHT);
  lcd.print("By Caio M. Poit");
  pinMode(REEDPIN, INPUT_PULLUP); //connect hall sensor without resistance
  attachInterrupt(REEDINTERRUPT, reedISR, CHANGE);
  lcd.clear();
  MaxVel = 0;

  // Keep in mind, when pin 7 has ground voltage applied, we know the button is being pressed
  pinMode(7, INPUT_PULLUP); // Initialize the pushbutton pin as an input pullup
  delay(500);
}

volatile byte reedCountSum;
volatile long reedMillisSum;
unsigned long lastReedMillis;
void reedISR()

{
  if (millis() - lastReedMillis >= 25) //25 ms corresponds to max. 40 wheel revolutions per second
  {
    reedCountSum++;                 // Count one revolution
    reedMillisSum += millis() - lastReedMillis; // add time
    lastReedMillis = millis();     // Remember time
  }
}
unsigned long totalRevolutions;

void speedometerDisplay()
{
  byte revolutions;
  unsigned long TIME;

  char buffer[10];
  noInterrupts();            // Disable interrupts
  revolutions = reedCountSum; // Copy count variable
  reedCountSum = 0;        // Reset count variable to 0
  TIME = reedMillisSum;    // Copy time counter
  reedMillisSum = 0;       // Reset timer to 0
  interrupts();              // Allow interrupts again
  totalRevolutions += revolutions; // Summing all wheel revolutions
  kilometer = (float)totalRevolutions * (float)WHEEL / 1000000.0; // Bicycle Odometer
  if (revolutions > 0)
    kph = float(WHEEL) * (float)revolutions / (float)TIME * 3.6;
  else
    kph = 0.0;
  lcd.setCursor(0, 0);
 // dtostrf(kilometer, 5, 3, buffer);
  lcd.print(kilometer,3);
  lcd.print(buffer);
  lcd.print(" km");
  lcd.setCursor(0, 1);
  lcd.print("kM/h ");
  dtostrf(kph, 4, 1, buffer);
  lcd.print(buffer);

  if (kph > MaxVel) {
    MaxVel = kph;
    lcd.setCursor (8, 0);
    lcd.print ("|TS:");
    lcd.setCursor (12, 0);
    lcd.print(MaxVel);
  }
}
unsigned long lastSecond = 0;
void loop() {
  ///////////////////////////////////////////////////////////////////
  while (digitalRead(7) == LOW ) {
    pressLength_ms = millis() - LastPress;
    Serial.print("ms = ");
    Serial.println(pressLength_ms);
  }//close while

  if (pressLength_ms >= 100 && pressLength_ms < 1000) {
    EEPROM.updateFloat(0, kilometer);
    Serial.println ("done");
  }
  
  if (pressLength_ms >= 1000) {
    SavedkMs = EEPROM.readFloat(0);
    kilometer = SavedkMs;
    Serial.println ("end");
    Serial.println (kilometer, 3);
  }

  LastPress = millis();
  pressLength_ms = 0;


  //////////////////////////////////////////////////////////////
  unsigned long thisSecond = millis() / 800; // Speedometer display update rate
  if (lastSecond != thisSecond)

    speedometerDisplay();
  lastSecond = thisSecond;


  light = analogRead (A0);       //Backlight control via ldr

  //  Serial.println(255 - light / 4);

  analogWrite(blueBack, 255 - light / 4);

  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    {
      {
        // subtract the last reading:
        total = total - readings[readIndex];
        // read from the sensor:
        readings[readIndex] = analogRead(inputPin);
        // add the reading to the total:
        total = total + readings[readIndex];
        // advance to the next position in the array:
        readIndex = readIndex + 1;

        // if we're at the end of the array...
        if (readIndex >= numReadings) {
          // ...wrap around to the beginning:
          readIndex = 0;
        }

        // calculate the average:
        average = total / numReadings;

        // send it as ASCII digits
        lcd.setCursor(10, 1);
        avgtemp = (float(average)) * 5 / (1023) / 0.01;  // minus calibration
        lcd.print("C");
        lcd.print((char)223);
        lcd.println(avgtemp);
      }
    }
  }
}

Maybe you have to use lcd.print? :confused:

caiopoit:
I'm going to test this tomorrow, but one more thing that I suspect that is going to be more challenging is how can we also show the average speed, the time riding and distance traveled? Is that possible? Or there is no enough memory for it? I plan to do that with a Nano... Also if possible use the sensor in a analog pin since the lcd uses several digital pins...

Average speed over what period?
Time riding and distance travelled from where/when?

Sensor in the analog pin is no problem, but you will need to use a pull-up or pull-down resistor, depending on the details of the sensor.

Maybe you have to use lcd.print? :confused:

I'm doing that.
Here:

void speedometerDisplay()
{
  byte revolutions;
  unsigned long TIME;

  char buffer[10];
  noInterrupts();            // Disable interrupts
  revolutions = reedCountSum; // Copy count variable
  reedCountSum = 0;        // Reset count variable to 0
  TIME = reedMillisSum;    // Copy time counter
  reedMillisSum = 0;       // Reset timer to 0
  interrupts();              // Allow interrupts again
  totalRevolutions += revolutions; // Summing all wheel revolutions
  kilometer = (float)totalRevolutions * (float)WHEEL / 1000000.0; // Bicycle Odometer
  if (revolutions > 0)
    kph = float(WHEEL) * (float)revolutions / (float)TIME * 3.6;
  else
    kph = 0.0;
  lcd.setCursor(0, 0);
 // dtostrf(kilometer, 5, 3, buffer);
  lcd.print(kilometer,3);
  lcd.print(buffer);
  lcd.print(" km");
  lcd.setCursor(0, 1);
  lcd.print("kM/h ");
  dtostrf(kph, 4, 1, buffer);
  lcd.print(buffer);

Average speed over what period?
Time riding and distance travelled from where/when?

Sensor in the analog pin is no problem, but you will need to use a pull-up or pull-down resistor, depending on the details of the sensor.

I won't need the sensor on analog pin. But the average speed would be over the total time riding...
And also a way to save it to the EEPROM like I'm trying to do with the run kms... So I can stop and return to where I was.

The value of kilometer printed in speedometerDisplay() is kilometer = (float)totalRevolutions * (float)WHEEL / 1000000.0; // Bicycle Odometer

It is not kilometer = SavedkMs;

speedometerDisplay() is called after the eeprom read, and you are overwriting the stored value.