Rotary encoder and long wires

Projects that are not for RC, but are cool and worthy of development.
Post Reply
User avatar
jhsa
Posts: 19480
Joined: Tue Dec 27, 2011 5:13 pm
Country: Germany

Rotary encoder and long wires

Post by jhsa »

Hi guys, I am working on a little project. It uses V-USB. It is a USB pedal that controls the audio player on my computer..
I have run into a problem and maybe someone here can help me ;)
I have a rotary encoder set to increase and decrease the track number, and immediately pause the player by sending a key stroke like a keyboard (Space)
If I connect the rotary encoder next to the arduino, short wires, it works well. But i wan't to place it about 1.5 meters away from the board. It doesn't work very well. It takes longer to change the tracks, when it changes them. Sometimes only pauses without moving to the next/previous track. Sometimes it moves without pausing. So, clearly there is some noise or some other problem caused by the longer cable.
Here is the code I'm using: I have modified it to suit my needs. I'm not a programmer as you know ;) I wonder if there is a simple hardware solution to overcome this problem.. Capacitors didn't work and actually made the problem worse..

Thanks in advance for your help..

João

Code: Select all

#include <ProTrinketHidCombo.h>


#define PIN_ENCODER_A      3
#define PIN_ENCODER_B      5
#define TRINKET_PINx       PIND
//#define TRINKET_PIN_BUTTONS    PINB  //This also doesn't work. I can only use "D"  :-(
#define PIN_ENCODER_SWITCH 4
#define PIN_PREVIOUS 1
#define PIN_NEXT 0
#define PIN_PLAY 6
//#define PIN_STOP 10

int LedPin = 13;


static uint8_t enc_prev_pos   = 0;
static uint8_t enc_flags      = 0;
static char    sw_was_pressed = 0;
static char    PREVIOUS_was_pressed = 0;
static char    NEXT_was_pressed = 0;
static char    PLAY_was_pressed = 0;
//static char    STOP_was_pressed = 0;

void setup()
{
  // set pins as input with internal pull-up resistors enabled
  pinMode(PIN_ENCODER_A, INPUT_PULLUP);
  pinMode(PIN_ENCODER_B, INPUT_PULLUP);
  pinMode(PIN_ENCODER_SWITCH, INPUT_PULLUP);
  pinMode(PIN_PREVIOUS, INPUT_PULLUP);
  pinMode(PIN_NEXT, INPUT_PULLUP);
  pinMode(PIN_PLAY, INPUT_PULLUP);
  pinMode(LedPin, OUTPUT);


  TrinketHidCombo.begin(); // start the USB device engine and enumerate
 

  // get an initial reading on the encoder pins
  if (digitalRead(PIN_ENCODER_A) == LOW) {
    enc_prev_pos |= (1 << 0);
  }
  if (digitalRead(PIN_ENCODER_B) == LOW) {
    enc_prev_pos |= (1 << 1);
  }
}

void loop()
{
  int8_t enc_action = 0; // 1 or -1 if moved, sign is direction

  // note: for better performance, the code will use
  // direct port access techniques
  // http://www.arduino.cc/en/Reference/PortManipulation
  uint8_t enc_cur_pos = 0;
  // read in the encoder state first
  if (bit_is_clear(TRINKET_PINx, PIN_ENCODER_A)) {
    enc_cur_pos |= (1 << 0);
  }
  if (bit_is_clear(TRINKET_PINx, PIN_ENCODER_B)) {
    enc_cur_pos |= (1 << 1);
  }

  // if any rotation at all
  if (enc_cur_pos != enc_prev_pos)
  {
    if (enc_prev_pos == 0x00)
    {
      // this is the first edge
      if (enc_cur_pos == 0x01) {
        enc_flags |= (1 << 0);
      }
      else if (enc_cur_pos == 0x02) {
        enc_flags |= (1 << 1);
      }
    }

    if (enc_cur_pos == 0x03)
    {
      // this is when the encoder is in the middle of a "step"
      enc_flags |= (1 << 4);
    }
    else if (enc_cur_pos == 0x00)
    {
      // this is the final edge
      if (enc_prev_pos == 0x02) {
        enc_flags |= (1 << 2);
      }
      else if (enc_prev_pos == 0x01) {
        enc_flags |= (1 << 3);
      }

      // check the first and last edge
      // or maybe one edge is missing, if missing then require the middle state
      // this will reject bounces and false movements
      if (bit_is_set(enc_flags, 0) && (bit_is_set(enc_flags, 2) || bit_is_set(enc_flags, 4))) {
        enc_action = 1;
      }
      else if (bit_is_set(enc_flags, 2) && (bit_is_set(enc_flags, 0) || bit_is_set(enc_flags, 4))) {
        enc_action = 1;
      }
      else if (bit_is_set(enc_flags, 1) && (bit_is_set(enc_flags, 3) || bit_is_set(enc_flags, 4))) {
        enc_action = -1;
      }
      else if (bit_is_set(enc_flags, 3) && (bit_is_set(enc_flags, 1) || bit_is_set(enc_flags, 4))) {
        enc_action = -1;
      }

      enc_flags = 0; // reset for next time
    }
  }

  enc_prev_pos = enc_cur_pos;

  if (enc_action > 0) {
    TrinketHidCombo.pressMultimediaKey(MMKEY_SCAN_NEXT_TRACK);  // Clockwise, send multimedia volume up
    digitalWrite(LedPin, HIGH);
    delay(20); // debounce delay
    digitalWrite(LedPin, LOW);
 delay(110); // debounce delay
    TrinketHidCombo.typeChar(KEYCODE_3); 
  
  }
  else if (enc_action < 0) {
    TrinketHidCombo.pressMultimediaKey(MMKEY_SCAN_PREV_TRACK); // Counterclockwise, is multimedia volume down
   digitalWrite(LedPin, HIGH);
    delay(20); // debounce delay
    digitalWrite(LedPin, LOW);
 delay(110); // debounce delay
    TrinketHidCombo.typeChar(KEYCODE_3);  

 
  }


  
  // remember that the switch is active low 
  if (bit_is_clear(TRINKET_PINx, PIN_ENCODER_SWITCH)) 
  {
    if (sw_was_pressed == 0) // only on initial press, so the keystroke is not repeated while the button is held down
    {
 TrinketHidCombo.pressMultimediaKey(MMKEY_PLAYPAUSE); // Encoder pushed down, toggle mute or not
      delay(5); // debounce delay,
     // digitalWrite(LedPin, HIGH);
      
  
    }
    sw_was_pressed = 1;
     LedBlink();
  }
  else
  {
    if (sw_was_pressed != 0) {
      delay(5); // debounce delay
     // digitalWrite(LedPin, LOW);
    }
    sw_was_pressed = 0;
  }


 
 // remember that the switch PREVIOUS is active low 
  if (bit_is_clear(TRINKET_PINx, PIN_PREVIOUS)) 
 {
    if (PREVIOUS_was_pressed == 0) // only on initial press, so the keystroke is not repeated while the button is held down
    {
      TrinketHidCombo.pressMultimediaKey(MMKEY_SCAN_PREV_TRACK); // Encoder pushed down, toggle mute or not
      delay(80); // debounce delay
    TrinketHidCombo.typeChar(KEYCODE_3);  
     
    }
    PREVIOUS_was_pressed = 1;
     LedBlink();
 }
  else
  {
    if (PREVIOUS_was_pressed != 0) {
      delay(15); // debounce delay
      digitalWrite(LedPin, LOW);      
    }
    PREVIOUS_was_pressed = 0;
  }


  // remember that the switch NEXT is active low 
  if (bit_is_clear(TRINKET_PINx, PIN_NEXT)) 
 {
    if (NEXT_was_pressed == 0) // only on initial press, so the keystroke is not repeated while the button is held down
    {
      TrinketHidCombo.pressMultimediaKey(MMKEY_SCAN_NEXT_TRACK); // Encoder pushed down, toggle mute or not
      delay(80); // debounce delay
    TrinketHidCombo.typeChar(KEYCODE_3);  
    
    }
    NEXT_was_pressed = 1;
     LedBlink();
 }
  else
  {
    if (NEXT_was_pressed != 0) {
      delay(15); // debounce delay
      digitalWrite(LedPin, LOW);      
    }
    NEXT_was_pressed = 0;
  }

  
  // remember that the switch PLAY is active low 
  if (bit_is_clear(TRINKET_PINx, PIN_PLAY)) 
 {
    if (PLAY_was_pressed == 0) // only on initial press, so the keystroke is not repeated while the button is held down
    {
      TrinketHidCombo.pressMultimediaKey(MMKEY_PLAYPAUSE); // Encoder pushed down, toggle mute or not
      delay(15); // debounce delay
       
    }
    PLAY_was_pressed = 1;
    LedBlink();
 }
  else
  {
    if (PLAY_was_pressed != 0) {
     // delay(15); // debounce delay
     // digitalWrite(LedPin, LOW);
    }
    PLAY_was_pressed = 0;
  }



  TrinketHidCombo.poll(); // check if USB needs anything done, do every 10 ms or so
}

 
void LedBlink() {

digitalWrite(LedPin, HIGH);
 delay(15); // debounce delay
  digitalWrite(LedPin, LOW);
   delay(100); // debounce delay
}
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9

Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW

User avatar
MikeB
9x Developer
Posts: 17990
Joined: Tue Dec 27, 2011 1:24 pm
Country: -
Location: Poole, Dorset, UK

Re: Rotary encoder and long wires

Post by MikeB »

I'll assume you are using the internal pull up resistors. These are quite a high value (20K+). I suggest adding a low value pull up resistor on each encoder line, say 1K to 2K.

Mike.
erskyTx/er9x developer
The difficult we do immediately,
The impossible takes a little longer!
User avatar
jhsa
Posts: 19480
Joined: Tue Dec 27, 2011 5:13 pm
Country: Germany

Re: Rotary encoder and long wires

Post by jhsa »

will try now Mike, thanks..
Yes, internal pullups enabled. I did try a 22K external pullup but it didn't work. was afraid of trying lower values.
João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9

Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
User avatar
jhsa
Posts: 19480
Joined: Tue Dec 27, 2011 5:13 pm
Country: Germany

Re: Rotary encoder and long wires

Post by jhsa »

1K5 resistors. It didn't work :( Same problem.
when i turn to the left it does work a bit better but still bad.
When turning it to the right, It only pauses/Plays and it doesn't change the track. If I move the encoder faster, then it changes the track, but most of the times it doesn't pause afterwards.. :( Weird problem.. with shorter connection the encoder works as it should..
Actually i have similar problem on my PRO radio with the encoder.. Sometimes it works backwards.. I must turn the encoder really slow for it to work properly.. But that's another problem :)

João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9

Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
User avatar
jhsa
Posts: 19480
Joined: Tue Dec 27, 2011 5:13 pm
Country: Germany

Re: Rotary encoder and long wires

Post by jhsa »

Ok, problem more or less solved. Please see the attached picture.
Much better now.. Maybe it needs a little tweak. Maybe i just leave it as it is :)
Your opinion is very welcome as always.. I think i have learnt something today :)

Will post my little project when it is working. Maybe someone is interested.. This allows me to control my backing tracks when I'm playing music. I have a monitor in front of me and the laptop is in the back. as my hands are busy with the guitar, I can use my feet to start/stop and also next/previous.
I added the encoder as it makes it much faster to select a song. So, i can use both hands and feet :)

João
Attachments
circuit-schematic-v2.jpg
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9

Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW

User avatar
MikeB
9x Developer
Posts: 17990
Joined: Tue Dec 27, 2011 1:24 pm
Country: -
Location: Poole, Dorset, UK

Re: Rotary encoder and long wires

Post by MikeB »

attached picture?

Mike.
erskyTx/er9x developer
The difficult we do immediately,
The impossible takes a little longer!
User avatar
jhsa
Posts: 19480
Joined: Tue Dec 27, 2011 5:13 pm
Country: Germany

Re: Rotary encoder and long wires

Post by jhsa »

Sorry Mike.. A bit stressed here :) Picture is attached now.
Now have to finish adapting the code to the project.
One thing i would like to do and i don't know how to is to apply the delay before sending the Pause command, and only after the encoder has finished rotating. At the moment it sends the pause command on every click of the encoder. That slows things a bit down.
the delay before the pause is needed because the player (VLC) doesn't recognize the command if I send it too earlier, and sometimes it stops, sometimes doesn't. And the delay on every click makes the selection slower if I turn the encoder too fast. So if the code could somehow that the encoder is not turning either direction anymore, the pause command could be sent afterwards. The delay wouldn't then affect the encoder's rotation..

Thanks for all..
João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9

Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW
User avatar
Ozzy
Posts: 92
Joined: Sun Jul 08, 2012 7:57 am
Country: -

Re: Rotary encoder and long wires

Post by Ozzy »

I am not a coder but a hardware guy. I have tried cheap rotary encoders with arduino, and they skip and multi pulse a lot more than I care for. I think better encoders (not the cheap $1 encoders I tried) will work fine, especially with correct hardware filtering (decent strong pull ups with RC filter before GPIO input pin).

Rotary encoders that are simple mechanical contacts have some issues compared to optical/hall/capacitive encoders that output sharp, clean and bounce free switching, making it a lot easier for the mpu.

Generally:
Best Performance: Both signals connect to interrupt pins.
Good Performance: First signal connects to an interrupt pin, second to a non-interrupt pin.
Low Performance: Both signals connect to non-interrupt pins,
Found here.
https://www.pjrc.com/teensy/td_libs_Encoder.html
User avatar
jhsa
Posts: 19480
Joined: Tue Dec 27, 2011 5:13 pm
Country: Germany

Re: Rotary encoder and long wires

Post by jhsa »

I did the way it is shown on the picture above, and now the encoder on my 9XR-PRO radio works very well..

João
My er9x/Ersky9x/eepskye Video Tutorials
https://www.youtube.com/playlist?list=PL5uJhoD7sAKidZmkhMpYpp_qcuIqJXhb9

Donate to Er9x/Ersky9x:
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHX43JR3J7XGW

Post Reply

Return to “Other Electronic Projects”