Encoders, and LCD's, and decimals... I need some guidence!

Hello fellow arduino'ers,

I've been playing with Arduinos for a few years now however my playing has been more or less limited to lighting and motion sensing. I have now dove head first into a considerably more complicated world that admittedly is beyond my current technical knowledge.

I suppose where I should start is by explaining what I am trying to accomplish. I have a rotary encoder (20ppr) on a pulley. Pulling a belt over the pulley will cause the the pulley to rotate and thus rotate the encoder. The thought is if I know the circumference of the pulley I should be able to convert the encoder pulses into a length of belt that has been pulled over the pulley. Then this information can be fed to an LCD in a feet and decimal readout.

I have been playing with some code that I found trying to get my head wrapped around all of this, and while I have successfully been able to get a readout to my LCD which correlates to the encoder pulses I am unable to figure out how to get it to read out less than 1. Given the circumference of my pulley each pulse should equal .157, so clearly reading 1 per pulse is not going to work out so well. I have spent several hours searching for information on this topic but I have not found any explanations. I would greatly appreciate if somebody could clarify this for me. Below is my sketch, I am sure I have messed all sorts of stuff in here, like I said I am stepping WAY outside my comfort zone here.

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3f,20,4);  // set the LCD 
//address to 0x3f for a 16 chars and 2 line display
const int clkPin= 2; //the clk attach to pin2
const int dtPin= 3; //the dt attach to pin3
const int swPin= 4 ;//the number of the button
int encoderVal = 0.157;

void setup()
{ 
  lcd.init();  // initialize the lcd
  // Print a message to the LCD.
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Belt Length :");
  //set clkPin,dePin,swPin as INPUT
  pinMode(clkPin, INPUT);
  pinMode(dtPin, INPUT);
  pinMode(swPin, INPUT);
  digitalWrite(swPin, HIGH);
  Serial.begin(9600); // initialize serial communications at 9600 bps
}

void loop()
{
  int change = getEncoderTurn();
  encoderVal = encoderVal + change;
  if(digitalRead(swPin) == LOW)//if button pull down
  {
    encoderVal = 0.157;
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Belt Length :");
  }
  Serial.println(encoderVal); //print the encoderVal on the serial monitor
  lcd.setCursor(5,1);
  lcd.print(encoderVal);
}

int getEncoderTurn(void)
{
  static int oldA = HIGH; //set the oldA as HIGH
  static int oldB = HIGH; //set the oldB as HIGH
  int result = 0.078;
  int newA = digitalRead(dtPin);//read the value of clkPin to newA
  int newB = digitalRead(clkPin);//read the value of dtPin to newB
  if (newA != oldA || newB != oldB) //if the value of clkPin or the dtPin has changed
  {
    // something has changed
    if (oldA == HIGH && newA == LOW)
    {
      result = (oldB * 2 - 1);
    }
  }
  oldA = newA;
  oldB = newB;
  return result;
}
int encoderVal = 0.157;

Try:

float encoderVal = 0.157;

Given the circumference of my pulley each pulse should equal .157

0.157 what? Hamburgers per fortnight?

so clearly reading 1 per pulse is not going to work out so well.

Well, you can't read less than one pulse, so trying to isn't going to work too well, either.

outsider:

int encoderVal = 0.157;

Try:

float encoderVal = 0.157;

Awesome progress thank you for that it now is displaying 1.16 however it is still counting by 1 when the encoder is rotated. So it goes from 1.16 to 2.16 ect... Can you give me an explanation of what the float command is? I greatly appreciate the help but rather than plugging in a command and saying yay it works I'd like to know why. Thank you again so very much!

PaulS:
0.157 what? Hamburgers per fortnight?
Well, you can't read less than one pulse, so trying to isn't going to work too well, either.

Sorry I should have clarified .157 inches, and reading the quoted statement yes reading less than one pulse doesn't work either. What I meant and my apologies for not being clear displaying 1 per pulse doesn't work well, I need to display .157 inches per pulse.

So, you want to multiply the number of encoder ticks by 0.157, not add the number of encoder ticks to 0.157. No?

I would like each encoder pulse to add .157, so I would like my display to read out .157, .314, .471 ect

As I look at the sketch it looks to me if I change result = (oldB * 2 - 1); to result = (oldB * 2 - 0.843); it would then increase my count by the desired .157 per pulse, however this is not the results I am getting. When I make this change the count still goes up by 1 when the encoder is turned clockwise. When the encoder is turned counter clockwise the count does nothing. What am I missing here, this seems like the solution should be fairly straight forward.

Thank you again for your help everybody!

As I look at the sketch it looks to me if I change result = (oldB * 2 - 1); to result = (oldB * 2 - 0.843); it would then increase my count by the desired .157 per pulse, however this is not the results I am getting. When I make this change the count still goes up by 1 when the encoder is turned clockwise. When the encoder is turned counter clockwise the count does nothing. What am I missing here, this seems like the solution should be fairly straight forward.

You are getting messed up by your data types, and trying to work with integers and floats at the same time. Some of your integer values are getting truncated and not coming out as you think they should.

So, you want to multiply the number of encoder ticks by 0.157, not add the number of encoder ticks to 0.157. No?

As PaulS suggested, I would recommend separating out the reading of the encoder and determining the counts, from calculating the belt length.

Determine the encoder counts. They change by +/- 1.

Set up a new float variable say beltFeet = encoderVal*.157 and display that.

I'm not clear that your encoder reading algorithm is correct for 20 ppr. You may find out that when you get the sketch correct for the .157 ft per pulse you may not get the right answer for belt length. The encoder reading function can be changed if there is a problem.

Cattledog,

Thank you for that input, it makes absolute sense. I am not entirely sure how to implement your suggestion so it appears I need to do some learning about floating variables and how to correctly implement them.

I came to .157 using the following... My pulley has a circumference of 3.1385" if my encoder is 20PPR then I need to divide 3.1385 by 20 which gives me .157 per pulse.

I understand now why my thought of using .157" might be off, or more realistically will be off. So now begs the question if my variable us virtually unknown as the belt could be any length how do I relate a virtually infinitely variable to 1023? Seems like every time I sort of figure out an answer 5 more questions come up. I am still trying to wrap my brain around using the float to reference .157 (Or whatever it actually should be) in relation to PPR on the encoder.

I am still trying to wrap my brain around using the float to reference .157 (Or whatever it actually should be) in relation to PPR on the encoder.

Try this

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3f,20,4);  // set the LCD 
//address to 0x3f for a 16 chars and 2 line display
const int clkPin= 2; //the clk attach to pin2
const int dtPin= 3; //the dt attach to pin3
const int swPin= 4 ;//the number of the button
int encoderVal = 0;//raw counts from encoder
const float distancePerCount = .157; //adjust as required
float totalDistance = 0.0;

void setup()
{ 
  lcd.init();  // initialize the lcd
  // Print a message to the LCD.
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Belt Length :");
  //set clkPin,dePin,swPin as INPUT
  pinMode(clkPin, INPUT);
  pinMode(dtPin, INPUT);
  pinMode(swPin, INPUT);
  digitalWrite(swPin, HIGH);
  Serial.begin(9600); // initialize serial communications at 9600 bps
}

void loop()
{
  int change = getEncoderTurn();
  encoderVal = encoderVal + change;
  totalDistance = encoderVal*distancePerCount;
  
  if(digitalRead(swPin) == LOW)//if button pull down
  {
    //encoderVal = 0.157;
    encoderVal = 0;
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Belt Length :");
  }
  Serial.println(encoderVal); //print the encoderVal on the serial monitor
  Serial.println(totalDistance,3);//print float with 3 dp
  lcd.setCursor(5,1);
  //lcd.print(encoderVal);
  lcd.print(totalDistance,3);
}

int getEncoderTurn(void)
{
  static int oldA = HIGH; //set the oldA as HIGH
  static int oldB = HIGH; //set the oldB as HIGH
  int result = 0.078;
  int newA = digitalRead(dtPin);//read the value of clkPin to newA
  int newB = digitalRead(clkPin);//read the value of dtPin to newB
  if (newA != oldA || newB != oldB) //if the value of clkPin or the dtPin has changed
  {
    // something has changed
    if (oldA == HIGH && newA == LOW)
    {
      result = (oldB * 2 - 1);
    }
  }
  oldA = newA;
  oldB = newB;
  return result;
}

If using a cog belt, you need to know the number of cogs per rev and the distance between cogs, try this:
Put the belt around the pulley and put a pencil mark on the belt at the 12 O'Clock position on the pulley, zero the counter and start pulling the belt over the pulley. When the counter gets to 100, hold it still and put another mark on the belt, again at 12 O'Clock. Measure between the marks, close as you can, then divide by 100, that should give you distance per pulse.

Cattledog,

Thank you so much for that code, I will be going through it this week so I can see and learn what I was doing wrong. I have a lot to learn about this stuff like I mentioned previously up until now my experience has been quite basic using LED's and motion sensors.

Outsider,

You're absolutely correct what I thought was accurate was in fact not, it's close but not close enough. I'll give your suggestion a shot and see if that gets me close, thank you for the information!