Using an encoder as a joystick input, but the range is really small.

I’m using a Teensy 2.0 to read an encoder and output to a Windows 8 laptop as the Z axis of a USB joystick. I’m using the Windows Game Controllers Control Panel to test the results, as explained on this page: http://pjrc.com/teensy/td_joystick.html.

My code seems to be working, in that when I turn the encoder counterclockwise, the bar in the control panel goes to the left, and when I turn the encoder clockwise, the bar in the control panel goes to the right. The problem I’m having is that I only turn the encoder about one or two detents before the bar goes all the way to the end. I was hoping to have the movement be a little slower, so that I could turn the encoder at least 180 degrees from center to get to the left side, and the same for the right side.

Thanks very much in advance for any help you might be able to provide me. I’m not very experienced with this kind of programming, and I’ve been doing my best to research and figure this out on my own, but I haven’t been able to get past this one issue.

#include <Encoder.h>


Encoder myEnc(5, 6);
int zposition = 512;

void setup() {
  Serial.begin(9600);
  Serial.println("Basic Encoder Test:");
  myEnc.write(0);
  Joystick.Z(zposition);
}

void loop() {
  if (myEnc.read() < -1){
    Joystick.Z(++zposition);
    myEnc.write(0);
    delay(10);
  }
  if (myEnc.read() > 1){
    Joystick.Z(--zposition);
    myEnc.write(0);
    delay(10);
  }
}

I suppose that without those delays it goes right to the ends?

What happens is if you turn 1 click to the right, your code keeps reading that, 100 times a second, and adds to the Z position even though the encoder has only turned 1 click from 0.

Why don't you only change Z position when the encoder read value changes?

GoForSmoke: Why don't you only change Z position when the encoder read value changes?

That makes sense to me about the delays and the reading time. But how do I only change Z when the read value changes? I guess I thought that's what I was doing with the code above.

I only looked at the basic example in the Encoder library in my IDE. I don’t have one to ‘get thurra’ with, but…

this is in my IDE under Examples->Encoder->Basic

/* Encoder Library - Basic Example
 * http://www.pjrc.com/teensy/td_libs_Encoder.html
 *
 * This example code is in the public domain.
 */

#include <Encoder.h>

// Change these two numbers to the pins connected to your encoder.
//   Best Performance: both pins have interrupt capability
//   Good Performance: only the first pin has interrupt capability
//   Low Performance:  neither pin has interrupt capability
Encoder myEnc(5, 6);
//   avoid using pins with LEDs attached

void setup() {
  Serial.begin(9600);
  Serial.println("Basic Encoder Test:");
}

long oldPosition  = -999;

void loop() {
  long newPosition = myEnc.read();
  if (newPosition != oldPosition) {
    oldPosition = newPosition;
    Serial.println(newPosition);
  }
}

I read that as the library used this way will provide my with the absolute position.
I can then do what I want with that value and tell the PC that the Z axis is at some position.

roggesound:
I’m not very experienced with this kind of programming, and I’ve been doing my best to research and figure this out on my own, but I haven’t been able to get past this one issue.

I don’t know your encoder library, but if it is polling the encoder state and the delay is for debouncing the input, your loop() code is wrong for debouncing the encoder.

You don’t need any end-stop positions for your joystick?
If so, I have prepared something for you, but commented out the code.

Perhaps try that loop logic:

void loop() {
  zposition += myEnc.read();
// if some end-stop conditions must be evaluated, do it!  
//  if (zposition < 0) zposition=0;
//  if (zposition > 1023) zposition=1023;
  Joystick.Z(zposition);
  myEnc.write(0);
  delay(5);
}

Just 5 milliseconds for debouncing should be enough.

I read that as the library used this way will provide my with the absolute position. I can then do what I want with that value and tell the PC that the Z axis is at some position.

I started with that encoder example, and couldn't really get it to translate to the Joystick. What you said makes sense, about knowing the value and then translating that to the joystick. I'm not sure about the best way to do that, and more importantly, I don't know what the joystick is looking for, or what the values for that might be.

jurs:
I don’t know your encoder library, but if it is polling the encoder state and the delay is for debouncing the input, your loop() code is wrong for debouncing the encoder.

You don’t need any end-stop positions for your joystick?
If so, I have prepared something for you, but commented out the code.

Sorry, work and school take up most of my week, so I just got back to this.

I plugged in your code and have the exact same result as my code. It takes about one notch on the encoder to go from high to low. Also, it is a specific notch, as in, I can’t turn it to the low side five notches, and then have it go back high if I go one notch in the other direction. I’m not sure if that description makes sense, but it seems like the correct behavior. I just need to get some amount of adjustment so that I can make it be ten or twenty or 50 notches.

roggesound:
I just need to get some amount of adjustment so that I can make it be ten or twenty or 50 notches.

At first you’d better test each hardware and each code for itself:

  • if the encoder test code works like it should (without the joystick)
  • if the joystick test code works like it should (without the encoder)

If every hardware is tested okay for itself, then put the two things together.

So what may be going wrong?

Perhaps your joystick control doesn’t work like you thought?
You think, the position should be set with your code from 0 to 1023?
And then it should move?

Then test it first, without any encoder:

void loop() {
  for (zposition=0;zposition<1024;zposition++)
  {
    Joystick.Z(zposition);
    delay(5);
  }
}

What’s going on then?

When I run that code, the joystick goes full left, waits 5 seconds, then goes full right, and loops. So that’s something.

I don’t have an actual usb joystick to test with this computer to see if it ever does anything else, but I’ve seen pictures of the bars in the test menu in different positions.

I’ve also tried to do this with a potentiometer and gotten essentially the same result. Which seems like it points to the joystick end of things, as I can get a real reading for the pot and the encoder on the serial monitor in arduino.

roggesound:
When I run that code, the joystick goes full left, waits 5 seconds, then goes full right, and loops. So that’s something.

I’d say it’s nothing.

The joystick is not working if you try to control it directly with values from a for-loop.

If the joystick would work with your code in the range 0…1023 you should see that:

  • starts on one end stop position
  • slowly moves to the opposite end stop position within 5 seconds
  • jumps back to start and loop begins again
    As you cannot see this in your test, your joystick code is NOT working as expected.

And if the joystick code is not working for itself with the joystick, you can never control the non working joystick code with some encoder code. It doesn’t matter if the encoder code is working or not. If the joystick code is not working at all, it will not be working when combined with some other hardware and code.

I see what you’re saying.

I did get one result. I changed the code to this:

int zposition;

void setup() {
  Serial.begin(9600);
}
  
void loop() {
  for (zposition=0;zposition<1024;zposition++)
  {
    Joystick.hat(zposition);
    delay(5);
  }
}

This code moves the hat function of the joystick through its 8 positions.

My confusion is that the code above is ALL of my code. It works with hat, but doesn’t work with Z or X or Y. I’m not sure if there’s a library I’m missing, but I’d imagine not since the hat switch works. I got all the joystick info from this website: http://pjrc.com/teensy/td_joystick.html

It turns out there's a calibration you can do for the game controller in the windows game controller settings window. This fixed my problem and made the knob work the way I thought it should.

Now I have to work on making it a little less sensitive; actually making it move faster.