Teensy Joystick HID and encoder assign to buttons

Frans M

Member
Hi,

How do you be able to connect an encoder to the standard joystick/keyboard/mouse HID and assign them to the joystick buttons?

Trying to get working is:

When turn the encoder left it should show in the joystick mapping of windows for instance button 1 going ON/OFF and when turn right it would be button 2 going ON/OFF..

Right now I have the situation that both buttons go ON/OFF at the same time.

Any suggestions or example script maybe possible to share?

Thanks

rgds
Frans M
 
encoders give values increasing or decreasing depending on the way you turn them.
you have to use 2 buttons one for turning clockwise and the second for counterclockwise
Teensy has a Encoder function just use it.

here's the code i used to add encoders to my joystick device.

in the declaration

Code:
long oldPosition  = 0;
Encoder myEnc(0, 1); //Pins used by your encoder

In the loop code
Code:
  long newPosition = myEnc.read(); //read the current encoder value
  if (newPosition > oldPosition) { //encoder current value is greater than previous value
    oldPosition = newPosition;
    Joystick.button(1, 1);
    Joystick.button(2, 0);
  }
  if (newPosition < oldPosition) { //encoder current value is smaller than previous
    oldPosition = newPosition;
    Joystick.button(1, 0);
    Joystick.button(2, 1);
  }
 
Hi IceMaker.

Thank you for helping me out in this :)

I have try your code and it works but I have now the situation that when I turn encoder left or right the button 1 and 2 going on and off after each other..

I was trying to get it this way:

Turn left only button 1 will go on and off
Turn right only button 2 will go on and off

Any suggestions for this solution?

Thanks

rgds
Frans
 
Sorry i have forgotten one thing in the code!

Put this after the joystick report line

Code:
  Joystick.button(2, 0);
  Joystick.button(1, 0);
 
HI IceMaker,

Possible you can provide me the complete code you use yourself for the joystick with Encoder use?

Thanks

rgds
Frans
 
@Frans M: if you posted a complete enough sketch to do what you had when you started this thread it would be a better prompt to people who can make it do what you want to just change your code enough to do it and post it back.

By posting your own work (which preferably compiles as is but series of errors at compile time is better than nothing) you save us the trouble of making a complete enough to expect it to compile for you and make it much more encouraging to bother.

@IceMaKeR: The trouble with snippets is that they leave details open to question for people who cannot (yet) do it for themselves - if Frans M could actually do it (albeit by trying very hard perhaps) then they would get it from your snippets and there wouldn't be any series of questions leading to the request above.

@All: If you feel like you've written something secret and technical that you could maybe even patent (or you are too embarrassed to show your messy work) then you needn't post all your secrets, strip out anything you don't want in the public domain and post enough of your sketch to allow us the least effort in giving you a working version of it - this will be your quickest way to resolving most problems with code.
 
Thank you robsolos for your comments on this.

I have been trying to sort it out but still not have the right results even if I get closer .. :)

Code I have now is this:


#include <Bounce.h>
#include <Encoder.h>


Bounce button2 = Bounce(1, 10);
Bounce button3 = Bounce(2, 10);


long oldPosition = 0;
Encoder myEnc(1, 2);


void setup() {

pinMode(1, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
}

void loop() {


button2.update();
button3.update();


long newPosition = myEnc.read();

if (newPosition > oldPosition) {
oldPosition = newPosition;
Gamepad.button(2, 1);
Gamepad.button(3, 0);

}
if (newPosition < oldPosition) {
oldPosition = newPosition;
Gamepad.button(2, 0);
Gamepad.button(3, 1);
}

if (button2.fallingEdge()) {
Gamepad.button(2, 1);
}

if (button3.fallingEdge()) {
Gamepad.button(3, 1);
}

if (button2.risingEdge()) {
Gamepad.button(2, 0);
}

if (button3.risingEdge()) {
Gamepad.button(3, 0);
}

Gamepad.send_now();


}

When move encoder to left it will show in the windows joystick controller button 2 going on and off and when move it to the right it will show button 3 go on and off but it also sometimes create that both buttons show on again so it is not really stable

it looks that moving right (button 3) gives more issues of showing as well button 2 become active than when move left (button 2)

Also when moving the encoder quicker it looks like it create some hickups..

I am sure there must be something not yet correct but tried many things but not have the correct result.

Thanks

rgds
Frans
 
When I tried to compile what you posted the compiler complained 'gamepad was not declared in this scope', what I am posting back here may or may not work but it will need the addition of declaring the gamepad to find out.

Code:
#include <Bounce.h>
#include <Encoder.h>

/* declare 'gamepad' around here please :) */

Bounce button2 = Bounce(1, 10);
Bounce button3 = Bounce(2, 10);


long oldPosition = 0;
Encoder myEnc(1, 2);

int8_t dir=0,odir=0;

void setup() {
	pinMode(1, INPUT_PULLUP);
	pinMode(2, INPUT_PULLUP);
	Serial.begin(Serial.baud());
}

void loop() {
	button2.update();
	button3.update();
	long newPosition = myEnc.read();
	if(newPosition!=oldPosition) {
		Serial.printf("Encoder: %i",newPosition);
		if(newPosition-1==oldPosition) {
			dir=1; // value is going up
		} else if(newPosition+1==oldPosition) {
			dir=-1; // value is going down
		} else if(newPosition>oldPosition) {
			dir=-1; // value jumped as if overflowed downwards
		} else /* if(newPosition<oldPosition) */ {
			dir=1; // conditional above commented out because it is the only thing left that could
			// have happened - value jumped as if overflowed upwards
		}
		oldPosition=newPosition;
	} else {
		dir=0;
	}
		
	if(dir>0) {
		Gamepad.button(2, 1);
	} else if(dir<0) {
		Gamepad.button(3, 1);
	} else if(odir!=0) {
		Gamepad.button(2, 0);
		Gamepad.button(3, 0);
	}
	odir=dir;
	
	if (button2.fallingEdge()) {
		Gamepad.button(2, 1);
	}

	if (button3.fallingEdge()) {
		Gamepad.button(3, 1);
	}

	if (button2.risingEdge()) {
		Gamepad.button(2, 0);
	}

	if (button3.risingEdge()) {
		Gamepad.button(3, 0);
	}

	Gamepad.send_now();
}
 
hey, if the version above works (once gamepad is appropriately declared) then this version may be better because it does not basically spam messages to the PC while nothing is actually happening - chances are that the library taking care of 'gamepad' already does a simile of this but if not then the additions I have made to the above will make it so that messages are only sent when something happens.

Code:
#include <Bounce.h>
#include <Encoder.h>


Bounce button2 = Bounce(1, 10);
Bounce button3 = Bounce(2, 10);


long oldPosition = 0;
Encoder myEnc(1, 2);

int8_t dir=0,odir=0;
uint8_t isChanged=0;

void setup() {
	pinMode(1, INPUT_PULLUP);
	pinMode(2, INPUT_PULLUP);
	Serial.begin(Serial.baud());
}

void loop() {
	button2.update();
	button3.update();
	long newPosition = myEnc.read();
	if(newPosition!=oldPosition) {
		Serial.printf("Encoder: %i",newPosition);
		if(newPosition-1==oldPosition) {
			dir=1; // value is going up
		} else if(newPosition+1==oldPosition) {
			dir=-1; // value is going down
		} else if(newPosition>oldPosition) {
			dir=-1; // value jumped as if overflowed downwards
		} else /* if(newPosition<oldPosition) */ {
			dir=1; // conditional above commented out because it is the only thing left that could
			// have happened - value jumped as if overflowed upwards
		}
		oldPosition=newPosition;
	} else {
		dir=0;
	}
		
	if(dir>0) {
		Gamepad.button(2, 1);
		isChanged=1;
	} else if(dir<0) {
		Gamepad.button(3, 1);
		isChanged=1;
	} else if(odir!=0) {
		Gamepad.button(2, 0);
		Gamepad.button(3, 0);
		isChanged=1;
	}
	odir=dir;
	
	if (button2.fallingEdge()) {
		Gamepad.button(2, 1);
		isChanged=1;
	}

	if (button3.fallingEdge()) {
		Gamepad.button(3, 1);
		isChanged=1;
	}

	if (button2.risingEdge()) {
		Gamepad.button(2, 0);
		isChanged=1;
	}

	if (button3.risingEdge()) {
		Gamepad.button(3, 0);
		isChanged=1;
	}

	if(isChanged) {
		Gamepad.send_now();
		isChanged=0;
	}
}
I would appreciate knowing how this works out for you :)
 
Last edited:
Hi Robsolos,

Thank you so much, this code seems to do the trick :)

I only have one thing... it turns now nicely up and down but there is only a jump between it...

sometimes it do +1 but then next turn it do +4 or +6 turn the other way same so it is not stable counting +1 or -1

Not sure if this have something to do with the way the encoder send the signal?

Thanks

rgds
Frans
 
I included some serial stuff so that the results coming from the encoder would be sent; when you plug in your Teensy you should be able to find its port under 'tools->serial port', select the right port and open 'serial monitor' please.

When you have serial monitor open try turning the rotary encoder clockwise slowly and watch the series of numbers coming back from it, please roll the encoder knob until it makes it to zero again; please post the numbers given from zero to zero in that direction and also the number sequence given in the other direction so that I can have a better chance to make it give you the right results with less glitches.

Edit: Oh, my line for serial printing the numbers coming from the encoder is not that good, please add \n where shown in snippet:
Code:
Serial.printf("Encoder: %i[B][COLOR="#0000FF"]\n[/COLOR][/B]",newPosition);
 
Here is the output:

You can see some hiccup in it...

Encoder: 1
Encoder: 2
Encoder: 3
Encoder: 4
Encoder: 5
Encoder: 6
Encoder: 7
Encoder: 8
Encoder: 9
Encoder: 10
Encoder: 11
Encoder: 12
Encoder: 13
Encoder: 14
Encoder: 15
Encoder: 16
Encoder: 20
Encoder: 19
Encoder: 18
Encoder: 17
Encoder: 16
Encoder: 20
Encoder: 19
Encoder: 18
Encoder: 17
Encoder: 16
Encoder: 15
Encoder: 14
Encoder: 13
Encoder: 12
Encoder: 11
Encoder: 10
Encoder: 9
Encoder: 10
Encoder: 9
Encoder: 8
Encoder: 7
Encoder: 6
Encoder: 5
Encoder: 6
Encoder: 5
Encoder: 4
Encoder: 8
Encoder: 7
Encoder: 6
Encoder: 5
Encoder: 4
Encoder: 3
Encoder: 2
Encoder: 1
Encoder: 0
 
sorry, would you mind posting the results in two distinct groups, like this;

Rolling clockwise
Code:
Encoder: 0
Encoder: 1
..
..
Encoder: 0
Counter-clockwise
Code:
Encoder: 0
Encoder: 20
..
..
Encoder: 0
..
I request it like this so I can be more sure of what is happening in both cases

you can use [code]<put code like stuff here>[/code] to make it come out in the 'code' block like that
 
When turn the encoder (one click) it gives 2 values in the serial monitor, sometimes 3 at the same time


clockwise:

Code:
Encoder: 1
Encoder: 2
Encoder: 6
Encoder: 7
Encoder: 8
Encoder: 9
Encoder: 10
Encoder: 11
Encoder: 12
Encoder: 13
Encoder: 14
Encoder: 15
Encoder: 16
Encoder: 17
Encoder: 18
Encoder: 22

counter clockwise
Code:
Encoder: -1
Encoder: -2
Encoder: -3
Encoder: -4
Encoder: -5
Encoder: -6
Encoder: -7
Encoder: -8
Encoder: -9
Encoder: -10
Encoder: -11
Encoder: -12
Encoder: -13
Encoder: -14
Encoder: -15
Encoder: -16
Encoder: -12
Encoder: -13
Encoder: -14
Encoder: -15
Encoder: -16
Encoder: -17
Encoder: -18
Encoder: -19
Encoder: -20
Encoder: -24
 
hmmm, ok, I wasn't expecting those values. The jumps may be noise or something on the lines and my code is inappropriate for what is happening.

Would you mind terribly sharing a circuit diagram (or picture as clear as you can capture) of how the encoder is connected, also please give some details about the rotary encoder itself - enough to help find a datasheet for it will be best.
 
just fyi: "Connect directly to the teensy board with 3 wires, 1 wire GND....." does not a circuit diagram make - I cannot tell if anything about how you have connected it can be improved except for one thing;

You should consider using a pair of external 10k resistors rather than 'INPUT_PULLUP' because the encoder you are using has 10n caps (apparently) on each of its output pins so using a weaker pull up ('INPUT_PULLUP' is a weaker pull up) makes the output 'sloppy' (curved waveforms) where stronger pull-ups will make the output much more rigid (squarer waveforms) and that is much easier for the Encoder library to read.

Try this code, it won't overcome the apparent glitches in output sequence you are getting but it should fare the best and if you fix the encoder glitching issues it should be adequate enough to proceed with

Code:
#include <Bounce.h>
#include <Encoder.h>

/* declare 'gamepad' around here please :) */


Bounce button2 = Bounce(1, 10);
Bounce button3 = Bounce(2, 10);


long oldPosition = 0;
Encoder myEnc(1, 2);

int8_t dir=0,odir=0
uint8_t isChanged=0;

void setup() {
	pinMode(1, INPUT_PULLUP);
	pinMode(2, INPUT_PULLUP);
	Serial.begin(Serial.baud());
}

void loop() {
	button2.update();
	button3.update();
	long newPosition = myEnc.read();
	dir=newPosition-oldPosition;
	oldPosition=newPosition;
		
	if(dir>0) {
		Gamepad.button(2, 1);
		isChanged=1;
	} else if(dir<0) {
		Gamepad.button(3, 1);
		isChanged=1;
	} else if(odir!=0) {
		Gamepad.button(2, 0);
		Gamepad.button(3, 0);
		isChanged=1;
	}
	odir=dir;
	
	if (button2.fallingEdge()) {
		Gamepad.button(2, 1);
		isChanged=1;
	}

	if (button3.fallingEdge()) {
		Gamepad.button(3, 1);
		isChanged=1;
	}

	if (button2.risingEdge()) {
		Gamepad.button(2, 0);
		isChanged=1;
	}

	if (button3.risingEdge()) {
		Gamepad.button(3, 0);
		isChanged=1;
	}

	if(isChanged) {
		Gamepad.send_now();
		isChanged=0;
	}
}
 
I did try to use it with the 10k but the results are not different it keep showing 2 outputs at the same time in serial monitor.

Same when I use the example encoder basis sketch one turn(click) show 2 lines at the same time in serial monitor ..

1
2

3
4

5
6
===
6
5

4
3

2
1
 
2 'steps' for 1 'click' is not unusual - jumps in sequence of numbers is the problem I am trying to help you eliminate by use of external resistors.

That sequence looks healthier but it is only showing a short sequence - over a longer sequence (minimum of 10 clicks, prefer 20; or maybe 2 full turns of shaft) if all numbers while turning clockwise become bigger than last number without going smaller and all numbers while turning anti-clockwise become smaller with no larger numbers then it is usable for your purposes and glitches will not be apparent.

If sequence does not miss any numbers that is super but even if it skips a few numbers only ever in the 'correct' direction then it is perfectly usable and the last batch of code I posted should give you the correct single button press even tho there are skips - it is where the 'jump' goes backwards makes a problem where the opposite button will be sent instead of the 'correct' one.

EDIT: If you want exactly only one button sent per click/detent on the rotary encoder then that is possible (practically easy actually) but only if the sequence of numbers returned for rotating the shaft is pretty much perfectly repeated every time.
 
Last edited:
I have been trying some things and now I have changed this line :

Code:
long newPosition = myEnc.read();

into

Code:
long newPosition = myEnc.read() /2;

This seems to do the trick by only 1 output instead of 2

The only weird things is now that if I turn it anti clockwise it give me a new value every click but if I turn it clock wise it needs 2 clicks to give me a new value...?

So it works perfect when I turn anti clockwise but when clockwise it seems to glitch still
 
Hi Robsoles,

Any idea what could be the reason for the different output when turn CW and CCW.

anywhere this could be adjusted software wise?

Thanks

rgds
Frans
 
I have no doubt it can be adjusted software-wise.

Can you post your current sketch or do I have to guess which batch of my attempts above you are using?
 
Back
Top