Implementing Multiple Mixed Switch Types By Polling

Implementing Multiple Mixed Switch Types By Polling

Implementing switches can be troublesome. This article offers a solution for configuring many mixed type switches at the same time.

  • 1,016 views
  • 0 comments
  • 2 respects

Components and supplies

Ph a000066 iso (1) ztbmubhmho
Arduino UNO
×1
19c7994 40
Tactile Switch, Top Actuated
example sketch is configured for 3 x button and 3 x toggle switches, but this is arbitrary
×3
19c6722 40
Toggle Switch, Toggle
example sketch is configured for 3 x button and 3 x toggle switches, but this is arbitrary
×3
11026 02
Jumper wires (generic)
×1
Mfr 25frf52 10k sml
Resistor 10k ohm
three switches are configured with 10k ohm pull down resistors
×3

Apps and online services

About this project

Implementing Multiple Mixed Switch Types

Overview

This article offers a highly configurable approach to implementing multiple switches of type button and/or toggle in a simple, direct but reliable way and using a technique of switch 'polling'. (For the purpose of the article 'toggle' switches will also include slide, tilt or rotary style switches.)

The article builds on the approach and techniques laid out in previous publications (a tutorial), namely Understanding & Using Button Switches and the article Toggle Switches, Reliable Reading.

The background and basis to the methods offered here are largely those put forward in that tutorial and article. If it is that you wish for a deeper understanding in the theory and implementation of simple button/toggle switches, then please do refer to this tutorial and article.

The example sketch at the centre of this article is capable of working with multiple mixed switch types (button and/or toggle), wired in either or a mix of commonly seen schemes - circuits incorporating pull down 10k ohm resistors (see figure 1) or without (see figures 1 and 2).

The design of the sketch is such that switches, either style, may be wired in either way or a mixture of both - there is no restriction. This flexibility is achieved through data configuration, with each switch to be implemented being defined in accordance with the developer's needs. The sketch's switch reading functionality is controlled and directed in accordance with the switch definition data.

In summary, the two commonly seen button switch wiring schemes are:

When implementing the sketch it essential to decide which switch wiring scheme is used for each style of switch you wish to connect as this can have a fundamental bearing and affect on how the inputs are interpreted for switch ON and OFF. If you are unsure then have a look at the tutorial mentioned above, that is Understanding & Using Button Switches.

How Does It Work?

The sketch is able to support many switches each connected to a digital pin. The number of switches that can be connected in this way is therefore determined by the architecture of the microcontroller used. For instance, the Arduino Mega 2560 will support literally dozens of direct digital switch connections - enough for a mock up of the Star Ship Enterprise bridge! However, 'out-of-the-box' (OOTB), the example sketch is configured for just six switches, three button and three toggle, so my UNO is a good choice with headroom to spare.

The example OOTB sketch has been preconfigured for six switches -

  • 2 x button switches wired for circuit_C1,
  • 1 x button switch wired for circuit_C2,
  • 2 x toggle switches wired for circuit_C2,
  • 1 x toggle switch wired for circuit_C1.

So, if we wish to add/remove switches or change switch circuit schemes, we simply need to make changes to the sketch's preset switch control data and, of course, include user defined code which is performed when a switch is activated.

Once configured, the sketch's main loop will poll each declared switch in turn in accordance with its switch control data. If a switch activation is detected, allowing for debounce, the switch read function returns a 'switched' status result which is then actioned by a corresponding main loop switch-case statement for that switch.

That's it, all very clear, but let's see how we can vary things.

Steps to Implementation

Implementation of the sketch is reasonably straight forward and can be directly worked out by reference to and inspection of the OOTB sketch. However, the steps to be followed will be:

  • decide how many switches and of what type you wish to configure.
  • for each switch, decide how it should be wired - either as 'circuit_C1' or 'circuit_C2'.
  • wire up the switches according to the above choices and connect to the microcontroller.
  • load up the sketch.
  • update the macro definition '#define num_switches' to be the total number of switches you are connecting.
  • update the macro definition list 'Switch to Pin Macro Definition List' for the switches you are connecting, one entry for each switch, suitably defined with a digital pin number etc. For ease of reading, follow the existing naming structure- see example OOTB sketch.
  • now add preset data items for each switch in the 'Switch Control Sruct(ure) Declaration' ('switch_control'), but note that preset data entries for button and toggle switches are a little different, as follows -

For button type switches the presets per buttonswitch are-

For toggle type switches the presets per toggleswitch are-

  • add your switch control code to the polling loop section in the main loop, add/remove switch-case segments as necessary.

OOTB Switch Preset Data

Number of Wired Switches-

Switch to Pin Macro Definition List-

Switch Control Sruct(ure) Declaration, preset data-

* NOTE: to improve readability and provide a degree of self documentation, digital pins are defined by a 'Switch to Pin Macro Definition List'. This helps the readability of the switch control preset data as, rather than see, for instance, a pin value of 12, we might define this pin as being 'button_switch_3' and declaring as this macro definition. That is, '#define button_switch_3 12'.

Main Loop Polling Segment -

  • Finally, for each switch, decide what you want it to do - see the main loop switch-case section under the polling for loop.

Example

I wish to add a seventh button switch to the existing six switch OOTB sketch configuration and for this to be on digital pin 10 and configured as circuit_C1 (with a 10k ohm pull down resistor). The declarative macro/variable/code changes to the sketch would be:

  • define the number of button switches configured as '#define num_switches 7'.
  • add a new button switch to digital pin definition to the 'Switch to Pin Macro Definition List'. That is, '#define button_switch_7 10'.
  • add new preset data to the 'Switch Control Sruct(ure) Declaration' ('switch_control') for the new button switch. That is,
'button_switch, button_switch_7, circuit_C1, LOW, false, 0, not_used'
Note that the order of the preset data in this structure is not relevant for decision control/processing - entries can be random.
  • include a switch-case section in the main loop to handle your newly added button switch.

Sketch Size

The size of the sketch was assessed, with a varying number of switches configured with the following results:

  • 1 x switch, plus 1 x switch-case handler - 1152 bytes of program storage and 23 bytes of dynamic memory
  • each additional switch configured adds an average of 13 bytes to both memory types, an average of 26 bytes overall.

Note that sizing considerations do not take into account any additional code beyond defining and processing switches.

The sketch is designed for handling both types of switch - button and toggle style. However, if your project only handles one of these two types of switch the sketch may be easily modified to remove the code not required.

To Note

  • There is a difference to be observed between the reading outcome of button switches and toggle switches. The polling section of the man loop is responsible for keeping an 'eye open' for all switch change states, irrespective of their type. This section is ideal for switch associated code to be added to process switch change states for both button and toggle style switches. Outside of the polling loop, data relating to button switches is not relevant as we can only detect a button press if we do a read - button switches are momentary switches, they go through a cycle of OFF/ON/OFF. However, toggle switches behave in a different way and are either ON or OFF. As a result the current status of toggle switches persists its last read and so can be examined and used outside of the polling loop by reference to switches[?].switch_status (it is either 'on' or '!on').
  • If you find that your switches are giving spurious results it will most likely be because they are configured with the wrong preset circuit type. Check and modify as appropriate. If fact, even if you have wired and configured correctly and all is working as expected, it is instructional to flip a circuit type preset or two in the sketch and witness the results!

Finally

There is a balance to be struck with the number of switches required for a project and the method used to configure them. Whilst this article has largely been instructional, it would prove suitable where switch numbers are not too great. I would most likely look for other methods if the number of switches was, say, more than 10? For example, connecting several switches to a single analogue pin. The wiring scheme would be different and the switch reading process quite different also. No method is perfect and each comes with constraints and limitations.

Further Reading

You might also find these contributions interesting and useful, by the same author:

  • UnderstandIng and UsIng Button SwItches, the basIcs - button switches, a simple but often tricky piece of kit. The tutorial provides in ins and outs of implementing a simple button switch, with flexibility to explore differences in circuit design, different reading methods and debouncing.
  • External Interrupts, a generic framework supporting concurrent asynchronous multiple interrupts. Configure multiple external interrupts with different characteristics and add code to provide post-interrupt asynchronous processing.
  • Programmatic Timed Reminder Alerting, a programmatic framework for both elapsed and real-time asynchronous alerting. Define any number of reminder alerts (sub second to hours) and process asynchronously.

Other Online Resources

Digital pins

Overview of Interrupts

Code

reading_multiple_switch_types_by_polling.inoC/C++
Polling multiple switches, button and/or toggle types, or a mix.
/*
   Ron D Bentley, Stafford, UK
   Feb 2021

   Reading Multiple Switch Types, using simple polling
   '''''''''''''''''''''''''''''''''''''''''''''''''''

   This example and code is in the public domain and may be used without restriction and
   without warranty.

   This sketch will reliably read any number of button and/or toggle switches by polling each of
   them in turn.  Once a switch is activated the main code loop will switch
   to user provided code to handle the purpose of the switch.  This is controlled
   via a switch control struct(ure) and associated main loop switch-case code.

   The sketch layout is straight forward and process code for each switch can be added
   where indicated within the main loop, under the respective switch-case section for a
   button switch.

   Configurability:
   1. Number of switches - the implementation is such that each switch is
      allocated to a digital pin.  The sketch will therefore support as many
      switches as a microcontroller can provide digital inputs.
      This example sketch is configured, 'out-of-the-box' (OOTB),for six switches,
      3 x button switches plus 3 x toggle swith, but more may be added:

      a. change the macro definition '#define num_switches' to be the total number of
         switches to be connected
      b. for each switch, decide its circuit type and allocate it to a digital pin
      c. in the 'Switch to Pin Macro Definition List' add new switch macro
         definitions, one for each additional switch,
         for example:
         '#define button_switch_?  <pin number>',
         '#define toggle_switch_?  <pin number>',
         etc
      d. in the 'Switch Control Sruct(ure) Declaration' add further preset data to the
         'switches' data struct(ure), for example:
         '...,button_switch_?, circuit_?,...',
         '...,toggle_switch_?, circuit_?,...',
         etc.

   For a fuller appreciation of button switch fundementals see the tutorial 'Understanding
   and Using Button Switches':
   https://create.arduino.cc/projecthub/ronbentley1/understanding-and-using-button-switches-2ffe6c?ref=platform&ref_id=424_trending___&offset=2
*/
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// These declarations are specifically for defining and controlling the attached switches
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

#define circuit_C1           INPUT   // switch circuit requires an external pull down 10k ohm resistor
#define circuit_C2    INPUT_PULLUP   // switch type reqires no other components beyond the button switch

#define debounce             10      // number of millisecs to wait for a switch to settle once activated
#define switched           true      // signifies switch has been pressed/switch cycle complete
#define on                 true      // used for toggle switch staus
#define not_used           true      // helps self document code

#define button_switch        1       // differentiates switch type
#define toggle_switch        2       // toggle switches are NOT used in this example sketch - future use

#define num_switches         6       // number of button switches connected

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// %       Switch to Pin Macro Definition List         %
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Associate swithes each with a digital pin.
// Add further definitions here if adding more switches.
// Note that digital pins allocated are arbitary, any
// available pin will do, or remove any not required.
// The naming convention offered here helps to self
// document the sketch.

#define button_switch_1      2
#define button_switch_2      3
#define button_switch_3      4

#define toggle_switch_1      5
#define toggle_switch_2      6
#define toggle_switch_3      7

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// %                   Switch Control Sruct(ure) Declaration                 %
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Now set up the configuration data for each individual switch to be used by
// the switch read functions and to generally define the nature of the switch.
//
struct switch_control {
  int  switch_type;               // type of switch connected
  int  switch_pin;                // digital input pin assigned to the switch
  int  circuit_type;              // the type of circuit wired to the switch
  bool switch_on_value;           // used for BUTTON SWITCHES only - defines what "on" means, set up in setup()
  bool switch_pending;            // records if switch in transition or not
  long unsigned int elapse_timer; // records debounce timer count when associated switch is in transition
  bool switch_status;             // used for TOGGLE SWITCHES only - current state of toggle switch.
} switches[num_switches] = {
  // Note order of preset entries not relevant, but keep in a tidy order

  button_switch, button_switch_1, circuit_C1, LOW, false, 0, not_used,
  button_switch, button_switch_2, circuit_C2, LOW, false, 0, not_used,
  button_switch, button_switch_3, circuit_C1, LOW, false, 0, not_used,

  toggle_switch, toggle_switch_1, circuit_C2, not_used, false, 0, !on,
  toggle_switch, toggle_switch_2, circuit_C1, not_used, false, 0, !on,
  toggle_switch, toggle_switch_3, circuit_C2, not_used, false, 0, !on

};
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void setup() {
  // Initialise digital input switch pins
  for (int sw = 0; sw < num_switches; sw++) {
    // Define the switch circuit type - circuit_type is:
    // circuit_C1 (INPUT) or circuit_C2 (INPUT_PULLUP)
    pinMode(switches[sw].switch_pin, switches[sw].circuit_type);
    // Establish 'meaning' for switch on/off for a button switch, depending on circuit type.
    // This is used only by button read function, it has no relevance for toggle switches.
    if (switches[sw].circuit_type == circuit_C2) {
      // Switch is NOT configured with a pull down switch resistor
      switches[sw].switch_on_value   = LOW;    // switch pin goes LOW when switch pressed, ie on
    } else {
      // Circuit_type_C1, so switch IS configured with a pull down switch resistor
      switches[sw].switch_on_value   = HIGH;   // switch pin goes HIGH when switch pressed, ie on
    }
  }
  Serial.begin(115200); // dont forget to set your serial monitor speed to whatever is set here
}  // end of setup

void loop() {
  // Poll each connected switch in turn and, if switched, process its associated purpose
  for (int sw = 0; sw < num_switches; sw++) {
    if (read_switch(sw) == switched) {
      // This switch (sw) has been pressed, so process via a case switch
      if (switches[sw].switch_type == button_switch) {
        Serial.print("\nbutton switch on digital pin ");
      } else {
        Serial.print("\ntoggle switch on digital pin ");
      }
      Serial.print(switches[sw].switch_pin);
      Serial.println(" triggered");
      // Move to switch's associated code section

      switch (switches[sw].switch_pin)
      {
        case button_switch_1:
          Serial.println("case statement 1 entered");
          break;
        case button_switch_2:
          Serial.println("case statement 2 entered");
          break;
        case button_switch_3:
          Serial.println("case statement 3 entered");
          break;
        case toggle_switch_1:
          Serial.println("case statement 4 entered");
          Serial.print("switch is ");
          Serial.println(switches[sw].switch_status);
          break;
        case toggle_switch_2:
          Serial.println("case statement 5 entered");
          Serial.print("switch is ");
          Serial.println(switches[sw].switch_status);
          break;
        case toggle_switch_3:
          Serial.println("case statement 6 entered");
          Serial.print("switch is ");
          Serial.println(switches[sw].switch_status);
          break;
        default:
          // Spurious switch index!  Should never arise as this is controlled
          // by the for loop within defined upper bound
          break;
      }
      Serial.flush();  // flush out the output buffer
    }
  }
  // Pollng of the switches now complete until next cycle, so do other things here as required...



} // end of main loop

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Generic switch read function:
// '''''''''''''''''''''''''''''
// Read the switch defined by the function parameter.
// Function returns a value indicating if the switch
// has undergone a transition or not.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool read_switch(int sw) {
  if (switches[sw].switch_type == button_switch) {
    return read_button_switch(sw);
  }
  return read_toggle_switch(sw);
}  // end of read_switch

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Generic toggle switch read function:
// '''''''''''''''''''''''''''''''''''
// Test the toggle switch to see if its status has changed since last look.
// Note that, although, switch status is a returned value from the function,
// the current status of the switch ('switches[sw].switch_status') is always
// maintained and can be tested outside of the function at any point/time.
// It will either have a switches[sw].switch_status of 'on' or '!on' (ie off).
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool read_toggle_switch(int sw) {
  int pin_value = digitalRead(switches[sw].switch_pin);  // test current state of toggle pin
  if (switches[sw].circuit_type == circuit_C2) {
    // Need to invert HIGH/LOW if circuit design sets pin HIGH representing switch in off state
    // ie inititialised as INPUT_PULLUP
    pin_value = !pin_value;
  }
  if (pin_value != switches[sw].switch_status && !switches[sw].switch_pending) {
    // Switch change detected so start debounce cycle
    switches[sw].switch_pending = true;
    switches[sw].elapse_timer = millis();  // set start of debounce timer
  } else {
    if (switches[sw].switch_pending) {
      // We are in the switch transition cycle so check if debounce period has elapsed
      if (millis() - switches[sw].elapse_timer >= debounce) {
        // Debounce period elapse so assume switch has settled down after transition
        switches[sw].switch_status = !switches[sw].switch_status;  // flip status
        switches[sw].switch_pending = false;                       // cease transition cycle
        return switched;
      }
    }
  }
  return !switched;
} // end of read_toggle_switch

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Read button switch function:
// ''''''''''''''''''''''''''''
// Generic button switch read function.
// Reading is controlled by:
//   a. the function parameter which indicates which switch
//      is to be polled, and
//   b. the switch control struct(ure), referenced by a).
//
// Note that this function works in a nonexclusive way
// and incorporates debounce code.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bool read_button_switch(int sw) {
  int switch_pin_reading;
  switch_pin_reading = digitalRead(switches[sw].switch_pin);
  if (switch_pin_reading == switches[sw].switch_on_value) {
    // Switch is pressed (ON), so start/restart debounce process
    switches[sw].switch_pending = true;
    switches[sw].elapse_timer = millis();    // start elapse timing
    return !switched;                        // now waiting for debounce to conclude
  }
  if (switches[sw].switch_pending && switch_pin_reading == !switches[sw].switch_on_value) {
    // Switch was pressed, now released (OFF), so check if debounce time elapsed
    if (millis() - switches[sw].elapse_timer > debounce) {
      // dounce time elapsed, so switch press cycle complete
      switches[sw].switch_pending = false;
      return switched;
    }
  }
  return !switched;
}  // end of read_button_switch

Comments

Similar projects you might like

A Switch Library for Arduino

by ronbentley1

  • 1,758 views
  • 1 comment
  • 6 respects

Button Switch Using An External Interrupt

Project tutorial by ronbentley1

  • 19,742 views
  • 5 comments
  • 2 respects

Understanding and Using Button Switches

by ronbentley1

  • 6,707 views
  • 6 comments
  • 14 respects

Toggle Switches - Reliable Reading

Project tutorial by ronbentley1

  • 3,745 views
  • 1 comment
  • 5 respects

The Troll Switch

Project tutorial by Aitor Gamarra

  • 3,311 views
  • 3 comments
  • 7 respects

10 Buttons Using 1 Interrupt

Project tutorial by Svizel_pritula

  • 48,785 views
  • 15 comments
  • 87 respects
Add projectSign up / Login