「ESP32でLEDをWi-Fi経由でON/OFFしたい…でもWebサーバーなんて難しそう」

そんな印象を持っている方、多いんじゃないでしょうか。でも実は、ESP32のWebサーバー機能はArduino IDEだけで完結できて、思ったよりずっとシンプルに実装できるんです。

今回は、ESP32でWebサーバーを立ち上げ、ブラウザのボタンクリックでGPIOをリアルタイム制御する方法をゼロから解説します。完成すると、スマホのブラウザからでも家のLEDをON/OFFできる、立派なIoTデバイスの出来上がりです😊

この記事で作るもの・対象読者

ESP32 WiFi IoT
ESP32 WiFi IoT / Photo by Tanha Tamanna Syed via Pexels

完成イメージはこんな感じです。

  • ✅ ESP32がWi-Fiルーターに接続する
  • ✅ ESP32が簡単なWebサーバーを起動する
  • ✅ PCやスマホのブラウザからIPアドレスにアクセスする
  • ✅ ブラウザ上のボタンを押すとGPIOがON/OFFされてLEDが点灯・消灯する

対象読者:

  • Arduino IDEでLチカ(Lチカ = LEDをチカチカ点滅させること)ができるレベルの初心者〜中級者
  • ESP32のWi-Fi機能を初めて使う方
  • IoT・スマートホームに興味がある方

シリアル通信やPythonとの連携は使いません。ESP32単体でブラウザ制御が完結するのが今回のポイントです。

用意するもの

ハードウェアはとてもシンプルです。

  • ESP32開発ボード(ESP32-DevKitC や ESP-WROOM-32 など)
  • LED × 1本
  • 抵抗(220Ω〜330Ω)× 1本
  • ジャンパーワイヤー適量
  • ブレッドボード
  • USBケーブル(PCとESP32を接続する用)

ソフトウェアはArduino IDEがインストール済みであることが前提です。ESP32のボードパッケージが未インストールの場合は、Arduino IDEの「ボードマネージャー」から「esp32 by Espressif Systems」を検索してインストールしておいてください。

回路を組んでみよう

まずはLEDの回路を作ります。配線はシンプルです。

  • ESP32のGPIO2(多くのボードで内蔵LEDと共通)→ 抵抗(330Ω)→ LEDのアノード(長い足)
  • LEDのカソード(短い足)→ GND

GPIO2番ピンを今回の制御対象として使います。外付けLEDをつなぐ場合は上記の通り抵抗を必ず挟んでください。内蔵LEDだけで確認する場合は配線不要です。

⚠️ 注意: ESP32のGPIOは3.3V動作です。5V系のデバイスを直結しないよう気をつけてください。

ESP32のWebサーバーの仕組みをざっくり理解しよう

コードを書く前に、仕組みを少しだけ確認しておきましょう。

イメージとしては、ESP32がミニチュアのHTTPサーバーになる感じです。

  1. ESP32が自宅のWi-Fiに接続する(STAモード)
  2. ESP32がIPアドレスを取得する(例:192.168.1.105)
  3. ESP32内部でHTTPリクエストを待ち受けるサーバーが起動する
  4. ブラウザから「192.168.1.105」にアクセスすると、ESP32がHTMLページを返す
  5. ブラウザのボタンを押すと「192.168.1.105/on」などのURLにリクエストが飛ぶ
  6. ESP32がそのリクエストを受けてGPIOをON/OFFする

HTTPサーバーという言葉は難しそうに聞こえますが、「URLを受け取ってHTMLを返す係」だと思ってもらえればOKです。

ESP32ではArduino IDEの標準ライブラリに含まれる WiFi.hWebServer.h(または WiFiServer クラス)を使って実装します。

コードを書こう【ステップ別に解説】

STEP 1:まずWi-Fi接続だけ確認する

いきなりWebサーバーを書く前に、Wi-Fi接続単体で動作確認をしておくのがオススメです。途中で詰まったときに「Wi-Fiが悪いのかサーバーが悪いのか」を切り分けやすくなります。

// Wi-Fi接続確認スケッチ
#include <WiFi.h>

const char* ssid     = "あなたのSSID";      // Wi-FiのSSID(ネットワーク名)
const char* password = "あなたのパスワード"; // Wi-Fiのパスワード

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println();
  Serial.print("Wi-Fiに接続中...");

  WiFi.begin(ssid, password); // Wi-Fi接続開始

  // 接続が完了するまでループで待機
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.println("接続成功!");
  Serial.print("IPアドレス: ");
  Serial.println(WiFi.localIP()); // 取得したIPアドレスを表示
}

void loop() {
  // 今はここは空でOK
}

このスケッチを書き込んで、シリアルモニタ(ボーレート115200)を開いてみてください。「接続成功!」と表示されてIPアドレスが出れば準備完了です✅

ポイントをまとめるとこんな感じです。

  • ssidpassword は自分のWi-Fiのものに書き換える
  • WiFi.begin() で接続開始、WiFi.status() != WL_CONNECTED でつながるまで待機
  • WiFi.localIP() でESP32に割り当てられたIPアドレスが取得できる

STEP 2:WebServerライブラリでHTTPサーバーを起動する

Wi-Fi接続が確認できたら、いよいよWebサーバーを追加します。ESP32のArduinoパッケージには WebServer.h という便利なライブラリが含まれています。

// シンプルなWebサーバー(GPIO制御なし・動作確認用)
#include <WiFi.h>
#include <WebServer.h>

const char* ssid     = "あなたのSSID";
const char* password = "あなたのパスワード";

WebServer server(80); // ポート80でサーバーを作成(HTTPの標準ポート)

// ルートURL「/」にアクセスされたときの処理
void handleRoot() {
  server.send(200, "text/plain", "Hello from ESP32!");
}

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("接続完了!");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot); // 「/」にアクセスされたらhandleRootを呼ぶ
  server.begin();              // サーバー起動!
  Serial.println("Webサーバー起動完了");
}

void loop() {
  server.handleClient(); // クライアントからのリクエストを処理し続ける
}

書き込んでシリアルモニタに表示されたIPアドレスをブラウザのアドレスバーに入力してみてください。「Hello from ESP32!」と表示されたら成功です🎉

server.handleClient()loop() 内に書くことで、ESP32がずっとリクエストを待ち受け続けられるようになっています。ここ、重要です!

STEP 3:GPIO制御付きWebサーバーを完成させる

ここからが本番です。ブラウザのボタンでGPIO2のLEDをON/OFFできるWebサーバーを作ります。

// GPIO制御Webサーバー(完成版)
#include <WiFi.h>
#include <WebServer.h>

const char* ssid     = "あなたのSSID";
const char* password = "あなたのパスワード";

const int LED_PIN = 2; // 制御するGPIOピン番号
bool ledState = false; // LEDの現在の状態(false=OFF, true=ON)

WebServer server(80);

// ブラウザに返すHTMLページを生成する関数
String buildHtmlPage() {
  String html = "<!DOCTYPE html><html lang='ja'>";
  html += "<head><meta charset='UTF-8'>";
  html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
  html += "<title>ESP32 GPIO制御</title>";
  // シンプルなCSSでボタンを見やすくする
  html += "<style>";
  html += "body{font-family:sans-serif;text-align:center;padding:40px;background:#f0f4f8;}";
  html += "h1{color:#333;}";
  html += ".btn{display:inline-block;padding:16px 40px;font-size:20px;border:none;";
  html += "border-radius:8px;cursor:pointer;margin:10px;color:#fff;}";
  html += ".on{background:#4CAF50;} .off{background:#e53e3e;}";
  html += ".status{font-size:18px;margin:20px;color:#555;}";
  html += "</style></head>";
  html += "<body>";
  html += "<h1>🔦 ESP32 LED コントロール</h1>";

  // 現在のLED状態を表示
  html += "<p class='status'>現在の状態: <strong>";
  html += ledState ? "ON(点灯中)" : "OFF(消灯中)";
  html += "</strong></p>";

  // ON/OFFボタン
  html += "<a href='/on'><button class='btn on'>LED ON</button></a>";
  html += "<a href='/off'><button class='btn off'>LED OFF</button></a>";
  html += "</body></html>";
  return html;
}

// 「/」にアクセスされたとき → 現在状態を表示するページを返す
void handleRoot() {
  server.send(200, "text/html", buildHtmlPage());
}

// 「/on」にアクセスされたとき → LEDをONにしてページを返す
void handleOn() {
  ledState = true;
  digitalWrite(LED_PIN, HIGH);
  Serial.println("LED ON");
  server.send(200, "text/html", buildHtmlPage());
}

// 「/off」にアクセスされたとき → LEDをOFFにしてページを返す
void handleOff() {
  ledState = false;
  digitalWrite(LED_PIN, LOW);
  Serial.println("LED OFF");
  server.send(200, "text/html", buildHtmlPage());
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);       // GPIO2を出力モードに設定
  digitalWrite(LED_PIN, LOW);     // 起動時はLED OFF

  WiFi.begin(ssid, password);
  Serial.print("Wi-Fi接続中");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.print("接続完了!アクセスURL: http://");
  Serial.println(WiFi.localIP());

  // URLのルーティング設定
  server.on("/",    handleRoot); // トップページ
  server.on("/on",  handleOn);   // LED ONページ
  server.on("/off", handleOff);  // LED OFFページ

  server.begin();
  Serial.println("Webサーバー起動完了!");
}

void loop() {
  server.handleClient(); // リクエストを受け付け続ける
}

このコードのポイントをまとめるとこんな感じです。

  • buildHtmlPage():HTMLを文字列で生成して返す関数。ledState の値によって表示が変わる
  • server.on():URLのルーティング設定。URLパスとそれを処理する関数を紐付ける
  • handleOn() / handleOff():GPIO操作 + HTMLページ返却を一緒に行う
  • ボタンは <a href='/on'> のリンクになっているので、タップするだけでURLへアクセスされる

動作確認をしよう


スケッチを書き込んだら、シリアルモニタを確認してみてください。こんな表示が出るはずです。

表示されたIPアドレス(例:192.168.1.105)をブラウザのアドレスバーに入力してアクセスしてみましょう。

  • 「LED ON」ボタンを押す → GPIO2がHIGHになりLEDが点灯
  • 「LED OFF」ボタンを押す → GPIO2がLOWになりLEDが消灯
  • 現在の状態が「ON(点灯中)」「OFF(消灯中)」とページ上に表示される

スマホのブラウザでも同じIPアドレスにアクセスすれば、スマホからでも制御できます!同じWi-Fiネットワークにつながっていることが条件です📶

複数のGPIOを制御したい場合の拡張方法

「LEDが1本だけじゃ物足りない!」という方向けに、複数GPIOを制御する拡張パターンも紹介しておきます。

// 複数GPIO制御の拡張パターン(抜粋)
// GPIO2とGPIO4の2本のLEDを制御する例

#include <WiFi.h>
#include <WebServer.h>

const char* ssid     = "あなたのSSID";
const char* password = "あなたのパスワード";

WebServer server(80);

// GPIO番号と状態をまとめて管理
struct GpioControl {
  int pin;
  bool state;
  String label;
};

// 制御するGPIOをここに追加するだけでOK
GpioControl gpios[] = {
  {2, false, "LED 1"},
  {4, false, "LED 2"}
};
const int GPIO_COUNT = 2;

void setup() {
  Serial.begin(115200);

  for (int i = 0; i < GPIO_COUNT; i++) {
    pinMode(gpios[i].pin, OUTPUT);
    digitalWrite(gpios[i].pin, LOW);
  }

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  Serial.println(WiFi.localIP());

  // 「/toggle/2」「/toggle/4」のようなURLでピン指定トグル
  server.on("/", []() {
    String html = "<!DOCTYPE html><html><head><meta charset='UTF-8'></head><body>";
    html += "<h2>GPIO コントロールパネル</h2>";
    for (int i = 0; i < GPIO_COUNT; i++) {
      html += "<p>" + gpios[i].label + ": <strong>" + (gpios[i].state ? "ON" : "OFF") + "</strong>";
      html += " <a href='/toggle/" + String(gpios[i].pin) + "'><button>切替</button></a></p>";
    }
    html += "</body></html>";
    server.send(200, "text/html", html);
  });

  // 「/toggle/ピン番号」でそのピンをトグルする
  server.on("/toggle", HTTP_GET, []() {
    // ※ URIからパラメータを取得する場合は引数チェックを実装してください
  });

  // 個別にhandlerを登録する方法(確実で初心者向け)
  server.on("/toggle/2", []() {
    gpios[0].state = !gpios[0].state;
    digitalWrite(gpios[0].pin, gpios[0].state ? HIGH : LOW);
    server.sendHeader("Location", "/");
    server.send(302); // トップページにリダイレクト
  });

  server.on("/toggle/4", []() {
    gpios[1].state = !gpios[1].state;
    digitalWrite(gpios[1].pin, gpios[1].state ? HIGH : LOW);
    server.sendHeader("Location", "/");
    server.send(302);
  });

  server.begin();
}

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

このパターンのポイントはここです。

  • GpioControl 構造体でピン番号・状態・ラベルをまとめて管理しているので、GPIOを増やすときは配列に追加するだけ
  • 302リダイレクト を使ってボタン操作後にトップページへ戻すことで、ブラウザのURLが「/toggle/2」で止まらなくなる
  • ラムダ関数([](){})を使うとhandler関数を別で定義しなくてスッキリ書ける

よくあるトラブルと対処法


「書き込んだのにブラウザでつながらない」という場合、チェックするポイントがあります。

  • SSIDとパスワードが正しいか? → 全角スペース混入や大文字小文字ミスに注意
  • PCやスマホが同じWi-Fiにつながっているか? → 5GHz帯と2.4GHz帯が混在する環境ではESP32が接続できない場合がある(ESP32は2.4GHzのみ対応)
  • シリアルモニタのボーレートは115200になっているか? → 文字化けしている場合はボーレート不一致が原因
  • ファイアウォールがブロックしていないか? → 社内LAN・学校のWi-Fiなどはデバイス間通信がブロックされる場合がある
  • ESP32のボードパッケージのバージョンが古くないか? → Arduino IDEのボードマネージャーから最新版に更新してみる

シリアルモニタに「接続完了!アクセスURL: http://…」が表示されているのにブラウザでつながらない場合は、IPアドレスの入力ミスがよくある原因です。「http://」を省かずに入力してみてください。

まとめ

今回の記事でやったことをまとめるとこんな感じです。

  • 📡 ESP32をWi-FiのSTAモードで接続して、IPアドレスを取得する
  • 🌐 WebServer.hライブラリでHTTPサーバーを80番ポートで起動する
  • 🔗 server.on() でURLルーティングを設定し、ハンドラ関数でGPIOを操作する
  • 💻 HTMLをESP32から直接返すことで、ブラウザだけで操作できるUIを実現する
  • 📱 スマホからも同一ネットワーク内であれば制御可能

「Webサーバーを自分で立てる」というと難しそうに聞こえますが、ESP32 + Arduino IDEの組み合わせなら、数十行のコードでブラウザからGPIOを動かすIoTデバイスが完成します。これがWi-Fi IoTの第一歩です!

ぜひ実際に手を動かして試してみてください🔧 次のステップとしては、センサーの値をブラウザにリアルタイム表示する「センサーモニター」や、WebSocketを使ったより応答性の高い制御に挑戦するのがオススメです。一緒に学んでいきましょう!

📡 Arduinoをもっと深く学ぼう!

Arduino・ラズパイ・ロボットプログラミングを体系的に学びたい方へ。おすすめのUdemyコースや電子部品もまとめています。

Arduinoロボット入門のおすすめコース・部品を見る →

📚 関連商品・おすすめ書籍

スッキリわかるPython入門 第2版 (スッキリわかる入門シリーズ)

もしも

スッキリわかるPython入門 第2版 (スッキリわかる入門シリーズ)

初心者に定番のPython入門書

Amazonで見る

Arduinoをはじめよう 第4版 (Make: PROJECTS)

もしも

Arduinoをはじめよう 第4版 (Make: PROJECTS)

Arduino公式推薦の定番入門書

Amazonで見る

徹底攻略! 電子工作&プログラミング Arduinoで学ぶ電子工作完全ガイド

もしも

徹底攻略! 電子工作&プログラミング Arduinoで学ぶ電子工作完全ガイド

電子工作とプログラミングを同時に学べる

Amazonで見る

※本記事にはアフィリエイトリンクが含まれます。

ABOUT ME
やまちゃん
これまで学生と社会人を合わせて5000人以上にプログラミング学習を指導。 ゼロからイチをわかりやすく解説する専門家として活動しており、本業ではArduinoを用いたIoT開発とロボットプログラミングが専門。 Pythonを用いたアプリ開発、ウェブアプリケーションの開発で業務の効率化をサポートしています。