ESP32とWebSocketでリアルタイム双方向IoT通信を実装する方法【ブラウザからセンサー値をライブ取得する完全ガイド】
「ESP32でWebサーバーは作れたけど、センサー値をリアルタイムで表示したい…ページを更新するたびに値が変わるのはちょっと違うよね」
そう思ったことはありませんか?
HTTPのリクエスト・レスポンス方式では、ブラウザ側から「くれ」と言うたびにデータが返ってくる仕組みです。でも温湿度センサーや照度センサーの値をページリロードなしで自動更新したいなら、WebSocketがぴったりなんです。
今回は ESP32 と WebSocket を使って、ブラウザとマイコンが双方向にリアルタイム通信する仕組みをゼロから実装します。センサー値のライブ表示だけでなく、ブラウザからESP32のLEDをON/OFFするところまで一気につくってみましょう! 🚀
WebSocketとHTTPの違いをざっくりおさえよう

まず、WebSocket が何者かをサクッと確認しておきましょう。
HTTPは「客が注文してはじめて料理が出てくる」方式です。ブラウザが「データをください」と送らないかぎり、ESP32は何もしません。一方 WebSocket は「カウンター越しに常に話せる状態」のようなイメージです。一度コネクションを張ったら、ESP32側からもブラウザ側からもいつでも自由にデータを送り合えるんです。
- ✅ ページリロード不要でセンサー値が更新される
- ✅ ブラウザからボタン操作でESP32を即制御できる
- ✅ 複数のブラウザを開いていても全員に同時配信できる
これがWebSocketの強みです。IoTダッシュボードとの相性は抜群ですよね。
必要なもの
- ESP32開発ボード(どのモデルでもOK)
- DHT22 または DHT11 温湿度センサー
- Arduino IDE(ESP32ボードマネージャー導入済み)
- ライブラリ:ESPAsyncWebServer、AsyncTCP、DHT sensor library
ライブラリはArduino IDEのライブラリマネージャーから検索してインストールしてください。「ESPAsyncWebServer」はGitHubからZIPで追加する場合もあります。
回路の配線
DHT22センサーをESP32に接続します。配線はシンプルです。
- DHT22の VCC → ESP32の 3.3V
- DHT22の GND → ESP32の GND
- DHT22の DATA → ESP32の GPIO 4(10kΩプルアップ抵抗を3.3Vとの間に挿入)
また、動作確認用にGPIO 2のオンボードLEDをブラウザから制御します。
ArduinoIDEのスケッチ全体を書いてみよう
では実際のコードです。少し長く見えますが、ブロックごとに分けて読むと意外とシンプルです。
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <DHT.h>
// ─── Wi-Fi設定 ───
const char* ssid = "YOUR_SSID"; // 自分のSSIDに変更
const char* password = "YOUR_PASSWORD"; // 自分のパスワードに変更
// ─── センサー設定 ───
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// ─── LED設定 ───
#define LED_PIN 2
// ─── サーバー&WebSocketの初期化 ───
AsyncWebServer server(80);
AsyncWebSocket ws("/ws"); // /ws エンドポイントでWebSocket通信
unsigned long lastSendTime = 0;
const long sendInterval = 2000; // 2秒ごとにセンサー値を送信
// ─── WebSocketイベントハンドラ ───
void onWsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client,
AwsEventType type, void* arg, uint8_t* data, size_t len) {
if (type == WS_EVT_CONNECT) {
Serial.printf("クライアント接続: id=%u\n", client->id());
} else if (type == WS_EVT_DISCONNECT) {
Serial.printf("クライアント切断: id=%u\n", client->id());
} else if (type == WS_EVT_DATA) {
// ブラウザからのメッセージを受け取る
String msg = "";
for (size_t i = 0; i < len; i++) {
msg += (char)data[i];
}
Serial.printf("受信メッセージ: %s\n", msg.c_str());
// LED制御コマンドを処理
if (msg == "LED_ON") {
digitalWrite(LED_PIN, HIGH);
ws.textAll("LED:ON"); // 全クライアントに状態を通知
} else if (msg == "LED_OFF") {
digitalWrite(LED_PIN, LOW);
ws.textAll("LED:OFF");
}
}
}
// ─── HTMLページ(ブラウザに返す) ───
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ESP32 リアルタイムモニター</title>
<style>
body { font-family: sans-serif; text-align: center; padding: 40px; background: #f0f4f8; }
.card { background: white; border-radius: 12px; padding: 24px;
display: inline-block; min-width: 280px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
h1 { color: #333; }
.value { font-size: 2.5em; font-weight: bold; color: #2196F3; margin: 8px 0; }
button { margin: 8px; padding: 12px 28px; font-size: 1em;
border: none; border-radius: 8px; cursor: pointer; }
#btnOn { background: #4CAF50; color: white; }
#btnOff { background: #f44336; color: white; }
#ledStatus { margin-top: 12px; font-size: 1.1em; }
</style>
</head>
<body>
<div class="card">
<h1>🌡 ESP32 センサーモニター</h1>
<p>温度:<span class="value" id="temp">--</span> ℃</p>
<p>湿度:<span class="value" id="humi">--</span> %</p>
<hr>
<p>LED制御</p>
<button id="btnOn" onclick="sendCmd('LED_ON')">ON</button>
<button id="btnOff" onclick="sendCmd('LED_OFF')">OFF</button>
<p id="ledStatus">LED: --</p>
</div>
<script>
// WebSocket接続(ESP32のIPアドレスを自動取得)
const ws = new WebSocket('ws://' + location.host + '/ws');
ws.onmessage = function(event) {
const msg = event.data;
if (msg.startsWith('SENSOR:')) {
// 例: "SENSOR:25.3,60.1" の形式でパース
const parts = msg.replace('SENSOR:', '').split(',');
document.getElementById('temp').textContent = parts[0];
document.getElementById('humi').textContent = parts[1];
} else if (msg === 'LED:ON') {
document.getElementById('ledStatus').textContent = 'LED: ON 💡';
} else if (msg === 'LED:OFF') {
document.getElementById('ledStatus').textContent = 'LED: OFF 🌑';
}
};
ws.onopen = () => console.log('WebSocket接続OK');
ws.onclose = () => console.log('WebSocket切断');
function sendCmd(cmd) {
if (ws.readyState === WebSocket.OPEN) {
ws.send(cmd);
}
}
</script>
</body>
</html>
)rawliteral";
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
dht.begin();
// Wi-Fi接続
WiFi.begin(ssid, password);
Serial.print("Wi-Fi接続中");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("IPアドレス: ");
Serial.println(WiFi.localIP()); // このIPをブラウザで開く
// WebSocketハンドラを登録
ws.onEvent(onWsEvent);
server.addHandler(&ws);
// ルートにHTMLを返す
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send_P(200, "text/html", index_html);
});
server.begin();
Serial.println("サーバー起動完了");
}
void loop() {
// 定期的にセンサー値をWebSocketで全クライアントに送信
if (millis() - lastSendTime >= sendInterval) {
lastSendTime = millis();
float temp = dht.readTemperature();
float humi = dht.readHumidity();
if (!isnan(temp) && !isnan(humi)) {
String payload = "SENSOR:" + String(temp, 1) + "," + String(humi, 1);
ws.textAll(payload); // 全接続クライアントに送信
Serial.println("送信: " + payload);
} else {
Serial.println("センサー読み取りエラー");
}
}
ws.cleanupClients(); // 切断済みクライアントの後始末
}
ポイントをまとめるとこんな感じです。
- 📡 ws.textAll() を使うと、接続中の全クライアントに同時配信できる
- 🔄 loop() 内で millis() を使い、2秒ごとにセンサー値を自動送信する(delay() は使わない)
- 💬 ブラウザ→ESP32の通信は WS_EVT_DATA イベントで受け取る
- 🧹 ws.cleanupClients() を忘れずに呼ぶ。メモリリーク対策に重要です
動かしてみよう!確認手順
コードを書き込んだら、以下の手順で動作確認してみましょう。
STEP 1:シリアルモニタを開いて Wi-Fi 接続と IP アドレスを確認する
STEP 2:表示された IP アドレス(例: 192.168.1.XX)をブラウザのアドレスバーに入力
STEP 3:2秒ごとに温度・湿度の数値がリアルタイムで更新されることを確認
STEP 4:「ON」「OFF」ボタンを押して LED が即座に点灯・消灯することを確認
複数のタブやスマホのブラウザからも同じIPに接続してみてください。どのクライアントからボタンを押しても、全員の画面に LED の状態変化が反映されるはずです。これが WebSocket の双方向・多クライアント通信の醍醐味ですよね 😊
よくあるトラブルと対処法
センサー値が「–」のまま変わらない
DHT センサーの読み取りに失敗しています。シリアルモニタで「センサー読み取りエラー」が出ていたら配線を再確認しましょう。プルアップ抵抗(10kΩ)が抜けているケースが多いです。
ページは開けるけどリアルタイム更新されない
ブラウザのコンソール(F12)を開いて「WebSocket接続OK」が表示されているか確認しましょう。「ws://」で接続しているかどうかも重要です。HTTPSが有効な環境では「wss://」が必要な場合があります。
ESPAsyncWebServer のビルドエラーが出る
AsyncTCP ライブラリが入っていない場合に起きやすいです。Arduino IDE のライブラリマネージャーで「AsyncTCP」を検索してインストールしてください。
まとめ
今回は ESP32 と WebSocket を組み合わせて、センサー値のリアルタイム表示とブラウザからのLED制御を実装しました。
- ✅ WebSocket は HTTP と違い、一度つなげば双方向通信がずっと続く
- ✅
ws.textAll()で全クライアントへ同時配信できる - ✅ ブラウザ側は標準の JavaScript API だけで動く(追加ライブラリ不要!)
HTTPだけのIoTダッシュボードと比べると、操作感がまるで違います。一度リアルタイム更新の気持ちよさを体験してしまうと、もう戻れないかもしれません(笑)。
次のステップとしては、センサーの種類を増やしたり、Chart.js でグラフ表示にしたりするとさらに実用的なダッシュボードになりますよ。ぜひ試してみてください! 🎉
こちらも読まれています
📡 Arduinoをもっと深く学ぼう!
Arduino・ラズパイ・ロボットプログラミングを体系的に学びたい方へ。おすすめのUdemyコースや電子部品もまとめています。
📚 関連商品・おすすめ書籍
※本記事にはアフィリエイトリンクが含まれます。





