Button Debouncing

Apa Itu Button Debouncing

Ketika kita menggunakan komponen push button atau switch mechanical, maka akan ada fenomena yang bernama button bounce atau bahasa Indonesianya mantul-mantul. Button bounce terjadi pada kontak fisik dari push button atau switch tersebut. Hal ini terjadi karena kontak fisik dari push button atau switch terbuat dari metal sehingga memiliki elastisitas.

Button bounce menyebabkan noise pada sinyal input untuk GPIO seperti pada gambar berikut ini. Fenomena ini bisa terjadi selama beberapa micro sampai mili detik sampai nilai button tersebut stabil. Noise ini dapat menyebabkan kesalahan pembacaan oleh GPIO terutama pada aplikasi pembacaan jumlah penekanan button. Satu kali penekanan button oleh user, dapat terbaca berkali-kali oleh mikrokontroler.

Solusi untuk masalah ini disebut button debouncing. Ada banyak metode debouncing yang bisa digunakan, mulai dari debouncing dengan hardware sampai debouncing dengan software. Pada tutorial ini akan dibahas mengenai debouncing dengan software.

Metode debouncing dengan software dilakukan dengan menggunakan timer untuk menunggu sampai nilai GPIO stabil, kemudian nilai tersebut yang akan dibaca. Proses debouncing ini digambarkan pada gambar berikut ini.

Contoh Rangkaian

Rangkaian yang dibuat yaitu sebagai berikut. ESP32 dan modul OLED dihubungkan dengan bus I2C. Pin yang digunakan yaitu pin 32 untuk SCL dan pin 33 untuk SDA. Button dihubungkan dengan pin 22 dan GND.

Program Button Tanpa Debouncing

Pada contoh progam ini, kita akan melakukan pembacaan push button tanpa debouncing. Program ini berisi dua bagian kode untuk OLED display dan button. Bagian pembacaan button yaitu pada line 45. Pada line 47, jika nilai button adalah LOW, maka nilai variabel counter akan dinaikkan sebanyak satu.

button_without_debouncing.ino
#include <Wire.h>
#include <Adafruit_SSD1306.h>

// Pin I2C
#define I2C_SDA 33
#define I2C_SCL 32

// Pin button
#define BTNPIN 22

// Address I2C OLED
#define OLED_ADDR 0x3C

// Deklarasi object untuk OLED display
Adafruit_SSD1306 oled(128, 64, &Wire, -1);

// Counter
int counter = 0;

// Button
int btn_state;

void setup()
{
  // Setup serial communication
  Serial.begin(115200);

  // Inisialisasi pin I2C
  Wire.begin(I2C_SDA, I2C_SCL, 400000);
  
  // Inisialisasi OLED
  oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
  oled.clearDisplay();
  oled.setTextColor(WHITE);
  oled.setTextSize(3);
  print_center(String(counter), 0, 20);
  oled.display();

  // Konfigurasi GPIO pin sebagai input pull-up untuk button
  pinMode(BTNPIN, INPUT_PULLUP);
}

void loop()
{
  btn_state = digitalRead(BTNPIN);

  if (btn_state == LOW)
  {
    // Naikkan nilai counter
    counter++;

    oled.clearDisplay();
    oled.setTextColor(WHITE);
    oled.setTextSize(3);
    print_center(String(counter), 0, 20);
    oled.display();
  }
}

// Fungsi untuk print text ke OLED dengan alignment center
void print_center(const String str, int x, int y)
{
  int16_t x1, y1;
  uint16_t w, h;
  oled.getTextBounds(str, x, y, &x1, &y1, &w, &h);
  oled.setCursor((x-w/2)+(128/2), y);
  oled.print(str);
}

Gambar berikut ini menampilkan hasil penekanan tombol tanpa debouncing. Setiap kali penekanan, counter bertambah lebih dari satu.

Program Button Dengan Debouncing

Pada contoh progam ini, kita akan melakukan pembacaan push button dengan debouncing. Berikut ini cara kerjanya:

  • Pada line 49, kita melakukan pembacaan push button kemudian disimpan pada variable lokal bernama reading. Nilai variable ini menyimpan pembacaan button secara real-time dan tanpa debouncing.

  • Pada line 52-56, kita melakukan perbandingan antara nilai button yang terbaca saat ini (reading) dan nilai button yang terbaca pada iterasi loop sebelumnya (last_button_state). Jika nilainya berbeda, maka ini berarti nilai GPIO masih berubah-ubah atau belum stabil (masih berada dalam tahap noise bounce). Jika masih dalam tahap bounce, maka timer akan selalu di-reset (di-update dengan nilai millis terbaru).

  • Pada line 59-77, kondisi if tersebut akan terpenuhi ketika noise bounce sudah terlewati dan timer debouncing sudah memenuhi waktu yang ditetapkan di variable debounce_delay. Jika kondisi ini terpenuhi, maka ini dianggap terjadi penekanan button yang valid. Selanjutnya kita melakukan update state button pada variable button_state, kemudian jika state bernilai LOW, maka nilai variabel counter akan dinaikkan sebanyak satu.

  • Pada line 81, kita menyimpan nilai pembacaan button saat ini untuk iterasi loop berikutnya.

button_with_debouncing.ino
#include <Wire.h>
#include <Adafruit_SSD1306.h>

// Pin I2C
#define I2C_SDA 33
#define I2C_SCL 32

// Pin button
#define BTNPIN 22

// Address I2C OLED
#define OLED_ADDR 0x3C

// Deklarasi object untuk OLED display
Adafruit_SSD1306 oled(128, 64, &Wire, -1);

// Counter
int counter = 0;

// Button debouncing
int button_state = HIGH;
int last_button_state = HIGH;
unsigned long last_debounce_time = 0;
unsigned long debounce_delay = 50;

void setup()
{
  // Setup serial communication
  Serial.begin(115200);

  // Inisialisasi pin I2C
  Wire.begin(I2C_SDA, I2C_SCL, 400000);
  
  // Inisialisasi OLED
  oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
  oled.clearDisplay();
  oled.setTextColor(WHITE);
  oled.setTextSize(3);
  print_center(String(counter), 0, 20);
  oled.display();

  // Konfigurasi GPIO pin sebagai input pull-up untuk button
  pinMode(BTNPIN, INPUT_PULLUP);
}

void loop()
{
  // Membaca state button kemudian disimpan pada variable
  int reading = digitalRead(BTNPIN);

  // Jika nilai button berubah, karena noise atau penekanan
  if (reading != last_button_state)
  {
    // Simpan nilai milis terbaru (reset timer debouncing)
    last_debounce_time = millis();
  }

  // Jika sudah melebihi waktu untuk menunggu debouncing
  if ((millis() - last_debounce_time) > debounce_delay)
  {
    // Jika state button berubah
    if (reading != button_state)
    {
      button_state = reading;

      if (button_state == LOW)
      {
        // Naikkan nilai counter
        counter++;

        oled.clearDisplay();
        oled.setTextColor(WHITE);
        oled.setTextSize(3);
        print_center(String(counter), 0, 20);
        oled.display();
      }
    }
  }

  // Update variable reading untuk iterasi loop berikutnya
  last_button_state = reading;
}

// Fungsi untuk print text ke OLED dengan alignment center
void print_center(const String str, int x, int y)
{
  int16_t x1, y1;
  uint16_t w, h;
  oled.getTextBounds(str, x, y, &x1, &y1, &w, &h);
  oled.setCursor((x-w/2)+(128/2), y);
  oled.print(str);
}

Gambar berikut ini menampilkan hasil penekanan tombol dengan debouncing. Setiap kali penekanan, counter bertambah hanya satu.

Last updated