Industrial manufacturing
Industrial Internet of Things | Industrial materials | Equipment Maintenance and Repair | Industrial programming |
home  MfgRobots >> Industrial manufacturing >  >> Manufacturing Technology >> Manufacturing process

Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO

Components and supplies

Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO
Arduino UNO
Or any other microcontroller
×1
Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO
Breadboard (generic)
×1
Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO
Jumper wires (generic)
×21
Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO
Tactile Switch, Top Actuated
Any button will do, big or small
×10

Apps and online services

Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO
Arduino IDE

About this project

Introduction

Interrupts are handy. They, in addition to sometimes making code simpler, can be used for precise timing or for waking the Arduino up from sleep mode.

Let's say you've got a user interface, a remote controller, for example, that runs on batteries. You might want to put the Arduino (or stand-alone ATmega) to power-down mode to save power. When an Arduino enters power-down mode it can only be woken up by an external interrupt. The ATmega328P chip used in an Arduino Uno has only two external pin interrupts. (INT0 and INT1 on pins 2 and 3) Since a user interface is likely to have more than two buttons, that's a problem.

The standard way to solve

this would be to connect all buttons normally, but to also connect them to an interrupt pin with a diode. This does, however, complicate the circuit significantly.

Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO

In addition to standard external interrupts, the ATmega328P also has pin change interrupts. There are libraries to deal with them and they are a good solution to this problem.

However, during a programming competition, I figured out how to do this using standard external interrupts with no extra electrical components.

The circuit

Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO
Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO
Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO

We have a few buttons. I used ten, which fit nicely on my breadboard. (I also don't have more.) You can have one button per pin, which means up to 20 on a Uno and up to 70 on a Mega! (If you actually need 70 buttons, I recommend using multiplexing, you don't need an entire Mega for that.)

Each button has one side connected to an arbitrary pin. (4-13 in my case) The other sides of all buttons are connected together to a single interrupt-capable pin. (2 in my case)

Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO

The code

The code is attached below. To get this example to work, upload it to your board. Open your serial monitor. When you press a button, its number will appear. As you can see, the loop function is not used at all.

Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO

How does it work?

There is, obviously, an interrupt. In my case, it's attached to pin 2. It's configured as FALLING.

To avoid using diodes, the Arduino rewires the circuit on the fly. There are two possible configurations: Common mode and Distinct mode.

Common mode

Most of the time, the circuit will be in common mode. The interrupt pin will be configured as INPUT_PULLUP and the rest will be OUTPUT and LOW.

Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO
void configureCommon() {
 pinMode(commonPin, INPUT_PULLUP);
 for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) {
   pinMode(buttonPins[i], OUTPUT);
   digitalWrite(buttonPins[i], LOW);
 }
}

In common mode, pressing any button will pull our interrupt pin down and fire our interrupt. Once that happens, our interrupt service routine will reconfigure the pins for distinct mode.

Distinct mode

Once our interrupt triggers, we quickly switch to distinct mode.

Distinct mode is the opposite of common mode. The interrupt pin will be OUTPUT and LOW and the rest will be INPUT_PULLUP.

Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO
void configureDistinct() {
 pinMode(commonPin, OUTPUT);
 digitalWrite(commonPin, LOW);
 for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) {
   pinMode(buttonPins[i], INPUT_PULLUP);
 }
}

In distinct mode, only pins corresponding to buttons actually pressed will be pulled down. We can easily go over all pins to find out which one triggered the interrupt.

After that's done, the Arduino can switch back to common mode and wait for another interrupt. Or, depending on your application, it can stay in distinct mode and process user input as it would normally, switching back to common mode before the Arduino goes to sleep.

A more complex example

Let's try something a bit more complex. We'll attach a servo and map each button to a different angle. (1=0°, 2=20°... 10=120°) We'll also power our Arduino with a few batteries.

Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO
Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO
Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNO

In this example, we put the Arduino to power-down mode after five seconds of inactivity to conserve power. You can find some tutorials on sleep mode online. In addition to that, we power the servo through a transistor to shut it down when it's not in use.

The code for this example is also attached below.

Code

  • Serial logger
  • Servo with sleep mode
Serial loggerArduino
const int commonPin = 2;
const int buttonPins[] = {4,5,6,7,8,9,10,11,12,13};

unsigned long lastFire = 0;

void setup() {
  configureCommon(); // Setup pins for interrupt

  attachInterrupt(digitalPinToInterrupt(commonPin), pressInterrupt, FALLING);

  Serial.begin(9600);
}

void loop() {
  // Empty!
}

void pressInterrupt() { // ISR
  if (millis() - lastFire < 200) { // Debounce
    return;
  }
  lastFire = millis();

  configureDistinct(); // Setup pins for testing individual buttons

  for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) { // Test each button for press
    if (!digitalRead(buttonPins[i])) {
      press(i);
    }
  }

  configureCommon(); // Return to original state
}

void configureCommon() {
  pinMode(commonPin, INPUT_PULLUP);

  for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) {
    pinMode(buttonPins[i], OUTPUT);
    digitalWrite(buttonPins[i], LOW);
  }
}

void configureDistinct() {
  pinMode(commonPin, OUTPUT);
  digitalWrite(commonPin, LOW);

  for (int i = 0; i < sizeof(buttonPins) / sizeof(int); i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);
  }
}

void press(int button) { // Our handler
  Serial.println(button + 1);
}
Servo with sleep modeArduino
#include <Servo.h>
#include <avr/sleep.h>
#include <avr/power.h>

const int commonPin = 2;
const int buttonPins[] = {4,5,6,7,8,9,10,11,12,13};

const int servoEnablePin = A1;
const int servoPin = A0;
Servo servo;

unsigned long lastFire = 0;
int status = 0;

void setup() {
  pinMode(commonPin, INPUT_PULLUP);
  
  configureCommon();
  
  attachInterrupt(digitalPinToInterrupt(commonPin), pressInterrupt, FALLING);

  servo.attach(servoPin);
  pinMode(servoEnablePin, OUTPUT);
}

void loop() {
  if (millis()-lastFire > 5000) {
    digitalWrite(servoEnablePin, LOW);
    
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();  
    sleep_mode();
  }

  delay(10);
}

void pressInterrupt() {
  sleep_disable();
  power_all_enable();
  
  if (millis()-lastFire < 200) {
    return;
  }
  lastFire = millis();
  
  configureDistinct();

  for (int i=0;i<sizeof(buttonPins)/sizeof(int);i++) {
    if (!digitalRead(buttonPins[i])) {
      press(i);
    }
  }

  configureCommon();
}

void configureCommon() {
  pinMode(commonPin, INPUT_PULLUP);
  
  for (int i=0;i<sizeof(buttonPins)/sizeof(int);i++) {
    pinMode(buttonPins[i], OUTPUT);
    digitalWrite(buttonPins[i], LOW);
  }
}

void configureDistinct() {
  pinMode(commonPin, OUTPUT);
  digitalWrite(commonPin, LOW);
  
  for (int i=0;i<sizeof(buttonPins)/sizeof(int);i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);
  }
}

void press(int button) {
  digitalWrite(servoEnablePin, HIGH);
  servo.write(map(button,0,9,0,180));
}

Schematics

Efficiently Control 10 Buttons with a Single Interrupt on Arduino UNOEfficiently Control 10 Buttons with a Single Interrupt on Arduino UNOmultiinterrupt_hoF76Oc4T5.fzzservo_with_sleep_u9ZqxF0jhY.fzz

Manufacturing process

  1. Create a Stunning Monitor Ambilight System with Arduino
  2. Build a Smart Voltmeter with Arduino & Smartphone – Easy DIY Project
  3. Real‑Time IoT Heart Rate Monitor with Arduino & MAX30100
  4. Arduino Uno WiFi Web Server: Toggle an LED via Browser
  5. Build an Arduino-Powered Automated Google Chrome Dino Game
  6. Build a Compact FM Radio with Arduino Nano and RDA8057M
  7. Wireless Arduino Programming Shield with HC-05 Bluetooth – No USB Needed
  8. Capacitive Touch LED Switch with Arduino UNO – Easy DIY Project
  9. Build a Real-Time Face-Tracking System with Arduino & OpenCV
  10. Build a Precise Clock with Arduino Nano and 16x2 LCD