Hopefully a simple encoder question.

Hi Everyone,
My name is Temoor and thanks for your help. I am trying to use encoder to type on a keyboard a series of letters as I move the knob and when I press the encoder button (4 times) it then will let me keyboard a series of numbers. My problem is that I just want to enter one number or letter. But as I knob thru it will print out aaaaaaaaaaaa, then bbbbbb, ccc, ddddddddd, maybe skip eeeee, etc. Same with the numbers. So far I am excited that it even works since I have started the learning phase of Arduino about a week ago. Thanks!!! Any ideas or advice would be greatly appreciated! Here is the CODE:

//libraries
#include <Bounce.h>  //for bounce library
#define ENCODER_OPTIMIZE_INTERRUPTS
#include <Encoder.h> //for encoder library

//Encoder pins
Encoder knobLeft(0, 1);  //   avoid using pins with LEDs attached
Encoder knobRight(2, 3);
const int button4pin = 4;

Bounce button4 = Bounce(4, 10); 

// Encoder positions
long positionLeft  = -9;
long positionRight = -9;
int newLeft;

//Encoder push buttons
int button4PushCounter = 0;
int button4State = 0;
int lastButton4State = 0;

void setup() {
  pinMode(0, INPUT_PULLUP); //KNOB1 
  pinMode(1, INPUT_PULLUP);  //KNOB1 
  pinMode(4, INPUT_PULLUP);  //KNOB SWITCH 1
  
Serial.begin(9600);
Serial.println("TwoKnobs Encoder Test as well:");
}
void loop() {
                 
//Code for Button state and Encoder menu
  button4State = digitalRead(button4pin);
  if (button4State != lastButton4State) {
    if (button4State == HIGH) {
       Serial.print("number of button pushes: ");
       Serial.println(button4PushCounter);
       button4PushCounter++;       
       }
       lastButton4State = button4State; 
  }
  if (button4PushCounter == 1) {
    Serial.println("LEVEL 1: Letters");
    newLeft = knobLeft.read();
    if (newLeft != positionLeft) {
    //newContrain = constrain(newLeft, 0, 10);
    int range = map(newLeft,0, 15, 0, 10);
    switch (range) {
    case 0:
          break;  
    case 1:
          Keyboard.print("a"); 
          break;
    case 3:
         Keyboard.print("b"); 
          break;
    case 5:
         Keyboard.print("c"); 
          break;
    case 7:
        Keyboard.print("d"); 
          break;
    case 9:
          Keyboard.print("e"); 
          break;  
    case 10:       
          break; 
    }
    delay(2);
    }
   }
   else if (button4PushCounter == 2) {
    Serial.println("LEVEL 2: Numbers");
    newLeft = knobLeft.read();
    if (newLeft != positionLeft) {
    //newLeft = constrain(newLeft, 0, 20);
    int range = map(newLeft,0, 15, 0, 10);
    switch (range) {
    case 0:
          break;
    case 1:
          Keyboard.print("1");
          break;
    case 2:
          break;
    case 3:
          Keyboard.print("2");
          break;
    case 4:
          break;
    case 5:
          Keyboard.print("3");
          break;
    case 6:
          break;
    case 7:
          Keyboard.print("4");
          break;
    case 8:
          break;
    case 9:
          Keyboard.print("5");
          break;
    case 10:
          break;
    }
    delay(2);    
    }
   } 
   else if (button4PushCounter == 3) {
   button4PushCounter = 0;
  }
  positionLeft = newLeft;
}

You need to explain that sketch. It appear as though you are using pins 0, 1, 2, and 3 for the encoder, pins 0 and 1 for the Serial port, and pins 0, 1, 2, and 3 for switches.

You can't use a pin for 3 purposes.

By the way, you don't have a function called VOID() anywhere. If you are going to comment the }, do it right.

Read this before posting a programming question

Please edit your post, select the code, and put it between [code][/code] tags.

You can do that by hitting the # button above the posting area.

I think the problem is that you're doing the character output any time you find that the button has been pressed four times, not when the fourth press is detected.

I think that if you put the 'if (button4PushCounter == 4)' chunk of code inside the block of code that increments button4PushCounter then you'd get the behaviour you say you want.

Ok, thanks for the tips fellas. I cleaned up the code and put it in the appropriate kitty box. Thanks. So I think I figured out some of the jitterness to the encoder switching from letters, numbers, etc. Feels a little better after tweaking. Now, is there a way to put lets say 10 number on the encoder so it takes up the whole 360 degree of the encoder? LEt me explain a different way, the letter and numbers Im using span about a fourth of the encoder total 360 degree spin. Can I somehow span that across the whole 360degrees? I tried it on a potentiometer using the MAP function but it didn't like it. Any thoughts? Thanks!

Count 36 pulses and increment the counter by one?

I tried it on a potentiometer using the MAP function but it didn't like it.

It appears in that sentence three times, clearly referring to three different things. No clue what any of them refer to, though, or what it was it didn't like about it.

Sorry to write unclearly. Basically, I would like to span lets say 12 letters or numbers across the encoder in one spin and somehow after I spin it 360 degrees it will restart the letters or numbers. Right now the numbers and letters span about a fourth of the circumference if that makes sense. I like the idea of counting 36 pulses and then increment a counter. Ill try that! Thanks Nick will let you know. The map function worked well using a potentiometer to span (different project) but encoder did not like it. Constrain function didn't work either.

The map function worked well using a potentiometer to span (different project) but encoder did not like it. Constrain function didn’t work either.

It’s hard to imagine why you care whether the encoder likes, or dislikes some code.

Now, whether the code was reading the encoder data correctly, or not, whether the code was mapping the counter that was being incremented or decremented as the encoder was moved was using the map function in a meaningful way, and/or whether the constrain function was being used correctly, or not, are all mysteries, seeing as how the code is missing from your post.

The map and constrain functions can be used successfully with the count from the encoder. Whether or not it makes sense to is a different story.

Thanks Paul'S for your suggestion. I'm sorry the code doesn't make too much sense but its my first code so I figure its not clean. Hopefully that will come soone with some experience. I did make a counter and mapped o it and now the spanning works but I get several aaaaa,bbbbb,etc when I move the knob.What I want to do is just have the encoder type one key at a time so I can use it to emulate a keyboard for macros. Ill add the code soon. Any quick fix idea? Maybe divide the counter and map out those values? As far as the overall code and the encoder switch, Do you think making an attach interrupt function for the switch will make this an overall better code structure?

Do you think making an attach interrupt function for the switch will make this an overall better code structure?

How fast is the encoder being turned? If it is at human speeds, polling is plenty fast to catch all the changes. If it is being turned be a motor, at 40000 rpm, then interrupts are necessary.

As for why the aaaaa, bbbbb, etc., I suggest that you need to send a new letter only when the letter to send is not the same as the last one sent.

Obviously, seeing your code, no matter how bad you think it is (it probably really isn’t that bad), would help with the answers.

I have a thread about rotary encoders and interrupts, if that helps.

http://www.gammon.com.au/forum/?id=11130

Cool. I will def. check out the link. and Thanks guys. I haven’t been challenged in a while and esp. making sense. If it helps the folks helping me out, the board I’m using is a teensey++, so I’am running the encodier lib. code from there site. So that might make more sense of the code. Any advice on that library? Strengths/weaknesses? Here is the updated code and it works. Great question on the encoder. Yes its human interface using the HID. So basically, I have a program running on my pc and just want to run macros (not software…long story, locked IT pc.) So this device will work as a encoder that after each press of the switch will switch to letters and second switch to numbers, and the third switch to nothing. I am still working on the stutter of the letters, so bare with me. Also, I was thinking about using interrupts, or getting rid of the whole map thing and just stick with a running average of the encoder spin?? So many ways to skin a cat. I feel like a yarn. Thanks again!

//libraries
#include <Bounce.h>  //for bounce library
#define ENCODER_OPTIMIZE_INTERRUPTS
#include <Encoder.h> //for encoder library

//Encoder pins
Encoder knobLeft(0, 1);  //   avoid using pins with LEDs attached
Encoder knobRight(2, 3);
const int button4pin = 19;

Bounce button4 = Bounce(19, 20); 
Bounce knobleft = Bounce(0,10);

// Encoder positions
long positionLeft  = 0;
long positionRight = 0;
int newLeft;
int encoderCounter = 0;

//FORCE VAR
int duration;

//Encoder push buttons
int button4PushCounter = 0;
int button4State = 0;
int lastButton4State = 0;

void setup() {
  pinMode(0, INPUT_PULLUP); //KNOB1 
  pinMode(1, INPUT_PULLUP);  //KNOB1 
  pinMode(19, INPUT_PULLUP);  //KNOB SWITCH 1
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  
Serial.begin(9600);
Serial.println("TwoKnobs Encoder Test as well:");
}
void loop() {
                 
//Code for Button state and Encoder menu
  button4State = digitalRead(button4pin);
  if (button4State != lastButton4State) {
    if (button4State == HIGH) {
       Serial.print("number of button pushes: ");
       Serial.println(button4PushCounter);
       button4PushCounter++;       
       }
       lastButton4State = button4State; 
 }
  if (button4PushCounter == 1) {
    //Serial.println("LEVEL 1: Letters");
    newLeft = knobLeft.read();
    if (newLeft < positionLeft) {
      encoderCounter = encoderCounter--;
      Serial.println(encoderCounter);
      } 
     else if (newLeft > positionLeft) {
     encoderCounter = encoderCounter++; 
     Serial.println(encoderCounter);
    int range = map(encoderCounter, 0, 120, 0, 10);
    switch (range) {
    case 0:
          Keyboard.print("a");
          break; 
    case 1:
          Keyboard.print("b"); 
          break;
    case 2:
         Keyboard.print("c"); 
          break;
    case 3:
         Keyboard.print("d"); 
          break;
    case 4:
         Keyboard.print("e"); 
          break;
    case 5:
         Keyboard.print("f"); 
          break;           
    case 6:
         Keyboard.print("g"); 
          break;
    case 7:
        Keyboard.print("h"); 
          break;
    case 8:
          Keyboard.print("i"); 
          break; 
    case 9: 
          Keyboard.print("j");    
          break; 
    case 10: 
          Keyboard.print("k");    
          break;    
          } 
   }
   positionLeft = newLeft;
  } 
   else if (button4PushCounter == 2) {
    Serial.println("LEVEL 2: Numbers");
    newLeft = knobLeft.read();
    if (newLeft != positionLeft) {
    //newLeft = constrain(newLeft, 0, 20);
    int range = map(newLeft,0, 15, 0, 10);
    switch (range) {
    case 0:
          break;
    case 1:
          Keyboard.print("1");
          break;
    case 2:
          break;
    case 3:
          Keyboard.print("2");
          break;
    case 4:
          break;
    case 5:
          Keyboard.print("3");
          break;
    case 6:
          break;
    case 7:
          Keyboard.print("4");
          break;
    case 8:
          break;
    case 9:
          Keyboard.print("5");
          break;
    case 10:
          break;
    }
    delay(2); 
    positionLeft = newLeft;   
    }
   }

in!

These do not go in the same sketch.

Bounce knobleft = Bounce(0,10);

Serial.begin(9600);
Serial.println("TwoKnobs Encoder Test as well:");

You can't use pin 0 as a serial port AND a switch input at the same time.

Encoder knobLeft(0, 1);  //   avoid using pins with LEDs attached
Encoder knobRight(2, 3);

You can't have a switch connected to the same pin as the encoder, which can't be on the serial port pins anyway.

These errors were pointed out at the start of the thread and you haven't fixed them yet.

Ok will correct that. I thought somehow the encoder pin acts like a switch and thought this was a way to fix any denouncing from the encoder signal.

I thought somehow the encoder pin acts like a switch

It does.

and thought this was a way to fix any denouncing from the encoder signal.

By connecting it to the hardware serial port? I'm sorry. I don't follow that line of reasoning.

The knobLeft instance is never used, so defining it was a waste of time, effort, and resources.

Defining names like knobleft and knobLeft in the same sketch is a bad idea. Mistaking one for the other is far too easy.

Thanks PaulS for catching that capitalization error. I tried actually changing the names. I would rather have knob1 etc. but when I changed it, it didn't work. Maybe something with the encoder library. I'm actually looking into how to implement attach interrupts because I want to add more to this program other than encoders. But I want the encoder to interrupt these future add ons. I'm not sure if the library program and the encoder sketch I'm using takes advantage of this feature. I'm going to try working on it today after "real work" If you all think that's a good idea? I would prefer a nice clean code from the start as a building platform. From some of the posts out there it seems I have alot of hairy we s in here. Alot of it is from lack o experience but thanks for looming them out. Mistakes are the only way I seem to somewhat learn from. Thanks for your patience folks!!

Thanks Nick for the site. I should have visited it a long time ago. I like the free interrupt version but two interrupts is probably the way Ill go. I hope you don’t mind but I used that code and replaced my whole encoder formatting. Also, the attachinterrupts function really helped out. I will have to visit the neighborhood radioshack for the capicator debouncing. Yes Im that new skool to this. I dont even have capicators laying around the house. Is there a way to code some function to ask if the key was sent i.e. is the cases I have Keyboard.print(“K”); and it checks it smoehow and stops it from resending it? Can it be plugged into an if statement? I still havent worked around that problem and get repeated letters. Thanks!

You mean something like:

if (key != lastKey)
  Keyboard.print (key);

lastKey = key;

I dont even have capicators laying around the house.

Don't forget Halloween is coming up. You might need them.

Great Thanks Nick for the page. I will def. try the capicators and hopefully that will help with the jitteriness. I really like the last code regarding the interrupts and only using one for each encoder. That frees up things alot! I am still confused on how I can just write a single letter. How would you go about doing that as you suggested? Can I add some kind of if statement? while? If I could just have the sketch type in one letter at a time as I move the encoder, that would be great and I can move on to the other challenges of this whole feat. Appreciate it!