Introduction: DIP Tune Selector Using 1 Pin

A while back I worked on a "music box" project that needed to choose between as many as 10 different tune snippets. A natural choice for picking a specific tune was a 4 pin dip switch since 4 switches provides 24=16 different settings. However, the brute force implementation for this approach requires 4 device pins, one for each switch. Since I was planning to use the ATtiny85 for development, loss of 4 pins was a bit too much. Fortunately, I ran into an article which describes an ingenious method for using 1 analog pin to handle multiple switch inputs.

The multi-switch;1-input technique uses a Voltage Divider circuit to provide a unique integer value for each of the 16 possible switch setting combinations. This set of 16 integer identifiers is then used in the application program to associate an action with a setting.

This instructable employs the multi-switch method to implement tune selection for the music box application. The selected melody is then played through a piezo buzzer using the Arduino tone function.

Step 1: Required Hardware

Use of the UNO as the implementation platform minimizes the number of required hardware components. Implementation of the multi-switch input method requires only a 4-pin dip switch, the 5 resistors used for the voltage divider, and hookup wire for connections. A piezo buzzer is added to the configuration for implementation of the music box tune selector. Optionally, depending on the type of dip switch used, it is helpful to use a 2x4 8 pin socket to connect the dip switch to the breadboard since the standard dip switch pins seem to made for soldering to a perfboard not plugging directly into a breadboard. The socket stabilizes the dip switch connections and keeps the switch from being easily lifted when setting the toggle switches.

NamePossible SourceHow Used
4-pin dip switch
Tune selection
2x4 pin socket (Optional)Amazon
The posts on most dip switches do not hold the switch very well in a breadboard. A socket helps to make the connection more solid. An alternative is to find a dip switch that is truly made for breadboard use with regular IC pins.


  • 10K x2
  • 20K
  • 40K
  • 80K

Implement voltage divider
passive piezo buzzerAmazon Play melody as driven by the application via the Arduino tone function

Step 2: Multi-switch Method Explanation

This section discusses the underlying concepts for the multi-switch method and develops the equations required for stand-alone calculation of unique identifiers for each of the 16 possible dip switch setting configurations. These identifiers can then be used in an application program to associate a switch configuration with an action. For example, you might want the setting - switch 1 on, switch 2 off, switch 3 off, switch 4 off (1,0,0,0) - to play Amazing Grace and (0,1,0,0) to play Lion Sleeps Tonight. For brevity and conciseness the configuration identifiers are referred to as comparators in the remainder of the document.

The foundational concept for the multi-switch method is the Voltage Divider circuit which consists of 2 in series resistors connected to an input voltage. The output voltage lead is connected between the resistors, R1 and R2, as shown above. The divider output voltage is calculated as the input voltage multiplied by the ratio of resistor R2 to the sum of R1 and R2 (equation 1). This ratio is always less than 1 so the output voltage is always smaller than the input voltage.

As indicated in the design diagram above the multi-switch is configured as a voltage divider with R2 fixed and R1 equal to the composite/equivalent resistance for the 4 dip switch resistors. The value of R1 depends on which dip switches are turned on and, therefore, contributing to the composite resistance. Since the dip switch resistors are in parallel, the equivalent resistance calculation equation is stated in terms of the reciprocals of the component resistors. For our configuration and the case that all switches are turned on, the equation becomes

1/R1 = 1/80000 + 1/40000 + 1/20000 + 1/10000

giving R1 = 5333.33 volts. To account for the fact that most settings have at least one of the switches turned off, the switch state is used as a multiplier:

1/R1 = s1*1/80000 + s2*1/40000 + s3*1/20000 + s4*1/10000 (2)

where the state multiplier, si, is equal to 1 if the switch is turned on and equal to 0 if the switch is turned off. R1 can now be used to calculate the resistance ratio needed in equation 1. Using the case where all switches are on as the example again

RATIO = R2/(R1+R2) = 10000/(5333.33+10000) = .6522

The last step in calculation of the predicted comparator value is multiplication of the RATIO by 1023 in order to emulate the effect of the analogRead function. The identifier for the case where all switches are on is then

comparator15 = 1023*.6522 = 667

All the equations are now in place for calculation of identifiers for the 16 possible switch settings. To summarize:

  1. R1 is calculated using equation 2
  2. R1 and R2 are used to calculate the associated resistance RATIO
  3. the RATIO is multiplied by 1023 to obtain the comparator value
  4. optionally, the predicted output voltage can also be calculated as RATIO*Vin

The set of comparators depends only on the resistor values used for the voltage divider and are a unique signature for the configuration. Because the divider output voltages will fluctuate from run to run (and read to read), unique in this context means that while two sets of identifiers may not be exactly the same they are close enough that the component comparator differences fall within a small pre-specified interval. The interval size parameter must be chosen large enough to account for expected fluctuations but small enough that different switch settings don't overlap. Usually 7 works well for the interval half-width.

A set of comparators for a particular configuration can be obtained by several methods - run the demo program and record the values for each setting; use the spreadsheet in the next section to calculate; copy an existing set. As noted above all sets will most likely be slightly different but should work. I suggest using the method author's set of identifiers for the multi-switch setup and the spreadsheet from the next section if any of the resistors are changed significantly or more resistors added.

The following demo program illustrates use of the comparators to identify the current dip switch setting. In each program cycle an analogRead is performed to obtain an identifier for the current configuration. This identifier is then compared across the comparator list until a match is found or the list exhausted. If a match is found an output message is issued for verification; if not found a warning is issued. A 3 second delay is inserted in the loop so that the serial output window will not be overwhelmed with messages and to give some time to reset the dip switch configuration.

// Demo program to read the voltage divider output and use it to identify the
// current dip switch configuration by looking the output value up in an array of
// comparion values for each possible setting.  The values in the lookup array can
// either be obtained from a previous run for the configuration or through calculation
// based on the underlying equations.
int comparator[16] = {0,111,203,276,339,393,434,478,510,542,567,590,614,632,651,667};

// Define processing variables
int  dipPin   = A0;     // analog pin for voltage divider input
int  dipIn    = 0;      // holds divider voltage output translated by analogRead
int  count    = 0;      // loop counter
int  epsilon  = 7;      // comparison interval half-width
bool dipFound = false;  // true if current voltage divider output found in look up table

void setup() 
  pinMode(dipPin, INPUT);       // configure voltage divider pin as an INPUT
  Serial.begin(9600);           // enable serial communication

void loop() 
  delay(3000);        // keep output from scrolling too fast

  // Initialize lookup parameters
  count    = 0;
  dipFound = false;
  // Read and document current output voltage
  dipIn = analogRead(dipPin);
  Serial.print("divider output ");

  // Search comparator list for current value
  while((count < 16) && (!dipFound))      
    if(abs(dipIn - comparator[count]) <= epsilon)
      // found it
      dipFound = true;
      Serial.print(" found at entry ");
      Serial.println(" value " + String(comparator[count]));

    // value not in table; shouldn't happen
    Serial.println("  OOPS! Not found; better call Ghost Busters");

Step 3: Comparator Spreadsheet

The calculations for the 16 comparator values are given in the spreadsheet shown above. The accompanying excel file is available for download at the bottom of this section.

Spreadsheet columns A-D record the dip switch resistor values and the 16 possible switch settings. Please note that the hardware DIP switch shown in the fritzing design diagram is actually numbered from left to right instead of the right to left numbering shown in the spreadsheet. I found this somewhat confusing but the alternative doesn't put the "1" configuration (0,0,0,1) at the first of the list. Column E uses formula 2 of the previous section to calculate the Voltage Divider equivalent resistance R1 for the setting. Column F uses this result to calculate the associated resistance RATIO, and, finally, Column G multiplies the RATIO by the analogRead max value (1023) to obtain the predicted comparator value. The final 2 columns contain the actual values from a run of the demo program along with the differences between the predicted and actual values.

The previous section mentioned three methods to obtain a set of comparator values including extension of this spreadsheet if the resistor values are significantly changed or more switches are added. It appears that small differences in the resistor values do not significantly affect the final results (which is good since resistor specifications give a tolerance, say 5%, and the resistor is rarely equal to its actual stated value).

Step 4: Play a Tune

To illustrate how the multi-switch technique might be used in an application, the comparison demo program from the "Method Explanation" section is modified to implement the tune selection processing for the music box program. The updated application configuration is shown above. The only addition to the hardware is a passive piezo buzzer to play the selected tune. The basic change to the software is addition of a routine to play a tune, once identified, using the buzzer and the Arduino tone routine.

The available tune snippets are contained in a header file, Tunes.h, along with definition of the necessary support structures. Each tune is defined as an array of note related structures containing the note frequency and duration. The note frequencies are contained in a separate header file, Pitches.h. The program and header files are available for download at the end of this section. All three files should be placed in the same directory.

Selection and identification proceeds as follows:

  1. The "user" sets the dip switches in the configuration associated with the desired tune
  2. every program loop cycle the identifier for the current dip switch setting is obtained via analogRead
  3. The step 2 configuration identifier is compared against each of comparators in the available tune list
  4. If a match is found the playTune routine is called with the information needed to access the tune note list
    • Using the Arduino tone function each note is played through the buzzer
  5. If no match is found, no action is taken
  6. repeat 1-5

DIP switch settings for the available tunes are shown in the table below where 1 means switch is on, 0 switch off. Recall that the way the dip switch is oriented places switch 1 in the left-most position (the one associated with the 80K resistor).

Switch 1
Switch 2
Switch 3
Switch 4
Danny Boy1000
Little Bear0100
Lion Sleeps Tonight1100
Nobody Knows the Trouble0010
Amazing Grace0001
Blank Space1001
MockingBird Hill1011

The sound quality from a piezo buzzer is certainly not great but it is at least recognizable. In fact if the tones are measured, they are very close to the notes exact frequency. One interesting technique used in the program is to store the tune data in the flash/program memory section instead of the default data memory section by using the PROGMEM directive. The data section holds the program processing variables and is much smaller, around 512 bytes for some of the ATtiny microcontrollers.

Microcontroller Contest

Participated in the
Microcontroller Contest