Finger switch recognize if going up or down

so i dont have a rotary encoder, but I do have a finger switch:

that goes from 0 to 9. the following code workes out what number the user is on:

int readSwitch()
{
  int total=0;
  if (digitalRead(q1)==HIGH) { total+=1; }
  if (digitalRead(q2)==HIGH) { total+=2; }
  if (digitalRead(q4)==HIGH) { total+=4; }
  if (digitalRead(q8)==HIGH) { total+=8; }
  return total;
}

but I want to use it like a rotary encoder, I know its not really designed for that but I feel it can be done, i just cant crack it!
I.e if new number > last number, going one way, otherwise going the other way. Except when I go from 0 to 9, or 9 to 0. Is the only way to use if's?
Any ideas greatly appreciated.
Thanks

Is the only way to use if's?

Yes.

int readSwitch()
{
  return 
    digitalRead(q1) +
    digitalRead(q2)<<1 +
    digitalRead(q4)<<2 +
    digitalRead(q8)<<3;
}

Then you can compare the new value to the old. Unfortunately you need to compare modulo 10:

int switchChange()
    {
    static int oldValue = 0;

    int newValue = readSwitch();
    if (newVale == oldValue)
        return 0;

    int returnValue = newValue - oldValue;

    if (returnValue < -6)
        returnValue += 10;  // Switch probably wrapped around from 9 to 0
    else
    if (returnValue > 6)
        returnValue -= 10;  // Switch probably wrapped from 0 to 9;

    oldValue = newValue;
   return returnValue;
   ]

Cheers for your answer. Im just trying to read normally from it at the moment, and I am getting 15 on my lcd (8+4+2+1). By checking on my multimeter, all four pins are getting 5 volts. i just wondered whether this was norm, because if I move the switch the number doesnt change from 15. i.e do I have a short, or is my wiring wrong. I have been going by this scematic:


My code is:

#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
Adafruit_PCD8544 display = Adafruit_PCD8544(52, 51, 50, 48, 49);
#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2
#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 
static unsigned char __attribute__ ((progmem)) logo16_glcd_bmp[]={
  0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0xf8, 0xbe, 0x9f, 0xff, 0xf8, 0xc0, 0xc0, 0xc0, 0x80, 0x00, 
  0x20, 0x3c, 0x3f, 0x3f, 0x1f, 0x19, 0x1f, 0x7b, 0xfb, 0xfe, 0xfe, 0x07, 0x07, 0x07, 0x03, 0x00, };

#define q1 38
#define q2 39
#define q4 40
#define q8 41

void setup() 
{ 
  display.begin();
  display.setContrast(40);
  display.clearDisplay();   // clears the screen and buffer
  // text display tests
  display.setTextSize(2);
  display.setTextColor(BLACK);
  display.setCursor(0,10);
  display.println("Hello World");
  display.display();
  delay(1000);

  pinMode(q1, INPUT); // thumbwheel '1'
  pinMode(q2, INPUT); // thumbwheel '2'
  pinMode(q4, INPUT); // thumbwheel '4'
  pinMode(q8, INPUT); // thumbwheel '8'  
} 
 

int readSwitch()
{
  int total=0;
  if (digitalRead(q1)==HIGH) { total+=1; }
  if (digitalRead(q2)==HIGH) { total+=2; }
  if (digitalRead(q4)==HIGH) { total+=4; }
  if (digitalRead(q8)==HIGH) { total+=8; }
  return total;
}

void loop()
{
  //dispSAA1064(readSwitch()); // sends switch value to display shield
  display.clearDisplay();
  display.println(readSwitch()); // sends switch value to serial monitor box
  display.display();
  delay(200);
}

Thanks

If your switch is reliably producing numbers from 0 to 16 you can use a table of structures to get the direction. In fact you don't care about all the values, just use the 2 LSBs.

EDIT: Maybe something like this

typedef struct {
   byte newVal;
   byte lastVal;
   byte dir;
} encoder;

#define UP 1
#define DN -1

byte lastVal = 0;
byte direction;

encoder x[8] = {
	{0, 1, UP},
	{0, 3, DN},
	{1, 2, UP},
	{1, 0, DN},
	{2, 3, UP},
	{2, 1, DN},
	{3, 0, UP},
	{3, 2, DN}
};

void setup() {
	
}

byte getSwitchVal() {};

void loop() {
   byte newVal = getSwitchVal();
   
   for (int i; i < 4; i++) {
     if ((x[i].newVal == newVal) && (x[i].lastVal == lastVal)) {
        direction = x[i].dir;
        break;
     }
   }
   lastVal = newVal;
  
  
}

Rob

Im getting there with the logic, but still havent nailed it. I couldn't quite understand your code Johnwasser, and it wasnt working quite correctly, so i tried to do it using a more stupid way:

int readSwitch()
{
  int total=0;
  if (digitalRead(q1)==HIGH) { total+=1; }
  if (digitalRead(q2)==HIGH) { total+=2; }
  if (digitalRead(q4)==HIGH) { total+=4; }
  if (digitalRead(q8)==HIGH) { total+=8; }
  return total;
}
void switchChange()
    {
    display.clearDisplay();
    static int oldValue = 0;
    int newValue = readSwitch();
    if (newValue == oldValue)
        display.println("No Change");
    if (newValue > oldValue && newValue <= 9 && newValue >)
      display.println("increase");
    if (newValue < oldValue && newValue >= 0)
      display.println("Decrease");  
    if (newValue > 9){
      display.println("Increase (>9)");
      oldValue = 0;
    }
    if (newValue < 0){
      display.println("Decrease (<0)");
      oldValue = 9;
    }
display.display();
oldValue = newValue;
    }

It still has issues when crossing the 0-9 and 9-0 line in terms of an increase or a decrease.

Sorry Rob, didnt see your code there should getSwitchVal just be:

byte getSwitchVal()
{
  int total=0;
  if (digitalRead(q1)==HIGH) { total+=1; }
  if (digitalRead(q2)==HIGH) { total+=2; }
  if (digitalRead(q4)==HIGH) { total+=4; }
  if (digitalRead(q8)==HIGH) { total+=8; }
  return total;
}

?

    if (newValue > oldValue && newValue <= 9 && newValue >)

Does this even compile? And newValue greater than what?

soz, I thought I had removed that last condition! Must have been an Ctr-Z moment!
I was running it with just the condiiton:

 if (newValue > oldValue && newValue <= 9)

How can newValue not be less than or equal to 9?

I think I'd take care of the limit conditions, first. If newValue is 9, do something. Else, if it is 0, do something else. Else, do a third thing.

should getSwitchVal just be:

Yes, except you don't need q4 and q8.

EDIT: Possibly easier with an array.


Rob

Here's a version that uses an array

#define UP 1
#define DN 2

byte lastVal = 0;
byte direction;

byte x[16] = {
  /*New	Last 	Dir */
  /*0, 	0*/  -1,	// 0	-1 = invalid combination
  /*0, 	1*/  DN,	// 1
  /*0, 	2*/  -1,	// 2
  /*0, 	3*/  -1,	// 3
  /*1, 	0*/  UP,	// 4
  /*1, 	1*/  -1,	// 5
  /*1, 	2*/  DN,	// 6
  /*1, 	3*/  -1,	// 7
  /*2, 	0*/  -1,	// 8
  /*2, 	1*/  UP,	// 9
  /*2, 	2*/  -1,	// 10
  /*2, 	3*/  DN,	// 11
  /*3, 	0*/  -1,	// 12
  /*3, 	1*/  -1,	// 13
  /*3, 	2*/  UP,	// 14
  /*3, 	3*/  -1	// 15

};

void setup() {

}

byte getSwitchVal() {
  int total=0;
  if (digitalRead(q1)==HIGH) { total+=1; }
  if (digitalRead(q2)==HIGH) { total+=2; }
  return total;
};

void loop() {
  byte newVal = (getSwitchVal() & 3);

  direction = x[(newVal << 2) | lastVal];
  lastVal = newVal;

}

I hope I got the values right, I admit it was doing my head in for a while and it's real late here.


Rob

a.mlw.walker:
I am getting 15 on my lcd (8+4+2+1). By checking on my multimeter, all four pins are getting 5 volts. i just wondered whether this was norm, because if I move the switch the number doesnt change from 15.

Sounds like your switch is not wired correctly. I assume the switch has a 'common' pin an four data pins. What is the common pin connected to?

You have two choices:

  1. Connect common to Ground and connect each data pin through a pull-up resistor to +5. You can use the internal pull-up resistors: pinMode(dataPin, INPUT); digitalWrite(dataPin, HIGH);

  2. Connect common to +5 and connect each data pin through a pull-down resistor to Ground.

Choice 1 with the internal pull-ups uses fewer parts but gives a '1' for open and a '0' for closed. You can reverse the logic in the program by using the logical invert operator '!':

int readSwitch()
{
  return 
    (!digitalRead(q1)) +
    (!digitalRead(q2))<<1 +
    (!digitalRead(q4))<<2 +
    (!digitalRead(q8))<<3;
}

I got something working with improved logic on my earlier post. You array ways sound like they are the better way to do it, purely because i think writing out the logic using ifs/else ifs seems amateur?
Yeah i have wired it common - 5V, my ground line had popped out of the arduino and i hadn't spotted it ..

a.mlw.walker:
i have wired it common - 5V

Do you have the four pull-down resistors? It won't work correctly without them.

array ways sound like they are the better way to do it,

Every time you see more than a few if/elses there's probably a better way to do it. That array version is not intuitive I admit but it uses a single line of code and is fast. It's also deterministic, no matter what the values it takes the same time.

For this simple project it doesn't matter but I thought I'd try to get you started thinking about alternative ways to do things.


Rob

No completely, im always up for learning the "better" way. Often i find myself starring at if else's and thinking how can this be done better. Usually i go about it the stupid way and then study the better way to work out what can be improved. I need to call other functions based on the outcome so hopefully i can see where I should do that...
(yeah i have the resistors..)
I'll look over your code and "re-energize" this thread when I've got another question :D.
Cheers guys

I need to call other functions based on the outcome so hopefully i can see where I should do that...

Look up "array of function pointers".

The array I had can be an array of function pointers instead of just bytes.


Rob