Atelier.

atelier · lecții

Lecția 05 20 min începător 3 întrebări quiz

Butoane — cum citește ESP32 când le apeși

Pull-up, debouncing și de ce un pin care plutește citește random. Plus de ce butonul VIA are debounce de 2 secunde.

Ce înveți

  • Ce înseamnă HIGH și LOW pe un pin de ESP32
  • De ce ai nevoie de pull-up (intern sau extern)
  • Cum scapi de debouncing fără bibliotecă
  • Cum citești un buton în 5 linii de cod

Ce vede ESP32 când apeși un buton

ESP32 are pini care pot fi INPUT — adică citesc, nu scriu. Un pin INPUT vede doar două stări:

  • HIGH — tensiune apropiată de 3.3V (1 logic)
  • LOW — tensiune apropiată de 0V (1 logic invers, adică 0)

Butonul nu face decât să conecteze sau să deconecteze două fire. Sarcina ta e să aranjezi circuitul ca apăsarea să schimbe pinul de la HIGH la LOW (sau invers).

Problema: pinul care plutește

Conectezi un buton între pinul GPIO4 și GND. Când apeși, pinul e clar la 0V — LOW. Dar când nu apeși, ce vede pinul?

Nimic. Pinul nu-i conectat la nimic. Plutește în aer și prinde zgomot electromagnetic din jur — de la WiFi, de la lampa fluorescentă, de la mâna ta. Citește random: o dată HIGH, o dată LOW, fără logică.

Pin care plutește vs pin cu pull-up

Soluția: îl ții de o parte ca să nu plutească. Asta-i pull-up.

Pull-up extern — cu rezistor

Pui un rezistor de 10kΩ între pinul GPIO și 3.3V. Așa:

  • Buton neapăsat: pinul e tras spre 3.3V prin rezistor → HIGH.
  • Buton apăsat: pinul e legat direct la GND → LOW. Rezistorul lasă puțin curent (3.3V / 10000Ω = 0.33mA) — nesemnificativ.

Logica e inversată: apăsat = LOW, neapăsat = HIGH. Ciudat la început, dar te obișnuiești.

Pull-up intern — mai simplu

ESP32 are rezistoare de pull-up chiar înăuntrul cipului, pe fiecare pin. Le activezi din cod cu INPUT_PULLUP. Fără rezistor extern, fără fire în plus.

pinMode(4, INPUT_PULLUP);  // GPIO4 cu pull-up activ intern

Restul circuitului devine: buton între GPIO4 și GND. Atât.

Folosește mereu pull-up intern când poți. Mai puțin de cablat, mai puține surse de eroare.

Schemă buton cu INPUT_PULLUP

Debouncing — țopăiala contactului

Apeși butonul o singură dată. ESP32 vede patru sau cinci apăsări. De ce?

Butonul e mecanic. Când apeși, contactele metalice nu se ating instant și curat — vibrează 5-10 milisecunde înainte să stea liniștite. În acele 10ms, ESP32 citește pinul de mii de ori și vede HIGH-LOW-HIGH-LOW-HIGH. Crede că-i un click rapid de cinci ori.

Asta-i bouncing (“țopăiala”) și soluția e debouncing.

Soluția — ignoră apăsările prea apropiate

Cel mai simplu debounce: după ce ai văzut o apăsare, ignoră tot ce vine în următoarele 50ms. Pseudocod:

unsigned long ultima_apasare = 0;
const int DEBOUNCE_MS = 50;

void loop() {
  if (digitalRead(4) == LOW) {
    if (millis() - ultima_apasare > DEBOUNCE_MS) {
      // aici e apăsarea reală
      ultima_apasare = millis();
      face_ceva();
    }
  }
}

50ms e suficient pentru orice buton normal. Tu nu poți apăsa de două ori mai repede de atât oricum.

Provocare — de ce VIA are 2000ms

În firmware-ul VIA (semaforul de la concurs) butonul nu are debounce de 50ms. Are 2000ms — 40 de ori mai mult.

De ce așa de mare? La concurs, dacă cineva stă cu degetul apăsat continuu pe buton, ESP32 ar declanșa o nouă rundă imediat ce s-a terminat cea veche. Cu 2 secunde între apăsări valide, sistemul îl forțează pe jucător să ridice degetul și să apese din nou — așa cum se cuvine la o probă.

Nu-i debounce mecanic. E anti-gaming. Aceeași tehnică, alt scop.

Ce ai învățat

  • Pinii INPUT văd doar HIGH sau LOW — nimic intermediar.
  • Fără pull-up, un pin neconectat plutește și citește random.
  • INPUT_PULLUP în cod e mai ușor decât rezistor extern.
  • Butoanele mecanice țopăie 5-10ms; debounce-ul îți rezolvă problema în cinci linii de cod.
  • Timpul de debounce nu-i magic: îl alegi după ce vrei tu să facă butonul.

După lecția asta, poți

  • Conectezi orice buton la ESP32 cu doar 2 fire (S + GND) fără rezistor extern
  • Înțelegi de ce INPUT_PULLUP citește HIGH când nu apeși, LOW când apeși
  • Recunoști când contorul sare prea repede (bouncing) și-l fix-uiești cu millis()
  • Folosești orice modul de buton cu 3 pini ignorând VCC-ul (e inutil cu pull-up intern)