← Back to Course
Digital Input: Buttons and Switches
🔘 Digital Input: Reading Buttons and Switches with ESP8266
🔘 What You'll Learn:
- 🔌 Wire a button to ESP8266 using internal pull-up (no external resistor)
- 💻 Read button presses with debounce to prevent false triggers
- 💧 Use button for manual irrigation override
- 🚨 Emergency stop button for pump safety
🔌 Button Wiring
Button Wiring (using internal pull-up):
ESP8266 (NodeMCU) Button
══════════════════ ══════
GPIO0 (D3) ───────────────── Button Pin 1
GND ───────────────── Button Pin 2
⚠️ No external resistor needed - using INPUT_PULLUP!
Button NOT pressed → GPIO reads HIGH (1)
Button pressed → GPIO reads LOW (0)
⚠️ GPIO0 is boot mode pin! Button may affect boot.
Use GPIO4 (D2), GPIO5 (D1), or GPIO14 (D5) for buttons instead.
💡 Recommended Pins for Buttons:
- GPIO0 (D3): Works but affects boot (hold LOW during boot = flashing mode)
- GPIO4 (D2), GPIO5 (D1), GPIO14 (D5): Safe choices, no boot issues
- GPIO16 (D0): Only digital input, no pull-up (needs external resistor)
📖 Complete Button Code with Debouncing
#define BUTTON_PIN 4 // GPIO4 (D2)
bool lastState = HIGH;
unsigned long lastDebounce = 0;
const unsigned long DEBOUNCE_DELAY = 50;
int pressCount = 0;
void setup() {
Serial.begin(115200);
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.println("🔘 Button Monitor Ready - Press to test");
}
void loop() {
bool reading = digitalRead(BUTTON_PIN);
// Debounce logic
if (reading != lastState) {
lastDebounce = millis();
}
if ((millis() - lastDebounce) > DEBOUNCE_DELAY) {
if (reading == LOW) {
pressCount++;
Serial.printf("✅ Button PRESSED! (Count: %d)\n", pressCount);
// Add your action here
}
}
lastState = reading;
}
💧 Farm Application: Manual Irrigation Override
#define BUTTON_PIN 4 // Manual override button
#define PUMP_RELAY 5 // Relay controls pump
bool lastState = HIGH;
unsigned long lastDebounce = 0;
unsigned long pumpStart = 0;
bool pumpRunning = false;
const int WATER_DURATION = 15; // seconds
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(PUMP_RELAY, OUTPUT);
digitalWrite(PUMP_RELAY, HIGH); // Pump OFF
Serial.begin(115200);
Serial.println("💧 Manual pump control ready");
}
void waterPump(int seconds) {
Serial.printf("💧 Pump ON for %d seconds\n", seconds);
digitalWrite(PUMP_RELAY, LOW); // Pump ON
pumpRunning = true;
pumpStart = millis();
while (millis() - pumpStart < seconds * 1000) {
// Check emergency stop during watering
if (digitalRead(BUTTON_PIN) == LOW) {
Serial.println("🛑 Emergency stop!");
break;
}
delay(50);
}
digitalWrite(PUMP_RELAY, HIGH); // Pump OFF
pumpRunning = false;
Serial.println("✅ Pump OFF");
}
void loop() {
bool reading = digitalRead(BUTTON_PIN);
if (reading != lastState) {
lastDebounce = millis();
}
if ((millis() - lastDebounce) > 50) {
if (reading == LOW && !pumpRunning) {
Serial.println("🔘 Manual override triggered!");
waterPump(WATER_DURATION);
}
}
lastState = reading;
delay(10);
}
📖 Farm Application:
A farmer installed a manual override button next to their automated irrigation controller. When they spotted a dry area, pressing the button watered that zone for 15 seconds without changing the automated schedule.
💡 Debounce Explained:
- Mechanical buttons "bounce" for 5-20ms after press
- Without debounce, one press looks like 10-100 presses
- Solution: Wait 50ms after first change before accepting button state
- Result: One press = one action
🎯 Key Takeaways:
- ✅ Use
INPUT_PULLUPfor internal resistor (no external parts) - ✅ Button pressed = LOW (0V), released = HIGH (3.3V)
- ✅ Always debounce (50-100ms delay) to prevent false triggers
- ✅ Safe GPIO for buttons: GPIO4, GPIO5, GPIO14
- ✅ Manual override lets operators intervene when needed
💡 Key Takeaways:
- Apply these concepts directly to your farm or project.
- Take notes on important details for the quiz.
- Use the button below to track your progress.
×