Community project

ESP32 Control Dashboard

ESP32
Photo of ESP32 Control Dashboard

Arya Dwi Nugraha Nugraha

Published October 4, 2026

The ESP32 Control Dashboard is a web-based interface for monitoring and controlling connected devices from any browser. Built on the ESP32 microcontroller, this project combines a responsive HTML dashboard with embedded firmware to create a local control system that runs directly on the device.

This guide provides everything needed to build the dashboard: a complete wiring diagram for connecting peripherals, a full parts list, the firmware code with web interface styling, and step-by-step assembly instructions. Makers will learn how to serve a modern web interface from an ESP32, handle real-time updates, and create an intuitive control panel for IoT applications.

Wiring diagram

Deploy the firmware

schematik_esp32.inoOpen in Schematik
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Nama Anda — Portfolio</title>
  <meta name="description" content="Portfolio pribadi Nama Anda." />

  <style>
    :root {
      --bg: #0b1020;
      --surface: rgba(20, 28, 52, 0.72);
      --text: #f5f7ff;
      --muted: #aab4d0;
      --primary: #7c5cff;
      --secondary: #21d4b4;
      --border: rgba(255, 255, 255, 0.12);
      --shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
    }

    * { box-sizing: border-box; }
    html { scroll-behavior: smooth; }

    body {
      margin: 0;
      font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      color: var(--text);
      background:
        radial-gradient(circle at 15% 10%, rgba(124, 92, 255, 0.22), transparent 30rem),
        radial-gradient(circle at 85% 20%, rgba(33, 212, 180, 0.13), transparent 24rem),
        var(--bg);
      line-height: 1.6;
    }

    a { color: inherit; text-decoration: none; }
    .container { width: min(1120px, calc(100% - 40px)); margin: auto; }

    /* Navbar */
    nav {
      position: sticky;
      top: 0;
      z-index: 20;
      backdrop-filter: blur(14px);
      background: rgba(11, 16, 32, 0.7);
      border-bottom: 1px solid var(--border);
    }

    .nav-inner {
      min-height: 72px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 24px;
    }

    .logo {
      font-weight: 800;
      font-size: 1.2rem;
      letter-spacing: -0.04em;
    }

    .logo span { color: var(--secondary); }

    .nav-links {
      display: flex;
      gap: 22px;
      color: var(--muted);
      font-size: 0.94rem;
    }

    .nav-links a:hover { color: var(--text); }

    /* Hero */
    .hero {
      min-height: calc(100vh - 72px);
      display: grid;
      align-items: center;
      padding: 70px 0;
    }

    .hero-grid {
      display: grid;
      grid-template-columns: 1.35fr 0.65fr;
      gap: 56px;
      align-items: center;
    }

    .badge {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      padding: 8px 13px;
      border: 1px solid var(--border);
      background: rgba(255,255,255,0.05);
      border-radius: 999px;
      color: var(--secondary);
      font-size: 0.9rem;
    }

    .dot {
      width: 8px;
      height: 8px;
      background: var(--secondary);
      border-radius: 50%;
      box-shadow: 0 0 14px var(--secondary);
    }

    h1 {
      font-size: clamp(2.8rem, 7vw, 5.7rem);
      line-height: 1.03;
      letter-spacing: -0.07em;
      margin: 18px 0;
    }

    h1 .gradient {
      background: linear-gradient(100deg, #9d8cff, #54ebd0);
      -webkit-background-clip: text;
      background-clip: text;
      color: transparent;
    }

    .hero p {
      max-width: 620px;
      color: var(--muted);
      font-size: 1.1rem;
      margin-bottom: 30px;
    }

    .buttons { display: flex; flex-wrap: wrap; gap: 12px; }

    .btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      padding: 13px 19px;
      border-radius: 12px;
      border: 1px solid var(--border);
      font-weight: 700;
      transition: 0.2s ease;
    }

    .btn-primary {
      background: linear-gradient(135deg, var(--primary), #9b6cff);
      border-color: transparent;
    }

    .btn:hover { transform: translateY(-3px); }

    .btn-secondary {
      background: rgba(255,255,255,0.05);
    }

    .profile-card {
      padding: 22px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 28px;
      box-shadow: var(--shadow);
    }

    .profile-image {
      aspect-ratio: 1/1;
      display: grid;
      place-items: center;
      overflow: hidden;
      border-radius: 20px;
      background: linear-gradient(135deg, #3e2e92, #163f67);
      font-size: 7rem;
    }

    .profile-card h3 { margin: 18px 0 4px; }
    .profile-card p { color: var(--muted); margin: 0; }

    /* Content */
    section { padding: 90px 0; }

    .section-label {
      color: var(--secondary);
      font-weight: 800;
      font-size: 0.86rem;
      letter-spacing: 0.12em;
      text-transform: uppercase;
    }

    h2 {
      font-size: clamp(2rem, 4vw, 3.3rem);
      line-height: 1.1;
      letter-spacing: -0.05em;
      margin: 12px 0 18px;
    }

    .section-intro {
      max-width: 650px;
      color: var(--muted);
      margin-bottom: 36px;
    }

    .about-grid, .contact-box {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 26px;
    }

    .card {
      padding: 26px;
      border: 1px solid var(--border);
      border-radius: 20px;
      background: var(--surface);
    }

    .card p { color: var(--muted); }

    .skills {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      margin-top: 20px;
    }

    .skill {
      padding: 8px 12px;
      color: #dfe5ff;
      background: rgba(124, 92, 255, 0.14);
      border: 1px solid rgba(124, 92, 255, 0.25);
      border-radius: 999px;
      font-size: 0.9rem;
    }

    .projects {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 20px;
    }

    .project {
      overflow: hidden;
      border: 1px solid var(--border);
      border-radius: 20px;
      background: var(--surface);
      transition: transform 0.25s ease, border-color 0.25s ease;
    }

    .project:hover {
      transform: translateY(-7px);
      border-color: rgba(84, 235, 208, 0.6);
    }

    .project-cover {
      height: 180px;
      display: grid;
      place-items: center;
      font-size: 3.5rem;
      background: linear-gradient(135deg, #34247b, #1a6a72);
    }

    .project-content { padding: 22px; }
    .project h3 { margin: 0 0 8px; }
    .project p { margin: 0 0 18px; color: var(--muted); }

    .project-link {
      color: var(--secondary);
      font-weight: 700;
    }

    .contact-box {
      padding: 34px;
      align-items: center;
      background: linear-gradient(135deg, rgba(124, 92, 255, 0.2), rgba(33, 212, 180, 0.1));
      border: 1px solid var(--border);
      border-radius: 24px;
    }

    .contact-links {
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      justify-content: flex-end;
    }

    footer {
      border-top: 1px solid var(--border);
      padding: 26px 0;
      color: var(--muted);
      font-size: 0.9rem;
      text-align: center;
    }

    @media (max-width: 820px) {
      .hero-grid, .about-grid, .contact-box { grid-template-columns: 1fr; }
      .projects { grid-template-columns: 1fr; }
      .profile-card { max-width: 430px; }
      .contact-links { justify-content: flex-start; }
    }

    @media (max-width: 600px) {
      .nav-links { display: none; }
      .hero { padding: 48px 0; }
      section { padding: 68px 0; }
    }
  </style>
</head>

<body>
  <nav>
    <div class="container nav-inner">
      <a class="logo" href="#home">nama<span>.</span></a>
      <div class="nav-links">
        <a href="#tentang">Tentang</a>
        <a href="#proyek">Proyek</a>
        <a href="#kontak">Kontak</a>
      </div>
    </div>
  </nav>

  <main>
    <section id="home" class="hero">
      <div class="container hero-grid">
        <div>
          <div class="badge"><span class="dot"></span> Tersedia untuk proyek baru</div>
          <h1>Halo, saya <span class="gradient">Nama Anda.</span></h1>
          <p>
            Seorang UI/UX Designer dan Front-End Developer yang membuat pengalaman
            digital sederhana, cepat, dan nyaman digunakan.
          </p>

          <div class="buttons">
            <a class="btn btn-primary" href="#proyek">Lihat proyek saya</a>
            <a class="btn btn-secondary" href="#kontak">Hubungi saya</a>
          </div>
        </div>

        <aside class="profile-card">
          <div class="profile-image">👨‍💻</div>
          <h3>Nama Anda</h3>
          <p>UI/UX Designer · Front-End Developer</p>
        </aside>
      </div>
    </section>

    <section id="tentang">
      <div class="container">
        <div class="section-label">Tentang Saya</div>
        <h2>Membuat desain yang indah dan mudah dipakai.</h2>

        <div class="about-grid">
          <div class="card">
            <p>
              Saya membantu bisnis dan personal brand membangun website yang terlihat
              profesional serta mudah dipahami pengunjung. Fokus saya adalah desain
              yang jelas, responsif, dan memiliki tujuan.
            </p>
          </div>

          <div class="card">
            <strong>Keahlian</strong>
            <div class="skills">
              <span class="skill">UI Design</span>
              <span class="skill">UX Research</span>
              <span class="skill">Figma</span>
              <span class="skill">HTML & CSS</span>
              <span class="skill">JavaScript</span>
              <span class="skill">Responsive Design</span>
            </div>
          </div>
        </div>
      </div>
    </section>

    <section id="proyek">
      <div class="container">
        <div class="section-label">Pilihan Karya</div>
        <h2>Proyek yang pernah saya kerjakan.</h2>
        <p class="section-intro">
          Ganti proyek di bawah ini dengan karya asli Anda, termasuk gambar, deskripsi, dan tautannya.
        </p>

        <div class="projects">
          <article class="project">
            <div class="project-cover">🛍️</div>
            <div class="project-content">
              <h3>Website Toko Online</h3>
              <p>Desain toko online yang membuat pencarian produk dan proses belanja lebih mudah.</p>
              <a class="project-link" href="#" target="_blank">Lihat proyek →</a>
            </div>
          </article>

          <article class="project">
            <div class="project-cover">📊</div>
            <div class="project-content">
              <h3>Dashboard Keuangan</h3>
              <p>Dashboard ringkas untuk memantau pemasukan, pengeluaran, dan target bulanan.</p>
              <a class="project-link" href="#" target="_blank">Lihat proyek →</a>
            </div>
          </article>

          <article class="project">
            <div class="project-cover">☕</div>
            <div class="project-content">
              <h3>Branding Kedai Kopi</h3>
              <p>Landing page untuk memperkenalkan menu, lokasi, dan cerita sebuah kedai kopi.</p>
              <a class="project-link" href="#" target="_blank">Lihat proyek →</a>
            </div>
          </article>
        </div>
      </div>
    </section>

    <section id="kontak">
      <div class="container">
        <div class="contact-box">
          <div>
            <div class="section-label">Kontak</div>
            <h2>Mari bekerja sama.</h2>
            <p>Anda punya ide proyek? Kirim pesan dan saya akan membalas secepatnya.</p>
          </div>

          <div class="contact-links">
            <a class="btn btn-primary" href="mailto:emailanda@gmail.com">Email saya</a>
            <a class="btn btn-secondary" href="https://linkedin.com/" target="_blank">LinkedIn</a>
            <a class="btn btn-secondary" href="https://github.com/" target="_blank">GitHub</a>
          </div>
        </div>
      </div>
    </section>
  </main>

  <footer>
    © <span id="year"></span> Nama Anda. Dibuat dengan penuh perhatian.
  </footer>

  <script>
    document.getElementById("year").textContent = new Date().getFullYear();
  </script>
</body>
</html>

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