ESP32でWebサーバーを立ち上げてブラウザからGPIOを制御する方法【ArduinoIDE対応・Wi-Fi IoT入門】
「ESP32でLEDをWi-Fi経由でON/OFFしたい…でもWebサーバーなんて難しそう」
そんな印象を持っている方、多いんじゃないでしょうか。でも実は、ESP32のWebサーバー機能はArduino IDEだけで完結できて、思ったよりずっとシンプルに実装できるんです。
今回は、ESP32でWebサーバーを立ち上げ、ブラウザのボタンクリックでGPIOをリアルタイム制御する方法をゼロから解説します。完成すると、スマホのブラウザからでも家のLEDをON/OFFできる、立派なIoTデバイスの出来上がりです😊
この記事で作るもの・対象読者

完成イメージはこんな感じです。
- ✅ 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サーバーになる感じです。
- ESP32が自宅のWi-Fiに接続する(STAモード)
- ESP32がIPアドレスを取得する(例:192.168.1.105)
- ESP32内部でHTTPリクエストを待ち受けるサーバーが起動する
- ブラウザから「192.168.1.105」にアクセスすると、ESP32がHTMLページを返す
- ブラウザのボタンを押すと「192.168.1.105/on」などのURLにリクエストが飛ぶ
- ESP32がそのリクエストを受けてGPIOをON/OFFする
HTTPサーバーという言葉は難しそうに聞こえますが、「URLを受け取ってHTMLを返す係」だと思ってもらえればOKです。
ESP32ではArduino IDEの標準ライブラリに含まれる WiFi.h と WebServer.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アドレスが出れば準備完了です✅
ポイントをまとめるとこんな感じです。
ssidとpasswordは自分の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コースや電子部品もまとめています。
📚 関連商品・おすすめ書籍
※本記事にはアフィリエイトリンクが含まれます。





