Community project

WiFi Button Status Monitor

Arduino

Published August 13, 2026

ESP32
Photo of WiFi Button Status MonitorGenerated with AI

This project turns an ESP32 into a WiFi-connected button status monitor. A push button wired to the microcontroller broadcasts its state over a local web interface, displaying whether the button is pressed or released with real-time updates.

The guide includes a complete wiring diagram, parts list, and ready-to-use firmware with debouncing logic. Builders will get a responsive status page accessible from any device on the network, plus all assembly steps needed to get the monitor running in minutes.

Wiring diagram

Interactive · read-only
Wiring diagram for WiFi Button Status Monitor

Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.

Parts list

Bill of materials
ComponentQtyNotes
Push ButtonMomentary, normally open1Momentary push button switch

Assembly

3 steps
  1. Power off the ESP32

    Disconnect the ESP32 DevKit v1 from USB before placing or wiring the button.

    • Tip: Use a normally-open momentary push button or the two switch terminals of a tactile button.
    • Do not connect GPIO21 to 3.3 V for this design; the firmware uses the ESP32's internal pull-up resistor.
  2. Wire the button

    Connect one button terminal to ESP32 GPIO21. Connect the opposite button terminal to an ESP32 GND pin. The button is active low: pressing it joins GPIO21 to ground.

    • Tip: For a 4-leg tactile switch, the two legs on each side are internally paired; use one leg from each opposite side.
    • Tip: No external resistor is required because INPUT_PULLUP is enabled in firmware.
    • Ensure the button connection is only between GPIO21 and GND.
  3. Power and open the status page

    Reconnect USB to power the ESP32. After deploying the firmware, connect a phone or computer to Wi-Fi network ESP32-Button using password pressrelease, then browse to http://192.168.4.1/.

    • Tip: The page refreshes once per second and displays either Button pressed or Button released.
    • This is a local ESP32 Wi-Fi network, not your home Wi-Fi.

Pin assignments

Board wiring reference
PinConnectionType
GPIO 21button_1 SIGNALdigital
GNDbutton_1 GNDground

Firmware

ESP32
main.cppDeploy to device
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>


// Forward declarations
void handleRoot();
void handleState();
void updateButton();

constexpr int BUTTON_PIN = 21;
const char *AP_SSID = "ESP32-Button";
const char *AP_PASSWORD = "pressrelease";

WebServer server(80);

bool stablePressed = false;
bool lastRawPressed = false;
unsigned long rawChangedAt = 0;
constexpr unsigned long DEBOUNCE_MS = 30;

const char PAGE[] PROGMEM = R"HTML(
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta http-equiv="refresh" content="1">
  <title>ESP32 Button</title>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 34rem; margin: 15vh auto; padding: 1rem; text-align: center; }
    .state { font-size: 2rem; font-weight: 700; padding: 1.2rem; border-radius: .6rem; }
    .pressed { color: #fff; background: #167a36; }
    .released { color: #2600e4ff; background: #303683ff; }
    small { display: block; margin-top: 1rem; color: #555; }
  </style>
</head>
<body>
  <h1>ESP32 Button</h1>
  <div id="state" class="state released">Loading…</div>
  <small>This page refreshes once per second.</small>
  <script>
    fetch('/state').then(r => r.json()).then(s => {
      const box = document.getElementById('state');
      box.textContent = s.pressed ? 'Button pressed' : 'Button released';
      box.className = 'state ' + (s.pressed ? 'pressed' : 'released');
    }).catch(() => {
      document.getElementById('state').textContent = 'Connection error';
    });
  </script>
</body>
</html>
)HTML";

void handleRoot() {
  server.send_P(200, "text/html", PAGE);
}

void handleState() {
  server.send(200, "application/json", stablePressed ? "{\"pressed\":true}" : "{\"pressed\":false}");
}

void updateButton() {
  const bool rawPressed = digitalRead(BUTTON_PIN) == LOW;
  const unsigned long now = millis();

  if (rawPressed != lastRawPressed) {
    lastRawPressed = rawPressed;
    rawChangedAt = now;
  }

  if (now - rawChangedAt >= DEBOUNCE_MS && stablePressed != rawPressed) {
    stablePressed = rawPressed;
  }
}

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  stablePressed = digitalRead(BUTTON_PIN) == LOW;
  lastRawPressed = stablePressed;
  rawChangedAt = millis();

  WiFi.mode(WIFI_AP);
  WiFi.softAP(AP_SSID, AP_PASSWORD);

  server.on("/", HTTP_GET, handleRoot);
  server.on("/state", HTTP_GET, handleState);
  server.begin();
}

void loop() {
  updateButton();
  server.handleClient();
}

“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik