#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
#define encoder0PinA 2
#define encoder0PinB 3
volatile unsigned int encoder0Pos = 0;
// 400ppr encoder with 300mm length wheel so 4 ticks == 3 mm -> 3 / 4 = 0.75
// dividing by 100 to get decimeters or by 1000 to get meters
#define DECIMETERS_PER_TICK 0.75 / 1000
long newposition;
long oldposition = 0;
unsigned long newtime;
unsigned long oldtime = 0;
long vel;
int first;
int distance;
void setup() {
lcd.begin (20, 4);
lcd.setCursor(0,0);
lcd.print ("SPEED");
lcd.setCursor(17,0);
lcd.print ("M/H");
lcd.setCursor(0,1);
lcd.print ("DISTANCE");
lcd.setCursor(19,1);
lcd.print ("M");
lcd.setCursor(0,2);
lcd.print ("INPUT SPEED");
lcd.setCursor(17,2);
lcd.print ("M/H");
lcd.setCursor(0,3);
lcd.print ("TIME");
lcd.setCursor(11,3);
lcd.print ("STOP");
pinMode(encoder0PinA, INPUT_PULLUP);//input pin with internal pull-up resistor
pinMode(encoder0PinB, INPUT_PULLUP); //input pin with internal pull-up resistor
// encoder pin on interrupt 0 (pin 2)
attachInterrupt(0, doEncoderA, CHANGE);
// encoder pin on interrupt 1 (pin 3)
attachInterrupt(1, doEncoderB, CHANGE);
}
void loop () {
distance = encoder0Pos * DECIMETERS_PER_TICK/40;
newposition = encoder0Pos;
newtime = millis();
vel =(newposition-oldposition) * 10/(newtime-oldtime);
lcd.setCursor(13,0);
lcd.print(vel);
if(vel<1000){ lcd.print(" ");
delay (100);}
oldposition = newposition;
oldtime = newtime;
delay(250);
lcd.setCursor(13,1);
lcd.print(abs(distance));
if(distance<10000){ lcd.print(" ");}
}
void doEncoderA() {
// look for a low-to-high on channel A
if (digitalRead(encoder0PinA) == HIGH) {
// check channel B to see which way encoder is turning
if (digitalRead(encoder0PinB) == LOW) {
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
else // must be a high-to-low edge on channel A
{
// check channel B to see which way encoder is turning
if (digitalRead(encoder0PinB) == HIGH) {
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
//Serial.println (encoder0Pos, DEC);
// use for debugging - remember to comment out
}
void doEncoderB() {
// look for a low-to-high on channel B
if (digitalRead(encoder0PinB) == HIGH) {
// check channel A to see which way encoder is turning
if (digitalRead(encoder0PinA) == HIGH) {
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
// Look for a high-to-low on channel B
else {
// check channel B to see which way encoder is turning
if (digitalRead(encoder0PinA) == LOW) {
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
}
trying to make a measuring wheel with a rotary encoder lpd3806, which will display the distance (meters) and the speed(meters per hour). am i doing the calculations correctly ?
i am getting results, but when i turn it clockwise the distance after 10 goes again back to 0
and the speed shows good but after some time it prints big numbers
i want to make it so it will work 0-1000 meters but no under 0 or negative, and from 1000-0
counter clock wise but stop at 0, stop at zero means even if it turns it will not display anything but zero
By 'length' did you mean the length around the outside (a.k.a. CIRCUMFERENCE)? A wheel with a 300 mm circumference would have a diameter of about 9.55 cm.
You say: dividing by 100 to get decimeters or by 1000 to get meters
but then you calculate: #define DECIMETERS_PER_TICK 0.75 / 1000
Shouldn't that be METERS_PER_TICK?
Note: If you put your calculation in a macro (like DECIMETERS_PER_TICK) you should put it in parentheses to avoid confusing the expression parser: #define DECIMETERS_PER_TICK (0.75 / 1000)
Better still, use a 'const' variable so the compiler is sure of the data type: const float DECIMETERS_PER_TICK = 0.75 / 1000;
Are you using an Arduino with 16-bit integers? If so, this counter is only good for a little over 49 meters. You should change it to 'unsigned long' to get your 1000 meters.
Because 'encoder0Pos' is 'volatile' it should be fetched only with interrupts disabled.
If you make changes and want further help, please post a new reply with the latest version of your sketch. That way we can tell which of the suggested changes you have made.
No need to change it to RPM first if you want meters per hour.
If your 'position' values are in meters and your 'time' values are in milliseconds you are calculating 'val' in meters per millisecond. To get meters per hour, multiply by milliseconds per hour.
60 minutes per hour
6060 (3600) seconds per hour
36001000 (3600000) milliseconds per hour
3.6 million is too large for a 16-bit 'int' so use a 32-bit 'long int'.
This should be: volatile unsigned long encoder0Pos = 0;
It is changed in the interrupts and used in loop().
This should be: #define METERS_PER_TICK (0.75 / 1000)
This makes it act more like a float constant when substituting into an expression.
attachInterrupt() should always use digitalPinToInterrupt():
// encoder pin on pin 2
attachInterrupt(digitalPinToInterrupt(2), doEncoderA, CHANGE);
// encoder pin on pin 3
attachInterrupt(digitalPinToInterrupt(3), doEncoderB, CHANGE);
Oops. I forgot that the 'position' variables are in ticks, not in meters. This should be: vel =((newposition - oldposition) * METERS_PER_TICK) / (newtime-oldtime);
This is going to be 0 for any velocity below 3.6 million meters per hour. Try making 'vel' a float value: float vel;