Encoder Output

I am trying to get the Arduino to read two encoders. I have edited the code from this website, but I don't get an output. Help please. Thanks.
http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino

//Encoder_1
#define ENC_A 14
#define ENC_B 15
#define ENC_PORT PINC

//Encoder_2
#define ENC_C 8
#define ENC_D 9
#define ENC_PORT PINB

void setup()
{
  /* Setup encoder pins as inputs */
  pinMode(ENC_A, INPUT);
  digitalWrite(ENC_A, HIGH);
  pinMode(ENC_B, INPUT);
  digitalWrite(ENC_B, HIGH);

  pinMode(ENC_C, INPUT);
  digitalWrite(ENC_C, HIGH);
  pinMode(ENC_D, INPUT);
  digitalWrite(ENC_D, HIGH);

  Serial.begin (115200);
  Serial.println("Start");
}

void loop()


{
  //Encoder 1
  static uint8_t counter_1 = 0;      //this variable will be changed by encoder input

  int8_t tmpdata;
  
  int count_1 = 0;

  tmpdata = read_encoder();
  
  if( tmpdata ) {
    if( tmpdata == 1 ) {
      count_1++;
    }
    if (count_1 == 4){
      Serial.println("A");
      count_1 = 0;
    }

    if( tmpdata == -1 ) {
      count_1--;
    }
    if (count_1 == -4){
      Serial.println("B");
      count_1 = 0;
    }
  }

  //Encode 2
  static uint8_t counter_2 = 0;      //this variable will be changed by encoder input

  int count_2 = 0;

  tmpdata = read_encoder();
  if( tmpdata ) {
    if( tmpdata == 1 ) {
      count_2++;
    }
    if (count_2 == 4){
      Serial.println("C");
      count_2 = 0;
    }

    if( tmpdata == -1 ) {
      count_2--;
    }
    if (count_2 == -4){
      Serial.println("D");
      count_2 = 0;
    }
  }

}


/* returns change in encoder state (-1,0,1) */
int8_t read_encoder()
{
  static int8_t enc_states[] = {
    0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0      };
  static uint8_t old_AB = 0;

  old_AB <<= 2;                   //remember previous state
  old_AB |= ( ENC_PORT & 0x03 );  //add current state

  return ( enc_states[( old_AB & 0x0f )]);
}

The example relies on both encoder pins being on the same underlying 8 bit port, not only that but bits 0 and 1 of that port. So it is dependent on the pins.

Adding another controller may not work as it is, because the pins may not be pins 0 and 1 of a port. I haven't checked.

Find two pins 0 and 1 on a different port and modify the sketch.

For a different version, have a look at the sketch for project 14 here: http://www.arduinoevilgenius.com/downloads

Its probably more adaptable to multiple rotary encoders.

It appears the link for the encoder example is missing.

Download the entire zip file for all the projects.

You will find it in project 14.

Ok, got the code. I have a few questions. I have added this to the different States to get an output when it changes. I am not using leds.

setLights(HIGH, HIGH, LOW);
      targetCount = shortPeriod;
      state = 2;
      Serial.println("State 1");

But it just prints "State 1, State 2, State 3" over and over. I thought this code changes leds when the encoder is turned?

I just need it to do one thing... Print out "A" when it increases and "B" when it decreases. Oh, and I need two encoders. Ugh. :slight_smile:

Probably easiest if you post the whole sketch.

// Project 11

int redPin = 2;
int yellowPin = 3;
int greenPin = 4;
int aPin = 6;
int bPin = 7;
int buttonPin = 5;

int state = 0;
int longPeriod = 50; // Time at green or red /5000
int shortPeriod = 70; // Time period when changing 
int targetCount = shortPeriod;
int count = 0;

void setup()
{
  pinMode(aPin, INPUT);
  pinMode(bPin, INPUT);
  pinMode(buttonPin, INPUT);
  pinMode(redPin, OUTPUT);
  pinMode(yellowPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  
  Serial.begin (115200);
}

void loop()
{
  count++;
  if (digitalRead(buttonPin))
  {
    setLights(HIGH, HIGH, HIGH);
  }
  else
  {
    int change = getEncoderTurn();
    int newPeriod = longPeriod + (change * 1000);   
    if (newPeriod >= 1000 && newPeriod <= 10000)
    {
      longPeriod = newPeriod; 
    }
    if (count > targetCount)
    {
      setState();
      count = 0;
    }
  }
  delay(1);
}

int getEncoderTurn()
{
  // return -1, 0, or +1
  static int oldA = LOW;
  static int oldB = LOW;
  int result = 0;
  int newA = digitalRead(aPin);
  int newB = digitalRead(bPin);
  if (newA != oldA || newB != oldB)
  {
    // something has changed
    if (oldA == LOW && newA == HIGH)
    {
      result = -(oldB * 2 - 1);
    }
  }
  oldA = newA;
  oldB = newB;
  return result;
} 

int setState()
  {
    if (state == 0)
    {
      setLights(HIGH, LOW, LOW);
      targetCount = longPeriod;
      state = 1;
    }
    else if (state == 1)
    {
      setLights(HIGH, HIGH, LOW);
      targetCount = shortPeriod;
      state = 2;
      Serial.println("State 1");
    }
    else if (state == 2)
    {
      setLights(LOW, LOW, HIGH);
      targetCount = longPeriod;
      state = 3;
      Serial.println("State 2");
    }
    else if (state == 3)
    {
      setLights(LOW, HIGH, LOW);
      targetCount = shortPeriod;
      state = 0;
      Serial.println("State 3");
    }
  }

void setLights(int red, int yellow, int green)
{
  digitalWrite(redPin, red);
  digitalWrite(yellowPin, yellow);
  digitalWrite(greenPin, green);
}

I don't know. Without really taking your code apart, I don't know what the problem is, and to be honest it would probably be quicker to write it for you. But sorry, I don't have the time.

Throw it away and start with a simple sketch with one rotary encoder that just uses getEncoderTurn() and have it report -1, 0, or 1 in the Serial monitor, then build it up from there, a tiny step at a time. And when it breaks go back and sort it out before adding to it.

Some hints.

Because this approach does not use interrupts, you have to keep the loop as fast as possible, on the final system, no delay() and no Serial.print. This will become apparent as some rotating events being missed as you turn the knob. But its not a problem while you are getting it working.

You will need to parameterise getEncoderTurn to take aPin and bPin as arguments. This can be one step once you have got it working with one encoder. Then add another and see how it goes.

Proceed logically in small steps and you will be fine.

Si, this is your code from your book. I just simply added a line to print to the serial the state it is in.

I have tried writing my own code, but I can't quit figure out this encoder business. Frustrating. I was hoping I could modify your code (or any code) to support another encoder.

Thanks for your time.

Si, this is your code from your book.

How embarrasing. But thats the code for project 11 not project 14.

But anyway, don't give up yet! have you got your encoder connected up ok? Did you remember the pull-up resistors? Or use internal ones.

Get this going first:

int aPin = 6;
int bPin = 7;

void setup()
{
  pinMode(aPin, INPUT);
  pinMode(bPin, INPUT);
  Serial.begin(9600);
}

void loop()
{
  Serial.print(getEncoderTurn());
}


int getEncoderTurn()
{
  // return -1, 0, or +1
  static int oldA = LOW;
  static int oldB = LOW;
  int result = 0;
  int newA = digitalRead(aPin);
  int newB = digitalRead(bPin);
  if (newA != oldA || newB != oldB)
  {
    // something has changed
    if (oldA == LOW && newA == HIGH)
    {
      result = -(oldB * 2 - 1);
    }
  }
  oldA = newA;
  oldB = newB;
  return result;
}

If that doesn't work, then your hardware is probably not wired up right.

When you move onto 2 controllers, you will need to modify getEncoderTurn quite a bit, OR just have 2 versions of it getEncoderTurn1 and getEncoderTurn2, because the function holds the state of the controller in static methods, so it would be confused by two rotary controllers.

Thanks Si,

I have seen that bit of code on a few examples. I can't follow it 100%, but I do know it returns 1,0, or -1. I think I can change it to spit out what I need.

Unfortunately, the code you post only spits out 0's so it must be my hardware. I have the encoders going straight from pin 6 and 7 to the encoder. And the middle pin of the encoder going to ground. I am guessing this is incorrect. What is the proper way to wire this up? Thanks.

I have the encoders going straight from pin 6 and 7 to the encoder. And the middle pin of the encoder going to ground.

That will work but only if you either wire external pull-up resistors from pins 6 & 7 to +5vdc, or enable the internal pull-ups for both those pins.

Lefty

Thanks Lefty. I ended up reading a bit and learning to use digitalWrite pin High.

And thanks Si. That bit of code was key.

No problem, glad you got it going :slight_smile:

Hey Si,

Maybe you have some insight into a little bit of craziness. I have everything working, but not well. Here are the few things I can see going wrong.

  1. This is the output, as viewed in serial monitor, of turning the encoder in one direction. "BABBAABABBBBAABABAABBAABBAA" Keep in mind, it prints A or B depending on the direction.
  2. When I view the output from the serial monitor in Arduino, I get a pretty constant output (even if the output seems pretty random). When Processing prints the values, I seem to lose most of my values. For example, I turned the encoder in 1 direction 20 times. It displays only 12 of them. And again, not always in the right direction.
  3. It seems like 90% of time, the encoders only go in one directions. Regardless of which way I turn them.

Arduino:

int Sel_1 = 5; //Select 1
int aPin = 6;
int bPin = 7;

int cPin = 8;
int dPin = 9;
int Sel_2 = 10;

void setup()
{
  pinMode(aPin, INPUT);
  digitalWrite(aPin, HIGH);
  pinMode(bPin, INPUT);
  digitalWrite(bPin, HIGH);
  pinMode(cPin, INPUT);
  digitalWrite(cPin, HIGH);
  pinMode(dPin, INPUT);
  digitalWrite(dPin, HIGH);

  pinMode(Sel_1, INPUT);
  pinMode(Sel_2, INPUT);

  Serial.begin(115200);
}

void loop()
{
  Serial.print(getEncoderTurn_1() );
  delay(20);
  Serial.print(getEncoderTurn_2() );
  delay(20);
  
  Serial.print(getSwitch_1() );
  delay(6);
  Serial.print(getSwitch_2() );
  delay(6);

}

char getEncoderTurn_1()
{
  // return -1, 0, or +1
  static int oldA = LOW;
  static int oldB = LOW;
  int result = 0;
  char change;
  int newA = digitalRead(aPin);
  int newB = digitalRead(bPin);
  if (newA != oldA || newB != oldB)
  {
    // something has changed
    if (oldA == LOW && newA == HIGH)
    {
      result = -(oldB * 2 - 1);
    }
  }
  oldA = newA;
  oldB = newB;

  if ( result >= 1){
    change = 'A';
  } 
  else if ( result < 0){
    change = 'B';
  }

  return change;
} 

char getEncoderTurn_2()
{
  // return -1, 0, or +1
  static int oldA = LOW;
  static int oldB = LOW;
  int result = 0;
  char change;
  int newA = digitalRead(cPin);
  int newB = digitalRead(dPin);
  if (newA != oldA || newB != oldB)
  {
    // something has changed
    if (oldA == LOW && newA == HIGH)
    {
      result = -(oldB * 2 - 1);
    }
  }
  oldA = newA;
  oldB = newB;

  if ( result >= 1){
    change = 'C';
  } 
  else if ( result < 0){
    change = 'D';
  }

  return change;
} 


char getSwitch_1()
{
  char sw;
  int read_sw = digitalRead(Sel_1);
  if (read_sw == HIGH){
    sw = 'M';
  }
  else {
    sw = 'm'; //Just to see what it's sending.
  }

  return sw;
}

char getSwitch_2()
{
  char sw;
  int read_sw = digitalRead(Sel_2);
  if (read_sw == HIGH){
    sw = 'N';
  }
  else {
    sw = 'n'; //Just to see what it's sending.
  }

  return sw;
}

Processing:

import processing.serial.*;

Serial myPort;

char inByte;

int d_size = 4;

int x_max = 800;
int y_max = 600;

float start_x = random(0,x_max);
float start_y = random(0,y_max);

int x_val = int(start_x);
int y_val = int(start_y);

void setup() {

  size(x_max, y_max);
  background(100);

  println(Serial.list());
  myPort = new Serial(this, Serial.list()[0], 115200);
}

void draw() {
  stroke(0);
  strokeWeight(5);
  noCursor();

  while (myPort.available() > 0) {
    inByte = myPort.readChar();
   // print("Byte in: " + inByte);
  }

  if(inByte == 'A') {
   // print("Byte: " + inByte + " ");
    x_val = x_val + d_size;
  }
  else if(inByte == 'B') {
   // print("Byte: " + inByte + " ");
    x_val = x_val - d_size;
  }
  else if(inByte == 'C') {
   // print("Byte: " + inByte + " ");
    y_val = y_val + d_size;
  }
  else if(inByte == 'D') {
   // print("Byte: " + inByte + " ");
    y_val = y_val + d_size;
  }
  else if(inByte == 'M') {
   // print("Save a file");
    String timestamp = "File" + year() + "_" + month() + "_" + day() + "_" + hour() + "_" + minute() + "_";
    save( timestamp );
  }
  else if(inByte == 'N') {
  //  print("Reset");
    float start_x = random(0,x_max);
    float start_y = random(0,y_max);

    int x_val = int(start_x);
    int y_val = int(start_y);
    draw();
  }

  println("X_Val: " + x_val + " ");
  println("Y_Val: " + y_val + " ");

  point(x_val, y_val);
}

It seems like 90% of time, the encoders only go in one directions. Regardless of which way I turn them.

Thats because you have Serial.prints and delays in the loop. It slows it down so it skips transitions. I bet if you turn the knobs really slowly its ok.

I didn't realise the Serial.print was any more than trace. If you want to capture the turns and send them to Processing, then you need to buffer the events and send them to Processing in batches, when no knob turning is going on.

Is that possible? What is the end goal of your project? Etch-a-sketch?

Yes, the end project is an Etch-a-sketch. It's for an art project. I should mention, I am not an art major. I'm an electrical engineer who's not very good at coding.

I've changed my void loop to look like this

void loop()
{
  for(int i = 0 ; i < 12 ; i = i+4){
    d_send[i] = getEncoderTurn_1();
    d_send[i+1] = getEncoderTurn_2();
    d_send[i+2] = getSwitch_1();
    d_send[i+3] = getSwitch_2();
  }

  //Serial.print(d_send);
  for (int i = 0; i < 12; i++){
    Serial.print(d_send[1]);
  }

}

And added this variable:

char d_send[12];

But it seems to have a larger problem. When I open the serial monitor, it freezes the serial monitor. When I run the processing code, I just get blanks and then it freezes it too. Ha.

Also, before I changed the code, I noticed it would just keep sending m and n. These were dummy values I created to see what the Arduino was sending when I wasn't clicking the button. It should never be sending m and n. It should only send M or N depending which switch it pressed. This is part of the code I posted earlier. Thanks again for the help.

It wouldn't be as good, but I have to say pots as potential divers on two analog inputs wouldn't have all these problems.

Worth considering.

You lost me Si. I don't have anything hooked up to the analogue inputs.

Imagiro1, all I meant was that you could use potentiometers (variable resistors) instead of the rotary encoders.

With one end connected to +V the other to GND and the slider to an analog in, you can read a value between 0 and 1023 for the position of the knob using analogRead(pin).