Guidance on a project running six switches

Ive been writing a sketch that uses an interrupt to constantly check the state of six switches in order to determine which switch has been pressed. any guidance anyone can offer would be appreciated.

ignore the loop i was testing the code.

this is the code I've been working on for the switches:

int buttonState = 0;



// set variables for switches
int switch_1 = 3;
int switch_2 = 4;
int switch_3 = 5;
int switch_4 = 6;
int switch_5 = 7;
int switch_6 = 10;

unsigned long time;

//Attached interrupt on pin 2
const byte interruptPin = 2;



void setup() {
  
  //set pin2 as pullup, all other switches set as OUTPUT
  pinMode(interruptPin, INPUT_PULLUP);
  pinMode(switch_1, OUTPUT);
  pinMode(switch_2, OUTPUT);
  pinMode(switch_3, OUTPUT);
  pinMode(switch_4, OUTPUT);
  pinMode(switch_5, OUTPUT);
  pinMode(switch_6, OUTPUT);

  
  digitalWrite(switch_1, LOW);
  digitalWrite(switch_2, LOW);
  digitalWrite(switch_3, LOW);
  digitalWrite(switch_4, LOW);
  digitalWrite(switch_5, LOW);
  digitalWrite(switch_6, LOW);

  //interrupt seeks a LOW reading for 6 switches
  attachInterrupt(digitalPinToInterrupt(interruptPin), seek, FALLING);

}


void loop() {
  int testRun = digitalRead(switch_1);
  
  if (buttonState > 0) {
    digitalWrite(ledPin, HIGH);
    delay(500);
    digitalWrite(ledPin, LOW);
    delay(500);
    buttonState = buttonState - 1;
  }


}


void seek() {
  
  noInterrupts (); //disalbes interrupts

  int buttonState = 0;

  //If digitalRead(pin 2) = 1, then buttonState = 1
  
  digitalWrite(switch_1, HIGH);

  if (digitalRead(interruptPin) == 1) {
    buttonState = 1;
  }
 
  digitalWrite(switch_2, HIGH);

  if (digitalRead(interruptPin) == 2) {
    buttonState = 2;
  }

  digitalWrite(switch_3, HIGH);

  if (digitalRead(interruptPin) == 3) {
    buttonState = 3;
  }

  digitalWrite(switch_4, HIGH);

  if (digitalRead(interruptPin) == 4) {
    buttonState = 4;
  }

  digitalWrite(switch_5, HIGH);

  if (digitalRead(interruptPin) == 5) {
    buttonState = 5;
  }

  digitalWrite(switch_6, HIGH);

  if (digitalRead(interruptPin) == 6) {
    buttonState = 6;
  }

  digitalWrite(switch_1, LOW);
  digitalWrite(switch_2, LOW);
  digitalWrite(switch_3, LOW);
  digitalWrite(switch_4, LOW);
  digitalWrite(switch_5, LOW);
  digitalWrite(switch_6, LOW);

  
  

  interrupts ();  //re-enables interrupts


}

Is the project about interrupts and uses switches or vice-versa?

GoForSmoke:
Is the project about interrupts and uses switches or vice-versa?

I'm currently running three peristaltic pumps using timer interrupts, but in the sketch I'm working on now i want to use an interrupts to check for when a button is pressed. i would say that the project is more about the switches operating correctly and my timer interrupts and not so much about the interrupt that will be handling the push buttons.

i would like to use an interrupt with switches as well if possible.

if you'd like to see the code for my timer interrupts i can post it.

GoForSmoke:
Is the project about interrupts and uses switches or vice-versa?

but in the sketch I'm working on now i want to use an interrupts to check for when a button is pressed.

Why?

if you'd like to see the code for my timer interrupts i can post it.

Yes it would help instead of that silly code you posted.

Interrupts should not be used to pole inputs on a timed interrupt, it is just an unnecessary complication. Your failure to see that marks you out as a beginner.

You seem to want to use interrupts but you have not yet mastered arrays and loops to make your turgid code shorter and more readable.

Grumpy_Mike:
Why?
Yes it would help instead of that silly code you posted.

Interrupts should not be used to pole inputs on a timed interrupt, it is just an unnecessary complication. Your failure to see that marks you out as a beginner.

You seem to want to use interrupts but you have not yet mastered arrays and loops to make your turgid code shorter and more readable.

Actually my profile marks me as a beginner so why don't you focus more on helping people instead of pointing out the obvious with your idiotic comments. I'm using interrupts because that is what i have been instructed to do by the person teaching me how to program. i wouldn't ask for help if i had the answers bro.

It sounds like you are trying to detect button presses by a human. Humans are very slow so there is no need to use interrupts to detect their activity. Just have a function that is called from loop() and uses digitalRead() to read all the switches.

Have a look at Planning and Implementing a Program

...R

There is no need to use interrupts for switches if your code is non-blocking. More on that later.

If the switches are simple contact switches then you will need to either debounce them or slow the reads down enough to not "see" the contact bouncing. (add: the latter there is poor technique)

A very complete tutorial on switches.

If you debounce in hardware or use some other kinds of switch (light interrupt or reflect using a led and detector, magnet and sensor for two examples) then you can simply read the pin state but still need to tell state change from raw state.

Each interrupt call has a whole load of cpu cycle overhead besides the code that's in it.

Non-bocking code is code that does not wait for something to happen by keeping execution to itself.
The concept is simple, the code required is more complicated but if you hold onto the concept then you can make sense of the techniques to do amazing things and even come up with your own.

Another link, these lessons are so well prepared I am happy to link them:
Non-blocking explained well at the simple level with code.

Excerpt -- he then takes this explanation right down to code.

Say you want to cook breakfast. You need to cook:

Coffee - takes 1 minute
Bacon - takes 2 minutes
Eggs - takes 3 minutes

Now a seasoned cook would NOT do this:

Put coffee on. Stare at watch until 1 minute has elapsed. Pour coffee.
Cook bacon. Stare at watch until 2 minutes have elapsed. Serve bacon.
Fry eggs. Stare at watch until 3 minutes have elapsed. Serve eggs.

The flaw in this is that whichever way you do it, something is going to be cooked too early (and get cold).

In computer terminology this is blocking. That is, you don't do anything else until the one task at hand is over.

What you are likely to do is this:

** Start frying eggs. Look at watch and note the time.**
** Glance at watch from time to time. When one minute is up then ...**
** Start cooking bacon. Look at watch and note the time.**
** Glance at watch from time to time. When another minute is up then ...**
** Put coffee on. Look at watch and note the time.**
** When 3 minutes are up, everything is cooked. Serve it all up.**

In computer terminology this is non-blocking. That is, keep doing other things while you wait for time to be up.

I have a button library that you can use if you want. It handles debouncing and returns a button state value that tells not just the pin state but state changes and whether or not the button is currently 'bouncing'. Upshot, check the one status value for change and you will always get the change without your sketch needing to keep track of previous state.

I finally got the last bug out of the fastest, lightest-on-RAM version. I'll set it up in a non-blocking multi-button demo that's worked for years using the not-as-light-on-RAM versions. I have a multiplexed-pins version as well, 8 pins to serve 16 buttons. It will take a bit for me to package an example just due to triple-checking and the usual apple-polishing (feature add/improve) to get the best out. In some ways that's pathetic, in other ways it's professional.

PS Sam, you may not understand all of what Mike was telling you but that does not make his comments idiotic.

thank you all for your help, I'm definitely starting to gain a better understanding. Hopefully ill be able to learn some more from those references you posted.

Actually my profile marks me as a beginner

No it marks you as not having made many posts.

I'm using interrupts because that is what i have been instructed to do by the person teaching me how to program.

Then he has not structured your learning profile correctly.
First some practical help.
there is no need for this:-
noInterrupts (); //disalbes interrupts
In an ISR. Once an ISR is entered it is entered with the interrupts already disabled.
Also there is no need to reenable them before exit, that happens automatically.

All variables used both inside and outside an ISR must be declared volatile, I can't see you doing that.

If you use a background interrupt driven task you use it to set flags when you detect a button push. Your main function has to check and detect those flags. In which case you might just as well check and detect the push buttons themselves, there is no point in using an interrupt.

Perhaps you should mention this to your instructor and ask him for some more realistic instructions.

I would start off by asking him to set you an array assignment so you can learn the basics first before you try the fancy stuff.

Note that the other two contributors also said you don't need interrupts either.

Mostly you don't want to waste interrupts on something relatively slow.

Sam, one thing that takes a bit of getting used to is the speed of these things. They're nothing so fast as a PC (since the mid-90's) on clock rate but these things don't have an OS to drag along.

Ferinstance, my usual button(s) and led(s) demos when I include a loop() counter to tell how often my tasks in lop() get run tends to be over 50KHz. With 4 buttons run 1 per pass through loop() they are each getting checked over 10K times per second which makes for very solid debouncing and led blinks on the millisecond.
However, I throw a single delay(1) in that loop() and over 50KHz becomes under 1KHz. Not so smooth.

Perhaps you should mention this to your instructor and ask him for some more realistic instructions.

Personally i would like to avoid having to use interrupts as well, ill definitely mention that to him. I am trying to learn the basics while also attempting to complete this more complicated project. I'm taking a free online arduino course.

Ferinstance, my usual button(s) and led(s) demos when I include a loop() counter to tell how often my tasks in lop() get run tends to be over 50KHz. With 4 buttons run 1 per pass through loop() they are each getting checked over 10K times per second which makes for very solid debouncing and led blinks on the millisecond.
However, I throw a single delay(1) in that loop() and over 50KHz becomes under 1KHz. Not so smooth.

it is a hard to grasp how fast things are processed in electronics and programming when you have never worked with them before. Thanks again for the help!

I have a button library that you can use if you want.

i forgot to mention. Would it be possible for me to take a look at that button sketch you mention a few days ago?

Okay, and see below for notes on how to get this into your IDE and Sketchbook.
You don't have to know what's in the library files to use them. The example shows only one use though. I'll post another for making multiple button handlers later today (got to go to the bank while it's open first).

The sketch:

// buttonclass2016 by GoForSmoke @ Arduino Forum
// Free for use, Sept 25th, 2016

#include <avr/io.h>
#include "button.h";

// these variables are here to ward off the WinXP bug!
// if you're not running Windoze XP you won't need them
// but in any case, the compiler will optimize them away.
int a, b, c, d, e, f;
char g;
char h;
char i;

button  myButton( 7, 5 ); // this makes the button object


void setup()
{
  Serial.begin( 250000 );
  Serial.println( F( "\n\n\n\nTEST\n" ));

  myButton.setUpButton(); // this starts the button object
};


void loop()
{
  byte buttonState = myButton.runButton(); // this runs the button object

  if ( buttonState < BOUNCE_4 )
  {
    switch ( buttonState ) 
    {
      case CURRENT_1 :
      Serial.print( F( "up  " ));
      Serial.println( millis());
      break;
      case PREVIOUS_2 :
      Serial.print( F( "down  " ));
      Serial.println( millis());
      break;
    }
  }
}

The library header file:

// button library, Sept 25th, 2016 by GoForSmoke


#ifndef button_h
#define button_h

#include "Arduino.h"

#define CURRENT_1 1
#define PREVIOUS_2 2
#define BOUNCE_4 4

typedef class button
{
private:
  byte arduPin;
  byte buttonState; // bit 0 = current, bit 1 = previous, bit 2 = bounce
  byte startMs;
  byte debounceMs; 

public:
  button( char, unsigned char ); // pin, debounce millis
  void setUpButton( void );
  byte runButton( void ); // returns buttonState as below
  // buttonState: bit 0 = current, bit 1 = previous, bit 2 = bounce
  byte buttonRead();  // returns buttonState as above
};

#endif

The library cpp file:

// button library, Sept 25th, 2016 by GoForSmoke

#include "Arduino.h"
#include "button.h"

button::button( char ap, byte dbm )
{
  arduPin = ap;
  debounceMs = dbm;
  buttonState = CURRENT_1;
};

void button::setUpButton( void )
{
  pinMode( arduPin, INPUT_PULLUP );
};

byte button::buttonRead()
{
  return buttonState;
};


byte button::runButton( void )
{
//  static byte msNow;

  buttonState &= BOUNCE_4 | CURRENT_1; // clears previous state bit
  buttonState |= ( buttonState & CURRENT_1 ) << 1; // copy current state to previous
  buttonState &= BOUNCE_4 | PREVIOUS_2; // clears current state bit
  buttonState += digitalRead( arduPin ); // current state loaded into bit 0

//  msNow = (byte) millis(); // gets the low byte of millis

  if ( buttonState & 3 == CURRENT_1 || buttonState & 3 == PREVIOUS_2 )  // state change detected
  {
    buttonState ^= BOUNCE_4;      // toggles debounce bit
    // on 1st and odd # changes since last stable state, debounce is on
    // on bounces back to original state, debounce is off 
    if ( buttonState & BOUNCE_4 )
    {
//      startMs = msNow;    // starts/restarts the bounce clock on any change.
      startMs = (byte) millis();    // starts/restarts the bounce clock on any change.
    }
  }
  else if ( buttonState & BOUNCE_4 ) // then wait to clear debounce bit
  {
//    if ( msNow - startMs >= debounceMs ) // then stable button state achieved
    if ( (byte)((byte)millis()) - startMs >= debounceMs ) // then stable button state achieved
    {
      //   understand that stable state means no change for debounceMs. When time
      //   is over the state bits are manipulated to show a state change.
      buttonState &= CURRENT_1; // clear all but the current state bit
      if ( buttonState == 0 )  buttonState = PREVIOUS_2;  // HIGH->LOW
      else                     buttonState = CURRENT_1;  // LOW->HIGH
      //   buttonState now appears as a debounced state change in bits 0 and 1
    }
  }

  return buttonState;
};

Notes: I will assume you know little about the IDE.

Start your IDE (I'm using 1.6.9) and open a New Sketch (File->New), it will start with the bare sketch.

Use File->SaveAs to give the sketch a name (like buttonlib2016) and before pressing enter, look at where the folder it's going into is on your drive. That should be your Sketchbook.

Select/highlight the sketch code in this post and use ctrl-c (or use the rt-click menu) to Copy it.
Select All in the IDE bare sketch then press ctrl-v to paste what you copied in.

In the green bar at the top of the IDE that has the sketch name, on the far right is a little box with a v in it. Click that to get a popdown menu then click New Tab. It will ask you for a File Name. You will make one tab for the file button.h and another for button.cpp.

Copy the header file contents from this post into the button.h tab and the cpp file contents into button.cpp. Then open each tab and use File->Save. The Sketchbook folder should now have all 3 files. You don't have to dig into your IDE library folder for this to work.

Now open the sketch tab and compile it to check. Set your Serial Monitor to 250000 baud and Add Newline to run as is. If you ground pin 7, that will be a button press, Serial Monitor will tell you.

Important: pressing the button should ground the pin. If you have the button connected to 5V through a resistor it won't work. It won't hurt anything but it won't work. The chip has internal resistors that pinMode(pin_number, INPUT_PULLUP) engages, saves you having to wire a resistor in but it does mean that button grounded makes the pin LOW and not makes it HIGH.

Here is my Updated (from May 2105) button array example.

Of course I had to add 2 functions to the library to accommodate making button arrays, no rest for the wicked here!

This updated library works with the previous example, it just allows more... arrays of buttons.

If you don't know arrays yet then learning about those will help you immensely with coding.

I know you will have questions about BOTH of these. We can all share the learning experience.

Example sketch:

/* buttonarrayclass example -- by GoForSmoke 2015 for public domain use
   revised with better library Oct 2106

  This uses 2 buttons or switches or jumpers for pins 2 & 3 to GND.
  Depending on switch, debounce value may need tuning.

  Button class object output is a state value for the main sketch code.
   // bit 0 = current, bit 1 = previous, bit 2 = bounce
  0, button currently still pressed. bits 000; same current and previous
  1, button has just been released. bits 100; different current and previous
  2, button has just been pressed.  bits 010; different the other way
  3, button currently still released. bits 110; same current and previous
  4 or more means the button is being debounced but not finished.
*/

#include "Arduino.h"
#include "button.h" // check for newest

// these variables are here to ward off the WinXP bug!
// if you're not running Windoze XP you won't need them
// but in any case, the compiler will optimize them away.
int a, b, c;
byte d, e, f;

const byte howManyButtons = 4;
byte buttonIdx;
byte buttonPin[ howManyButtons ] = { 4,5,6,7 };
button user[ howManyButtons ]; // button array

const byte ledpin = 13;

void setup()
{
  Serial.begin( 250000 );
  Serial.println( F( "\n  Startup\n" ));

  pinMode( ledpin, OUTPUT ); // default is INPUT LOW, now is OUTPUT LOW
  
  for ( buttonIdx = 0; buttonIdx < howManyButtons; buttonIdx++ )
  {
    user[ buttonIdx ].setButton( buttonPin[ buttonIdx ], 5 ); // buttons on pins 4,5,6,7
  }
  buttonIdx = 0;
}

void loop()
{
  static byte buttonRead; // allocated once, used often

  buttonRead = user[ buttonIdx ].runButton(); // turns the crank once, make sure it runs often!

  if ( buttonRead == 2 ) // just pressed
  {
    digitalWrite( ledpin, HIGH );
    Serial.print( F( "button " ));
    Serial.print( buttonIdx );
    Serial.print( F( " pressed millis() == " ));
    Serial.println( millis());
  }
  else if ( buttonRead == 1 ) // just released
  {
    digitalWrite( ledpin, LOW );
    Serial.print( F( "button " ));
    Serial.print( buttonIdx );
    Serial.print( F( " released millis() == " ));
    Serial.println( millis());
    Serial.println( );
  }
  
  if ( ++buttonIdx >= howManyButtons ) buttonIdx = 0; // adds 1 then tests limit
}

The Updated button.h file:

/*
  button.h for public domain use by GoForSmoke May 29 2015
  Revised Sept 2016  
  To use:
  make a buttonclass object in your sketch
  run that object every time through loop(), it is quickly done
  when you want to know the status of the button, you read the object
  it returns state;  bit 0 = current, bit 1 = previous, bit 2 = bounce
*/

// button library, Sept 25th, 2016 by GoForSmoke

#ifndef button_h
#define button_h

#include "Arduino.h"

#define CURRENT_1 1
#define PREVIOUS_2 2
#define BOUNCE_4 4

typedef class button
{
private:
  byte arduPin;
  byte buttonState; // bit 0 = current, bit 1 = previous, bit 2 = bounce
  byte startMs;
  byte debounceMs; 

public:
  button(); // default constructor
  void setButton( byte, byte ); // pin, debounce millis
  button( byte, byte ); // pin, debounce millis
  void setUpButton( void );
  byte runButton( void ); // returns buttonState as below
  // buttonState: bit 0 = current, bit 1 = previous, bit 2 = bounce
  byte buttonRead();  // returns buttonState as above
};

#endif

The Updated button.cpp file:

// button library, Sept 25th, 2016 by GoForSmoke

#include "Arduino.h"
#include "button.h"

//    button ================================================

button::button() // default constructor for arrays, needs the full setup
{
  buttonState = CURRENT_1;
}

button::button( byte ap, byte dbm )
{
  arduPin = ap;
  debounceMs = dbm;
  buttonState = CURRENT_1;
};

void button::setButton( byte ap, byte dbm ) // pin, debounce millis
{
  arduPin = ap;
  debounceMs = dbm;
  pinMode( arduPin, INPUT_PULLUP );
};


void button::setUpButton( void )
{
  pinMode( arduPin, INPUT_PULLUP );
};

byte button::buttonRead()
{
  return buttonState;
};


byte button::runButton( void )
{
//  static byte msNow;

  buttonState &= BOUNCE_4 | CURRENT_1; // clears previous state bit
  buttonState |= ( buttonState & CURRENT_1 ) << 1; // copy current state to previous
  buttonState &= BOUNCE_4 | PREVIOUS_2; // clears current state bit
  buttonState += digitalRead( arduPin ); // current state loaded into bit 0

//  msNow = (byte) millis(); // gets the low byte of millis

  if ( buttonState & 3 == CURRENT_1 || buttonState & 3 == PREVIOUS_2 )  // state change detected
  {
    buttonState ^= BOUNCE_4;      // toggles debounce bit
    // on 1st and odd # changes since last stable state, debounce is on
    // on bounces back to original state, debounce is off 
    if ( buttonState & BOUNCE_4 )
    {
//      startMs = msNow;    // starts/restarts the bounce clock on any change.
      startMs = (byte) millis();    // starts/restarts the bounce clock on any change.
    }
  }
  else if ( buttonState & BOUNCE_4 ) // then wait to clear debounce bit
  {
//    if ( msNow - startMs >= debounceMs ) // then stable button state achieved
    if ( (byte)((byte)millis()) - startMs >= debounceMs ) // then stable button state achieved
    {
      //   understand that stable state means no change for debounceMs. When time
      //   is over the state bits are manipulated to show a state change.
      buttonState &= CURRENT_1; // clear all but the current state bit
      if ( buttonState == 0 )  buttonState = PREVIOUS_2;  // HIGH->LOW
      else                     buttonState = CURRENT_1;  // LOW->HIGH
      //   buttonState now appears as a debounced state change in bits 0 and 1
    }
  }

  return buttonState;
};

//    end button ============================================