DIP Switch with one Analog Input Code

Hello all,

After several projects using DIP switches, with each switch connecting to a single input, I read an interesting article about how to use a resistor ladder and single analog input to read DIP switch settings. Especially when miniaturizing projects with often a limited number of I/O ports, this comes in handy.

First part of this to select your DIP switch and resistors. See attached picture for my particular setup. Once you build this with real components you'll need to run the following code to find your table values:

const int ledPin = 13;
int previousA0Value = 0;
  
void setup()
{
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600); 
}

void loop()
{
  int A0value = analogRead(A0);
  if ( A0value != previousA0Value ) {
    Serial.print("value = "); Serial.println(A0value);
    previousA0Value = A0value;
  }
  digitalWrite(ledPin, HIGH); delay(500);
  digitalWrite(ledPin, LOW); delay(500);
}

If your components are exactly to specification, you will find the following values for all 16 combinations:

    0, 114, 205, 279,    //0,   1,   2,   3 
    341, 394, 438, 477,  //4,   5,   6,   7 
    512, 542, 568, 592,  //8,   9,  10,  11 
    614, 633, 651, 667   //12, 13,  14,  15

This table can then be used in code to find your DIP Switch setting, which then can be used for any other logic statements in your code. This is where I run into a roadblock; it seems I can't get the DIP switch setting to show up on the serial monitor. Here is the code I'm running (please note that I left void loop content to determine table values in the code, but it can be omitted):

const int ledPin = 13;
int previousA0Value = 0;
  
void setup()
{
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}


void loop()
{
  int A0value = analogRead(A0);
  if ( A0value != previousA0Value ) {
    Serial.print("value = "); Serial.println(A0value);
    previousA0Value = A0value;
  }
  digitalWrite(ledPin, HIGH); delay(500);
  digitalWrite(ledPin, LOW); delay(500);
}

byte readIDSwitch(){

  byte id;
  // this table is derived from measuring voltages / resistance
  // from the resistor ladder circuit
  const int id_table[] = {
    0, 114, 205, 279,    //0,   1,   2,   3 
    341, 394, 438, 477,  //4,   5,   6,   7 
    512, 542, 568, 592,  //8,   9,  10,  11 
    614, 633, 651, 667   //12, 13,  14,  15 
  };
  const int epsilon = 10;

  short v = analogRead(A0); //Wherever the signal goes to from resistors
  Serial.println("buttons " + String(v) );
  
  // use a lookup table, because the voltages are not linear
  for (int i = 0 ; i < 16; i++ ) {
    if ( (id_table[i] - epsilon < v) &&
            (v < id_table[i] + epsilon) ) {
        id = i;
        Serial.println("id " + String(id) );
        return id;
    }
  }
  return 0;
}

I'm fairly new at this, and spent about a week on Google and this forum to look for tutorials to no avail. What am I doing wrong?

This is the article I mentioned in my initial post: Creative solutions to your ID circuit

Everything you've shared so far seems fine to me. What problems is it giving you? What's happening that you don't want?

When I run this in ThinkerCAD, I don't get the DIP switch ID to show up in the serial monitor.

I'm expecting for DIP switch setting 0100:
value = 341
button = ? (this was in original code but not sure what it does)
id = 4

I only see "value = 341".

So basically the value "4" is something I could use in any additional logical code. Value "341" might fluctuate in reality due to humidity, temperature, and that is where "4" is what I would like to use for any further logic code.

You define the function readIDswith() but you never call it, so it never runs.

Thank you for your response. I've tried several things to call this but perhaps since I'm not familiar with "byte" it throws me off.

What should I enter where in my code?

Do you mean this function?

byte readIDSwitch(){

This means that the function readIDSwitch() returns a byte value. But as GypsumFantastic says, you never call the function.

What should I change to get a value of 0, 1, 2, 3........ 14, 15?
I don't want a value return from the table because those values are not consistent. Perhaps I should not be using "byte"?

Perhaps there is a better way of coding, but my ultimate goal was to use the setup in my uploaded picture to call different parts of Arduino code. For example; the DIPx4 is set at 0101 (value ~394 returns id = 5). Then this id5 will call for a red LED for instance. (value ~438 equals id = 6 could then be a green LED and so on).

I haven't had success yet trying to accomplish this.

You are dealing with analog, so there will be a range of valid values for each switch or combination of switches.

Here is a short test that you can run that will display the value on A0 from each switch. I used your setup and when I had SW1 on, I get 511 to 513. In the code, I test the range of 500 to 530 = SW1.
I didn't test all switch combinations, but it works on my breadboard.

Your values will be different from mine because I didn't use the exact same resistors as your drawing.

/*
   Sketch to read a value on A0
*/

int val = 0;

void setup() {
  Serial.begin(115200);

}
void loop() {
  val = analogRead(0);
  Serial.print(val);
  Serial.print(", ");

  if (val > 500 && val < 530) Serial.println ("SW1");
  if (val > 330 && val < 345) Serial.println ("SW2");
  if (val > 210 && val < 235) Serial.println ("SW3");
  if (val > 100 && val < 115) Serial.println ("SW4");

  if (val > 610 && val < 620) Serial.println ("SW1 + SW2");
  if (val > 570 && val < 580) Serial.println ("SW1 + SW3");
  if (val > 535 && val < 545) Serial.println ("SW1 + SW4");
  if (val > 440 && val < 450) Serial.println ("SW2 + SW3");
  if (val > 390 && val < 400) Serial.println ("SW2 + SW4");
  if (val > 285 && val < 300) Serial.println ("SW3 + SW4");

  if (val < 100 ) Serial.println ("All SW Off");

  delay (500);
}

In your code you could set a switch value instead of the println() in my sketch, then do a select case on the switch value to execute more code.

WhiskeyTangoFoxtrot:
What should I enter where in my code?

I think probably something like this:

const int ledPin = 13;
int previousA0Value = 0;
  
void setup()
{
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}


void loop()
{
  int A0value = analogRead(A0);
  if ( A0value != previousA0Value ) {
    Serial.print("value = "); Serial.println(A0value);
    previousA0Value = A0value;
    readIDSwitch(); //                                               <----------add this here
  }
  digitalWrite(ledPin, HIGH); delay(500);
  digitalWrite(ledPin, LOW); delay(500);
}

byte readIDSwitch(){

  byte id;
  // this table is derived from measuring voltages / resistance
  // from the resistor ladder circuit
  const int id_table[] = {
    0, 114, 205, 279,    //0,   1,   2,   3 
    341, 394, 438, 477,  //4,   5,   6,   7 
    512, 542, 568, 592,  //8,   9,  10,  11 
    614, 633, 651, 667   //12, 13,  14,  15 
  };
  const int epsilon = 10;

  short v = analogRead(A0); //Wherever the signal goes to from resistors
  Serial.println("buttons " + String(v) );
  
  // use a lookup table, because the voltages are not linear
  for (int i = 0 ; i < 16; i++ ) {
    if ( (id_table[i] - epsilon < v) &&
            (v < id_table[i] + epsilon) ) {
        id = i;
        Serial.println("id " + String(id) );
        return id;
    }
  }
  return 0;
}

I would find the points half way between each pair of numbers and switch on that number, take your first 4 numbers:
(114 - 0) / 2 = 57 + 0 = 57 , (205 - 114) / 2 = 46 + 114 = 160, (279 - 205) / 2 = 37 + 205 = 242, (341 - 279) / 2 = 31 + 279 = 310: Then:

int val;
byte range;
val = analogRead(A0);
if(val < 57)
   range = 0;
else if(val < 160)
           range = 1;
else if(val < 242)
           range = 2;
else if(val < 310)
           range = 3;

And so on, only 1 switch ON at a time, though.

Great suggestions SteveMann, GypsumFantastic & JCA34F, this helped me accomplish this "building block" project!!

I'm intending on using this in various projects. I'll be able to run different code by just changing DIP switch setting. Again, a BIG THANK YOU!!