Multiple Buttons

Using Multiple Buttons to Control Multiple LEDs

Multiple Buttons:

Using Multiple Buttons to Control Multiple LEDs

Introduction

In this project you will create a circuit with three buttons and three LEDs as shown in Fig. 1. When controlling lights, typically you are used to associating a switch with a given light or set of lights—if you change the position of a switch, the same lights always turn off or on. However, in this project the chipKIT™ board comes between the “switches” (buttons) and the lights (LEDs). In this way we can use software (a sketch) to dictate the ways in which the buttons control the LEDs. The interaction of lights and switches is no longer “hardwired” (i.e., no longer a direct result of the physical connections in the circuit). Instead, if we want to change the way the buttons control the lights, we can simply change the sketch. To accomplish what is needed for this project, we must further explore the “condition” on which if-statements make decisions. Specifically, we will consider logical expressions using and and or. Along the way we will also consider how we can specify the behavior of a circuit using a truth table.

Figure 1. Button-controlled LED circuit with three buttons and three LEDs.
Before you begin, you should:
  • Understand how a button can be used to control an LED as described in the Button Controlled LED project.
  • Understand basic if-statements and the use of comparison operators.
After you're done, you should:
  • Understand logical expressions, i.e., the use of and (&&) and or (||).
  • Understand truth tables.
  • Understand the way software can be use to make hardware behave in different ways.

Inventory:

Qty Description Typical Image Schematic Symbol Breadboard Image
3 Two-port buttons
3 LEDs
3 220 Ω resistors
3 10 kΩ resistors

(10 kΩ = 10,000 Ω)

Step 1: Setting up the LEDs

  1. Place the three LEDs in the breadboard as shown in Fig. 2, keeping in mind that the anode is the longer of the two “legs” and the cathode is the shorter. The cathodes in Fig. 2 are to the left and anodes are to the right. Going from left to right, we'll identify these as LED A, LED B, and LED C.
    Figure 2. LED configuration.
  2. Using three jumper wires, connect the anode of LED A to pin 12, the anode of LED B to pin 11, and the anode of LED C to pin 8. (These wires are shown in blue in Fig. 2.)
  3. Connect the chipKIT ground pin (labeled “GND”) to a rail at the bottom of the breadboard. (This is the long black wire in Fig. 2. There is also a ground pin at the top of the chipKIT Uno32™ that is labeled “GND”. All the ground pins are equivalent so you are free to use whichever one is most convenient.) For the remainder of this project, the bus strip connected to the ground pin will be referred to as the ground bus strip.
  4. Attach one end of a 220 Ω resistor to the cathode of each of the LEDs. (Resistors behave the same way regardless of their orientation.) The other end of each resistor should be placed so that it connects to the ground bus strip. This can be done by placing the end of the resistor directly into the bus strip. Alternatively, as shown in Fig. 2, you can insert the other end of the resistor into a different column (node) in the breadboard and then use a wire to connect from that column to the ground bus strip. (These connections are formed by the short black wires in Fig. 2.)

Step 2: Setting up the Buttons

  1. As shown in Fig. 3, connect the chipKIT 3.3V pin to the bus strip (rail) above the ground bus strip that was set up in Step 1. (This connection is formed by the long red wire in Fig. 3. For the sake of simplicity, the LEDs are not shown in Fig. 3.)
    Figure 3. Button configuration.
  2. Place three buttons into the breadboard as shown in Fig. 3. Going from left to right, we will label these as button A, button B, and button C. Note the orientation of each button: one pair of legs is inserted to one side of the “valley” and the other pair is inserted on the other side. Most breadboards have a gap or a valley that separates columns in the main section of the board. Even if your breadboard does not have the aforementioned valley, the circuit will still be functional provided that you retain this button orientation.
  3. Using three wires, connect the right side of each button to the 3.3V bus strip. (These connections are formed by the short red wires in Fig. 3.)
  4. Using three more wires, connect chipKIT digital I/O pin 7 to the left side of button A, pin 6 to the left side of button B, and pin 5 to left side of button C. These connections are formed by the yellow wires in Fig. 3. These wires will communicate the “state” of the buttons to the chipKIT pins that we will configure for input.
  5. For each button, connect one end of a 10 kΩ resistor to the left side of the button. (These resistors act as pull-down resistors in this circuit.)
  6. Connect the other end of each resistor to the ground bus strip. You can use a wire to connect from the resistor to the bus strip, as shown in Fig. 3, or you can directly connect the end of the resistor to the bus strip. (These connections are formed by the shorter black wires in Fig. 3.)

When a button is pressed, it supplies 3.3V to the corresponding digital I/O pin. The chipKIT Max32™ and Uno32 boards are designed to recognize a range of voltages—from a minimum of 2.4V to a maximum of 5.5V—as a HIGH input for the digital I/O pins. That is, when the function digitalRead() is called, it returns HIGH if the voltage on the pin being read is within this range. If the voltage is below 2.4V, the digitalRead() function returns LOW. The digitalRead() function will be discussed later in this project.

Complete Circuit

At this point, you should now have all the buttons and LEDs in the breadboard as shown in Fig. 4. The next step is to write the sketch that controls the circuit. Because this sketch uses if statements and comparison operators, we will first introduce those programming constructors.

Figure 4. Complete circuit with three buttons and three LEDs.

The schematic diagram of the circuit is shown in Fig. 4.

Figure 5. Schematic representation of circuit.

A PDF version of this schematic is available here.

Circuit Specification: Truth Tables.

We now need to specify how the three buttons will control the three LEDs. The most “obvious” approach is almost undoubtedly to have button A control LED A, button B control LED B, and button C control LED C. In other words, if button A is pressed, then LED A is illuminated regardless of the state of the other buttons. Let's get a bit more complicated than that.

In our circuit we have three buttons and thus there eight ($2^3=8$) possible combination of button states. We have the freedom to specify what each combination of inputs translates to in terms of illuminating the LEDs. Let us express the relationship between the “inputs” (button states) and the “outputs” (LED states) in the form of a truth table. A truth table shows all the possible input states and the output states that correspond to those input states—each row of the table corresponds to a particular combination of inputs and the corresponding output for those inputs. We will use a 0 (zero) to indicate that a button is not pushed and a 1 to indicate that it is pushed. If truth tables are new to you, please click on the tab on the right to learn more about them.

First, before creating the truth table, let's describe the behavior of the circuit in narrative form. If one button is pushed, the corresponding LED should be illuminated. If two buttons are pushed, the LED corresponding to the non-pushed button should be illuminated. And, finally, if none of the buttons are pushed or all of the buttons are pushed, then none of the LEDs should be illuminated. The truth table corresponding to this system is (outputs are indicated with a yellow background):

BTN A BTN B BTN C LED A LED B LED C
0 0 0 000
0 0 1 001
0 1 0 010
0 1 1 100
1 0 0 100
1 0 1 010
1 1 0 001
1 11 000

Note the conditions that cause LED A to illuminate. In English we might say, “Illuminate LED A if button A is pushed and buttons B and C are not pushed, or illuminated it if button A is not pushed and buttons B and C are pushed. Otherwise, do not illuminate LED A.” Now the questions becomes: How do we translate this statement into C/C++? To do so, we need to use logical operators which are the subject of the next section.

Logical Operators and Compound Expressions

Sometimes we need to check if two conditions are simultaneously true. Conversely, sometimes we need to check if one or another condition is true. We can use the logical “and” operator (written &&) to test if two conditions are simultaneously true. If we want to test if one or another condition is true, i.e., test if either condition is true, we can use the “or” operator (written ||). If these operators are new to you, please read more about them via the tab on the right.

For example, assuming that the pins btnPinA and btnPinB have been properly initialized to correspond to the states of button A and button B, respectively, the following expression will evaluate to true if both buttons are pressed:

		        digitalRead(btnPinA) == HIGH && digitalRead(btnPinB) == HIGH
	        

Read “&&” as “and.” Thus, this expression essentially evaluates to the answer of: is button A pressed and button b pressed? Or, said another way, are both button A and B pressed? This expression would be used, for instance, as the condition in an if-statement if we wanted to execute a particular block of code when both buttons are pressed.

In contrast, if we wanted to execute a particular block of code when one or the other button is pressed (or both are pressed), the expression that would be used as the condition in an if-statement would be:

		        digitalRead(btnPinA) == HIGH || digitalRead(btnPinB) == HIGH
	        

Read “||” as “or.” This expression evaluates to true if either or both buttons are pressed.

We can construct logical expressions that involve multiple “and” and “or” operations to form compound logical expressions. More details concerning such expressions can be found via the tab on the right. As an example, assume we want to check if button A is pressed or buttons B and C are pressed (so, we want to obtain true if A is pressed [independent of B and C] or if both B and C are pressed [independent of A]). The appopriate expression is:

		        digitalRead(btnPinA) == HIGH || digitalRead(btnPinB) == HIGH && digitalRead(btnPinC) == HIGH
	        

This expression relies on the fact that the “and” operator has higher precedence than the “or” operator. If we are unsure of the order of precedence, we can always add parentheses. The following expression is equivalent to the one above.

		        digitalRead(btnPinA) == HIGH || (digitalRead(btnPinB) == HIGH && digitalRead(btnPinC) == HIGH)
	        

If we wanted the expression to evaluate to true if either button A or B were pressed while, at the same time, button C were pressed, we would need to use parentheses as follows:

		        (digitalRead(btnPinA) == HIGH || (digitalRead(btnPinB) == HIGH) && digitalRead(btnPinC) == HIGH
	        

Provided you understand these logical constructs, you now have all the software tools at your disposal to implement the entire sketch.

Step 3: Writing the Sketch

The following sketch implements the control system described above, where the push of a single button will illuminate the corresponding LED, but the push of two buttons will illuminate the LED corresponding to the unpushed button. There are a couple of things you should note about this sketch. First, all the variables are declared const (i.e., to be constant). This seems okay for the variables that correspond to the input and output pins, but declaring the button states as const may seem incorrect because clearly the button states can change. However, for each execution of the loop() function, the button states are read once and do not change throughout the execution of the function. When the function has completed execution, the variables are “discarded” and read in anew when the function is executed again. The other thing to note is that in this implementation we check if all the buttons are pressed or none of the buttons are pressed. You should ask yourself: Given the way the rest of the sketch is written, is this truly necessary? (The answer is no, it isn't truly necessary! But there is no harm in doing things this way. In fact, one could actually make arguments that this is the best way to implement the controller if the typical state is to have all or none of the buttons pressed—this implementation would then typically have fewer conditions to test for each execution of the loop() function.)

                  /* The input from three buttons is used to control the
                   * the illumination of three LEDs.  If a single button
                   * is pushed, the associated LED is illuminated.  If two
                   * buttons are pushed, only the LED associated with the
                   * unpushed LED is illuminated.  If all of the buttons
                   * are pushed or if none of the buttons are pushed, then
                   * none of the LEDs are illuminated.
                   */
                  
                  const int btnPinA = 7;
                  const int btnPinB = 6;
                  const int btnPinC = 5;
                  
                  const int ledPinA = 12;
                  const int ledPinB = 11;
                  const int ledPinC = 8;
                  
                  void setup() {
                    // Configure the buttons for input.
                    pinMode(btnPinA, INPUT);
                    pinMode(btnPinB, INPUT);
                    pinMode(btnPinC, INPUT);
                    
                    // Configure the LEDs for output.   
                    pinMode(ledPinA, OUTPUT);
                    pinMode(ledPinB, OUTPUT);
                    pinMode(ledPinC, OUTPUT);
                  }
                  
                  void loop() {
                    // Obtain the states of all three buttons.
                    const int btnStateA = digitalRead(btnPinA);
                    const int btnStateB = digitalRead(btnPinB);
                    const int btnStateC = digitalRead(btnPinC);
                    
                    // If all or none of the buttons are pushed,
                    // then all the LEDs should be off.
                    if ((btnStateA == LOW  && btnStateB == LOW  && btnStateC == LOW) ||
                        (btnStateA == HIGH && btnStateB == HIGH && btnStateC == HIGH)) {
                      digitalWrite(ledPinA, LOW);
                      digitalWrite(ledPinB, LOW);
                      digitalWrite(ledPinC, LOW);
                    } 
                    else {
                      // Determine whether or not LED A should be on.
                      if ((btnStateA == LOW  && btnStateB == HIGH && btnStateC == HIGH) ||
                          (btnStateA == HIGH && btnStateB == LOW  && btnStateC == LOW)) {
                        digitalWrite(ledPinA, HIGH);
                      } 
                      else {
                        digitalWrite(ledPinA, LOW);
                      }
                              
                      // Determine whether or not LED B should be on.
                      if ((btnStateA == HIGH && btnStateB == LOW  && btnStateC == HIGH) ||
                          (btnStateA == LOW  && btnStateB == HIGH && btnStateC == LOW)) {
                        digitalWrite(ledPinB, HIGH);
                      }
                      else {
                        digitalWrite(ledPinB, LOW);
                      }
                              
                      // Determine whether or not LED C should be on.
                      if ((btnStateA == HIGH && btnStateB == HIGH && btnStateC == LOW) ||
                          (btnStateA == LOW  && btnStateB == LOW  && btnStateC == HIGH)) {
                        digitalWrite(ledPinC, HIGH);
                      } 
                      else {
                        digitalWrite(ledPinC, LOW);
                      }
                    }
                  }

  • Other product and company names mentioned herein are trademarks or trade names of their respective companies. © 2014 Digilent Inc. All rights reserved.
  • Circuit and breadboard images were created using Fritzing.