ronbentley1
Published

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.

IntermediateProtip1,476
Implementing Multiple Mixed Switch Types By Polling

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
Tactile Switch, Top Actuated
Tactile Switch, Top Actuated
example sketch is configured for 3 x button and 3 x toggle switches, but this is arbitrary
×3
Toggle Switch, Toggle
Toggle Switch, Toggle
example sketch is configured for 3 x button and 3 x toggle switches, but this is arbitrary
×3
Jumper wires (generic)
Jumper wires (generic)
×1
Resistor 10k ohm
Resistor 10k ohm
three switches are configured with 10k ohm pull down resistors
×3

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Code

reading_multiple_switch_types_by_polling.ino

C/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

Credits

ronbentley1

ronbentley1

25 projects • 13 followers

Comments