ESP32がWi-Fiにつながらない?WiFi.status()のコードと本当に効く対処法
ESP32がWi-Fiにつながらないときは、WiFi.status()とreasonコードを読みましょう。2.4 GHzやパスワードの問題、ブラウンアウト、setTxPower、再接続、固定IP、eduroamの対処法を解説します。
ESP32がWi-Fiにつながらず、シリアルモニタにはドットが延々と並ぶだけ?原因がボードの故障であることはほとんどありません。よくある原因は、5 GHzのネットワークにつなごうとしている、ネットワーク名やパスワードのちょっとした打ち間違い、Wi-Fiがオンになった瞬間に電圧が落ちる弱い電源、ルーターのセキュリティやチャンネルの設定、学校や職場のエンタープライズネットワークです。うれしいことに、何がうまくいかなかったのかはESP32がすでに教えてくれています。WiFi.status()の値と、接続が切れたときに報告されるreason(理由)コードの読み方を知っていれば十分です。
このガイドでは、まずESP32がネットワークに参加するまでのステップと、各ステップで報告されるステータスコードを見ていきます。続いて、ステータスコードとreasonコードを表示する診断スケッチ、周りのネットワークを一覧にするスキャナー、2.4 GHzの必須条件とデュアルバンドのルーター、ブラウンアウトとsetTxPowerによる対処、自動で復帰する再接続スケッチ、固定IP、学校のネットワーク向けのWPA2-Enterprise接続、そして問題がルーターとボードのどちらにあるのかを数分で切り分けられるスマホのテザリングテストを紹介します。スケッチはArduino IDE用ESP32コアのバージョン2.xと3.xで動き、最後にはRaspberry Pi Pico WとMicroPython用のテストスクリプトもあります。

ESP32はどのようにWi-Fiネットワークにつながるのか?
WiFi.begin()を呼ぶと、ESP32は裏で4つのステップを順に実行します。まず、指定した名前(SSID)のネットワークを探してチャンネルをスキャンします。見つかれば、ルーターと認証(authentication)とアソシエーション(association)を行います。WPA2のネットワークでは続いて4ウェイハンドシェイクが行われ、ここでパスワードが正しいかどうかがわかります。最後に、ルーターのDHCPサーバーにIPアドレスを要求します。WiFi.status()がWL_CONNECTEDを返すのは、この最後のステップまで終わってからです。どのステップで止まっているかがわかれば、原因もほぼわかります。
| ステップ | 何が起きる? | 止まったときのステータスとreason |
|---|---|---|
| 1. スキャン | ESP32が2.4 GHzのチャンネルでネットワーク名を探す | WL_NO_SSID_AVAIL(1)、reason 201 |
| 2. 認証とアソシエーション | ルーターがESP32を受け入れるか拒否する | WL_CONNECT_FAILED(4)またはWL_DISCONNECTED(6)、reason 202または203 |
| 3. 4ウェイハンドシェイク | パスワードから作った鍵を照合する | WL_DISCONNECTED(6)とreason 15、WL_CONNECTION_LOST(5)とreason 204 |
| 4. DHCP | ルーターがESP32にIPアドレスを割り当てる | WL_IDLE_STATUS(0)のまま止まる |
| 接続後 | ルーターがビーコンパケットを送り続ける | 途絶えると:WL_CONNECTION_LOST(5)、reason 200 |
WiFi.status()のコードの意味
WiFi.status()はwl_status_t型の値を返します。そのままシリアルモニタに表示すると、数字しか見えません。下の表は、ESP32 Arduinoコアでの各数字の意味です。注意点があります。0〜6の値は古いArduino WiFiライブラリと同じ名前ですが、ESP32では少し違う場面で現れます。たとえば、WiFi.begin()の直後のステータスは0ではなく6です。ボードがルーターに参加してIPを待っている間に0になり、IPを受け取ると3になります。
| コード | 定数 | 意味 | まず確認すること |
|---|---|---|---|
| 0 | WL_IDLE_STATUS | ルーターに参加したが、まだIPアドレスがない | 数秒なら正常。続くならDHCP |
| 1 | WL_NO_SSID_AVAIL | その名前のネットワークが見つからない | ネットワーク名、2.4 GHz帯、距離 |
| 2 | WL_SCAN_COMPLETED | ネットワークのスキャンが終わった | 接続とは関係なし |
| 3 | WL_CONNECTED | 接続済みでIPアドレスもある | 問題なし |
| 4 | WL_CONNECT_FAILED | ルーターが接続を拒否した | パスワード、セキュリティモード、MACフィルター |
| 5 | WL_CONNECTION_LOST | 確立していた接続が切れた | 電波の強さ、電源、アンテナ |
| 6 | WL_DISCONNECTED | 未接続。まだ試行中か、試行に失敗した | reasonコード |
| 254 | WL_STOPPED | Wi-Fiステーションが停止している(3.xコア) | WiFi.mode(WIFI_STA)を呼ぶ |
| 255 | WL_NO_SHIELD | Wi-Fiがまだ起動していない(2.xコア) | WiFi.mode()またはWiFi.begin()を呼ぶ |
診断スケッチ:ステータスとreasonコードをシリアルモニタに表示する
下のスケッチは20秒間接続を試み、ステータスコードが変わるたびに名前と一緒に表示し、接続が切れるたびにESP-IDFのreasonコードを表示します。つながった場合は、IPアドレス、ゲートウェイ、電波の強さ(RSSI)、チャンネルを表示し、どの場合でもボードのMACアドレスを表示します。ネットワーク名とパスワードを自分のものに書き換え、スケッチを書き込んで、シリアルモニタを115200 baudで開いてください。スケッチの書き込みすらできない場合は、先に書き込みエラーのガイドを読んでください。
1#include <WiFi.h>
2
3const char* WIFI_ADI = "EvAgi_2G"; // Wi-Fi adı, büyük-küçük harfe duyarlı / network name, case-sensitive
4const char* WIFI_SIFRE = "sifre1234"; // Wi-Fi şifresi / Wi-Fi password
5
6// Durum kodunu adına çevir / turn the status code into its name
7const char* durumAdi(wl_status_t durum) {
8 switch (durum) {
9 case WL_IDLE_STATUS: return "WL_IDLE_STATUS";
10 case WL_NO_SSID_AVAIL: return "WL_NO_SSID_AVAIL";
11 case WL_SCAN_COMPLETED: return "WL_SCAN_COMPLETED";
12 case WL_CONNECTED: return "WL_CONNECTED";
13 case WL_CONNECT_FAILED: return "WL_CONNECT_FAILED";
14 case WL_CONNECTION_LOST: return "WL_CONNECTION_LOST";
15 case WL_DISCONNECTED: return "WL_DISCONNECTED";
16 default: return "WL_NO_SHIELD / WL_STOPPED";
17 }
18}
19
20// Bağlantı her koptuğunda sebep kodunu yaz / print the reason code on every disconnect
21void koptugunda(arduino_event_id_t olay, arduino_event_info_t bilgi) {
22 Serial.printf("Koptu / Disconnected, reason: %d\n", (int)bilgi.wifi_sta_disconnected.reason);
23}
24
25void setup() {
26 Serial.begin(115200);
27 delay(1000);
28 WiFi.onEvent(koptugunda, ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
29 WiFi.mode(WIFI_STA); // istasyon modu / station mode
30 WiFi.begin(WIFI_ADI, WIFI_SIFRE); // begin() yalnızca bir kez / call begin() only once
31
32 wl_status_t onceki = WL_NO_SHIELD;
33 unsigned long baslangic = millis();
34 while (WiFi.status() != WL_CONNECTED && millis() - baslangic < 20000) {
35 wl_status_t simdi = WiFi.status();
36 if (simdi != onceki) { // yalnızca değişince yaz / print only when it changes
37 Serial.printf("Durum / Status: %d %s\n", (int)simdi, durumAdi(simdi));
38 onceki = simdi;
39 }
40 delay(500);
41 }
42
43 if (WiFi.status() == WL_CONNECTED) {
44 Serial.print("Bagli / Connected, IP: ");
45 Serial.println(WiFi.localIP());
46 Serial.print("Ag gecidi / Gateway: ");
47 Serial.println(WiFi.gatewayIP());
48 Serial.printf("RSSI: %d dBm, kanal / channel: %d\n", (int)WiFi.RSSI(), (int)WiFi.channel());
49 } else {
50 wl_status_t son = WiFi.status();
51 Serial.printf("Baglanamadi / Failed, son durum / last status: %d %s\n", (int)son, durumAdi(son));
52 }
53 Serial.print("MAC: ");
54 Serial.println(WiFi.macAddress()); // MAC filtresi için / for MAC filtering
55}
56
57void loop() {}最初にDurum / Status: 6 WL_DISCONNECTEDと表示されるのは正常です。接続に成功すると、ステータスは一瞬0になり、そのあと3になります。1のままならネットワークが一度も見つかっていません。Koptu / Disconnected, reason: 15のような行が続くなら、パスワードが間違っている可能性が高いです(DurumとKoptuはトルコ語で「状態」と「切断」)。さらに詳しく見たいときは、Arduino IDEの「ツール」>「Core Debug Level」をWarnかInfoにします。するとコアが切断のたびにreasonの名前も表示します。たとえばReason: 201 - NO_AP_FOUNDです。よく出会うreasonコードは次のとおりです。
| reason | 名前 | 意味 | 対処法 |
|---|---|---|---|
| 2 | AUTH_EXPIRE | 認証がタイムアウトした | 電波が弱いかルーターが混雑している。近づく、ルーターを再起動する |
| 5 | ASSOC_TOOMANY | ルーターの接続台数の上限に達した | 使っていない機器を外すか、上限を上げる |
| 8 | ASSOC_LEAVE | ESP32が自分からネットワークを離れた | コードがWiFi.disconnect()を呼んでいる。これは正常 |
| 15 | 4WAY_HANDSHAKE_TIMEOUT | パスワードのハンドシェイクが完了しなかった | ほとんどはパスワードの間違い。パスワードとセキュリティモードを確認 |
| 200 | BEACON_TIMEOUT | ルーターからのビーコンが届かなくなった | 電波が弱いか電圧が落ちている。アンテナ、電源、setTxPower |
| 201 | NO_AP_FOUND | この名前のネットワークが見つからない | SSIDのつづり、5 GHzのネットワーク、距離、非公開ネットワーク、チャンネル12と13 |
| 202 | AUTH_FAIL | 認証が拒否された | パスワード、またはEnterpriseなど対応していないセキュリティモード |
| 203 | ASSOC_FAIL | ルーターがアソシエーションを拒否した | MACフィルター、または802.11ax専用などの無線モード |
| 204 | HANDSHAKE_TIMEOUT | ハンドシェイクがタイムアウトした | パスワードの間違い、または非常に弱い電波 |
| 210、211 | NO_AP_FOUND_W_COMPATIBLE_SECURITYとNO_AP_FOUND_IN_AUTHMODE_THRESHOLD | ネットワークは見つかったが、セキュリティモードが対応していないか、許可された最低レベルより低い(3.xコア) | ルーターをWPA2-Personal(AES)にするか、WiFi.setMinSecurity()で最低レベルを下げる |
2.4 GHzの必須条件:ESP32には5 GHzのネットワークが見えない
初代ESP32、ESP32-S2、ESP32-S3、ESP32-C3、ESP32-C6は2.4 GHz帯でしか動きません。5 GHzで電波を出しているネットワークはスキャンにまったく出てこず、結果はいつもWL_NO_SSID_AVAILです。家庭用ルーターの多くは両方の帯域で電波を出しています。ルーターが帯域ごとに別の名前を使っている場合(たとえばMyWiFiとMyWiFi_5G)、ESP32には必ず2.4 GHzのほうの名前を指定してください。両方の帯域が同じ名前の場合(スマートコネクト、バンドステアリング、メッシュシステム)、ESP32はたいてい問題なく2.4 GHz側に参加します。そもそも5 GHzの電波が見えないからです。問題が起きるのは、2.4 GHz帯がオフになっているときや、ルーターが端末を5 GHzへ移そうとするときです。ESP32はつながっても数秒後に切れ、それが繰り返されます。その場合は、ルーターの設定で帯域を分けるか、2.4 GHzだけで電波を出すゲストネットワークを作ってください。なお、日本でも2.4 GHz帯の範囲はほぼ同じですが、技適マークのない無線モジュールの使用は電波法で制限されています。
| ボード | Wi-Fiの帯域 | メモ |
|---|---|---|
| ESP32(WROOM-32、DevKit)、ESP32-S2、ESP32-S3 | 2.4 GHzのみ | 802.11 b/g/n |
| ESP32-C3 | 2.4 GHzのみ | 安価なSuper Miniボードでは送信出力を下げる必要がある場合も |
| ESP32-C6 | 2.4 GHzのみ | Wi-Fi 6(802.11ax)に対応。ただし2.4 GHzのみ |
| ESP32-C5 | 2.4 GHzと5 GHz | Espressif初のデュアルバンドチップ |
| ESP8266(NodeMCU、Wemos D1 mini、ESP-01) | 2.4 GHzのみ | 802.11 b/g/n |
| Raspberry Pi Pico WとPico 2 W | 2.4 GHzのみ | 無線チップはCYW43439 |
| Arduino Uno R4 WiFi | 2.4 GHzのみ | Wi-Fiは基板上のESP32-S3が担当 |
帯域が合っているのにネットワークが見つからない場合は、ルーターのチャンネルとモードの設定を確認しましょう。ESP32の初期の地域設定では、チャンネル1〜11はアクティブスキャン、チャンネル12と13はパッシブに受信するだけです。これらのチャンネルでも公開されているネットワークはたいてい見つかりますが、チャンネル12か13の非公開ネットワーク(SSIDの通知がオフ)はESP32には見つかりません。日本のルーターは12や13チャンネルを自動で選ぶこともあるので注意してください。いちばん確実なのは、チャンネルを自動から1、6、11のいずれかの固定に変えることです。初代ESP32、S2、S3、C3は、802.11ax(Wi-Fi 6)専用に設定されたルーターには参加できません。モードをb/g/nか、b/g/n/axの混在モードに切り替えてください。セキュリティは、WPA2-Personal(AES)か、WPA2/WPA3の混在モードがもっとも互換性があります。Arduinoコアは初期設定でWPA2より弱いもの(WEPや古いWPA-TKIP)を拒否します。どうしてもとても古いネットワークにつなぐ必要があるなら、begin()の前にWiFi.setMinSecurity(WIFI_AUTH_WEP)を追加してください。設定を変える前に、このスキャナーでESP32に実際に何が見えているかを確認しましょう。
1#include <WiFi.h>
2
3// Güvenlik türünü yazıya çevir / turn the security type into text
4const char* guvenlikAdi(wifi_auth_mode_t tur) {
5 switch (tur) {
6 case WIFI_AUTH_OPEN: return "Acik / Open";
7 case WIFI_AUTH_WEP: return "WEP";
8 case WIFI_AUTH_WPA_PSK: return "WPA";
9 case WIFI_AUTH_WPA2_PSK: return "WPA2";
10 case WIFI_AUTH_WPA_WPA2_PSK: return "WPA/WPA2";
11 case WIFI_AUTH_WPA3_PSK: return "WPA3";
12 case WIFI_AUTH_WPA2_WPA3_PSK: return "WPA2/WPA3";
13 default: return "Diger / Other (Enterprise)";
14 }
15}
16
17void setup() {
18 Serial.begin(115200);
19 WiFi.mode(WIFI_STA);
20 WiFi.disconnect(); // temiz tarama için bağlantıyı kes / disconnect for a clean scan
21 delay(100);
22}
23
24void loop() {
25 Serial.println("Taraniyor / Scanning...");
26 int adet = WiFi.scanNetworks(); // engelleyen tarama, 2-4 sn / blocking scan, 2-4 s
27 if (adet <= 0) {
28 Serial.println("Ag bulunamadi / No networks found");
29 }
30 for (int i = 0; i < adet; i++) {
31 Serial.printf("%2d | %-32s | %4d dBm | kanal/ch %2d | %s\n",
32 i + 1, WiFi.SSID(i).c_str(), (int)WiFi.RSSI(i),
33 (int)WiFi.channel(i), guvenlikAdi(WiFi.encryptionType(i)));
34 }
35 WiFi.scanDelete(); // sonuçları bellekten sil / free the results
36 Serial.println();
37 delay(10000); // 10 sn sonra yeniden tara / scan again in 10 s
38}一覧に自分のネットワーク名がまったくない場合、問題はコードより前にあります。ネットワークが5 GHzか、非公開か、ESP32が遠すぎるかのどれかです。名前があれば、RSSIを見てください。-30〜-60 dBmはとても良好、-60〜-70 dBmは良好です。-70〜-80 dBmではつながりますが切れることがあり、-80 dBmより低いと安定した接続は期待できません。同じ名前が何度も出てくる場合は、ルーターやメッシュシステムにアクセスポイントが複数あります。初期設定の高速スキャンでは、ESP32は最初に見つかった一致先に参加します。いちばん強い電波を選ばせたいときは、begin()の前にWiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN)を追加してください。スキャンに表示された名前をそのままコピーしてスケッチに貼り付ければ、打ち間違いを確実になくせます。
ネットワーク名(SSID)とパスワードの間違い
診断スケッチがreason 201か15を報告する場合、問題はたいていコードの2行にあります。SSIDは最大32バイト、WPA2のパスワードは8〜63文字で、どちらも大文字と小文字を区別します。見落としやすい間違いは次のとおりです。
- 大文字と小文字:ルーターでMyWiFiと表示されているネットワークには、mywifiと入力してもつながりません。パスワードも同じです。
- 見えないスペース:ネットワーク名やパスワードの末尾のスペースや、どこかからコピーしたときに紛れ込んだ見えない文字があると、一致しなくなります。
- 特殊文字:ネットワーク名にアクセント付きの文字や英語以外の文字(日本語など)、記号があると、ルーターが別の文字コードで保存していることがあります。ネットワーク名を半角英数字だけに変えるのがいちばん確実です。
- カーリーアポストロフィ:iPhoneのテザリングの名前は端末の名前で、英語名のときはキーボードのまっすぐな「'」ではなく、曲がった「’」が入っていることがよくあります。そのため、コードに書いた名前と一致しません。
- バックスラッシュと引用符:パスワードに「\」や「"」が含まれる場合は、C++の文字列の中で「\\」と「\"」のように書きます。そうしないと、コンパイラがパスワードを変えてしまうか、スケッチがコンパイルできません。
- オープンネットワーク:パスワードのないネットワークにつなぐときは、パスワードを完全に省き、WiFi.begin(name)だけにします。オープンネットワークにパスワードを渡すと、コアはWPA2を探してしまい、ネットワークが見つかりません。
- 非公開ネットワーク:SSIDの通知をオフにしたネットワークにも正確な名前を入力すればつながりますが、スキャンには出てきません。うまくいかない場合は、テスト中だけ通知をオンにしてください。
電源とブラウンアウト:Wi-Fiが動き出すとボードがリセットするのはなぜ?
ESP32モジュールの消費電流は平均で約80 mAですが、Wi-Fiの送信中は電流が一瞬250〜400 mAまで跳ね上がります。最大のピークは、たいてい起動時の無線のキャリブレーションと最初の接続のときに起きます。そのためEspressifは、少なくとも500 mAを供給できる電源を推奨しています。電源がこのピークに追いつかないと、3.3 Vのラインが一瞬落ち、チップのブラウンアウト検出が働いて、シリアルモニタにBrownout detector was triggeredというメッセージが出てボードが再起動します。症状はおなじみのものです。Wi-Fiの行まではコードが問題なく動き、WiFi.begin()でボードがリセットし、それが無限に繰り返されます。このエラーはESP32のブラウンアウトとGuru Meditationエラーの記事で詳しく解説しています。

| 電源 | 使える? | 理由 |
|---|---|---|
| パソコン背面のUSBポート、短くて太いケーブル | たいていOK | USB 2.0ポートは500 mAを供給。短いケーブルは電圧降下を減らす |
| 前面パネルのUSB、USBハブ、長くて細いケーブル | 問題が出ることがある | 電流のピーク時に、ケーブルと接点の抵抗で電圧が下がる |
| 1 A以上の5 Vスマホ充電器 | OK | 電流のピークに対して十分な余裕がある |
| Arduino Unoの3.3Vピン | NG | このピンが供給できるのは約50 mAだけ |
| 3.7 VのLiイオン電池をボードの5Vピンに直結 | NG | AMS1117のドロップアウトのため、出力が3 Vを下回る |
| Liイオン電池+5 V昇圧コンバーター、または低ドロップアウトの3.3 Vレギュレーター | OK | 電池が減っても安定した3.3 Vを供給できる |
| MB102ブレッドボード電源モジュールの3.3 V出力 | おおむねOK | 十分な電流のACアダプターから給電し、コンデンサを追加する |
setTxPowerによる対処:送信出力を下げる
ボードによっては、電源が良くても、ESP32がネットワークを見つけているのにつながらなかったり、接続が切れ続けたりします。特に安価なESP32-C3 Super Miniボードや一部の互換品のESP32ボードでは、最大送信出力のときにアンテナ回路とレギュレーターが追いつきません。ESP32の初期設定の送信出力は約19.5〜20 dBmです。WiFi.setTxPower()でこれを下げると電流のピークが小さくなり、こうしたボードではすぐに接続が直ることがよくあります。呼ぶのはWiFi.begin()の直後です。Wi-Fiが起動する前、つまりWiFi.mode()やbegin()より前に呼んでも効きません。
| 定数 | 出力 | 使う場面 |
|---|---|---|
| WIFI_POWER_19_5dBm | 19.5 dBm | 初期設定に近い。到達距離がいちばん長い |
| WIFI_POWER_15dBm | 15 dBm | 軽い問題のときにまず試す値 |
| WIFI_POWER_11dBm | 11 dBm | ルーターが同じ部屋にあれば、たいてい十分 |
| WIFI_POWER_8_5dBm | 8.5 dBm | ESP32-C3 Super Miniでもっともよく勧められる値 |
| WIFI_POWER_5dBm | 5 dBm | ルーターのすぐ横でのテスト用 |
3 dB下げるごとに送信出力は半分になるので、到達距離も短くなります。まず15 dBmから始め、問題が続くなら11 dBm、さらに8.5 dBmへ下げてください。出力を下げて接続が直るなら、本当の原因はたいてい電源かアンテナの設計です。長く使うなら、より良い電源とコンデンサも追加しましょう。ボードの現在の設定はWiFi.getTxPower()で読めます。値の単位は0.25 dBmなので、78は19.5 dBmという意味です。
自動再接続スケッチ
ルーターの再起動、停電からの復旧、しばらく電波が弱まったときなどに、ESP32は接続を失います。ESP32コアではWiFi.setAutoReconnect(true)が初期設定でオンになっていて、たいていの切断のあとはボードが自分で再接続します。ただし202 AUTH_FAILなど一部の理由では、コアが1回再試行しただけであきらめ、リセットするまでボードはオフラインのままになります。何か月も動かし続けるプロジェクトでは、自分でチェックを追加しておくべきです。下のスケッチはdelay()を使わず、millis()で15秒ごとに接続を確認し、切れていれば再接続し、5分間つながらなければボードを再起動します。
1#include <WiFi.h>
2
3const char* WIFI_ADI = "EvAgi_2G"; // Wi-Fi adı / network name
4const char* WIFI_SIFRE = "sifre1234"; // Wi-Fi şifresi / Wi-Fi password
5
6const unsigned long KONTROL_ARALIGI = 15000; // 15 sn'de bir kontrol / check every 15 s
7const int EN_FAZLA_DENEME = 20; // 20 x 15 sn = 5 dk / 20 x 15 s = 5 min
8unsigned long sonKontrol = 0;
9int basarisizSayac = 0; // üst üste başarısız kontrol / consecutive failed checks
10
11void wifiBaslat() {
12 WiFi.mode(WIFI_STA);
13 WiFi.setAutoReconnect(true); // çekirdeğin kendi yeniden bağlanması / core auto-reconnect
14 WiFi.begin(WIFI_ADI, WIFI_SIFRE);
15 WiFi.setTxPower(WIFI_POWER_15dBm); // gerekirse 8_5dBm'e kadar düşür / lower to 8_5dBm if needed
16 WiFi.setSleep(false); // modem uykusunu kapat / disable modem sleep
17}
18
19void setup() {
20 Serial.begin(115200);
21 wifiBaslat();
22}
23
24void loop() {
25 if (millis() - sonKontrol >= KONTROL_ARALIGI) {
26 sonKontrol = millis();
27 if (WiFi.status() == WL_CONNECTED) {
28 basarisizSayac = 0;
29 Serial.printf("Bagli / Connected, IP: %s, RSSI: %d dBm\n",
30 WiFi.localIP().toString().c_str(), (int)WiFi.RSSI());
31 } else {
32 basarisizSayac++;
33 Serial.printf("Bagli degil / Not connected (%d/%d), yeniden baglaniyor / reconnecting\n",
34 basarisizSayac, EN_FAZLA_DENEME);
35 WiFi.disconnect();
36 WiFi.reconnect();
37 if (basarisizSayac >= EN_FAZLA_DENEME) {
38 Serial.println("Yeniden baslatiliyor / Restarting");
39 delay(100);
40 ESP.restart(); // son çare: kartı yeniden başlat / last resort: restart the board
41 }
42 }
43 }
44
45 // Projenin kodu buraya; uzun delay() kullanma / your project code here; avoid long delay()
46}WiFi.setSleep(false)の行は、接続中にESP32が一定間隔で無線を短時間オフにするモデムスリープを無効にします。これにより、一部のルーターでの切断や、Webサーバーの応答の遅れが減ります。その代わり、ボードの消費電流は数十mAほど増えます。電池のプロジェクトではこの行を消し、電池がどのくらいもつかを電池寿命計算ツールで求めるとよいでしょう。setTxPowerの行は、前の節の問題があるときだけ残してください。プロジェクトのコードをloop()に追加するときは、長いdelay()は避けましょう。接続チェックはloop()が回り続けている間しか動かないからです。Wi-Fiの接続情報をコードに直接書かず、スマホから入力したい場合は、WiFiManagerライブラリを見てみてください。
ESP32の固定IP設定
通常、ルーターはDHCPでESP32にアドレスを割り当て、そのアドレスは毎回変わることがあります。スマホからいつも同じアドレスで開くWebサーバーを作る場合や、ルーターのDHCPの調子が悪い場合(ステータスが0のまま止まる)は、固定IPにすると楽になります。3つのルールを覚えておきましょう。WiFi.config()はWiFi.begin()より前に呼ぶこと。選ぶアドレスはルーターのネットワーク内で、DHCPの割り当て範囲の外にすること(ルーターが192.168.1.2〜100を配っているなら、192.168.1.200などを使います)。そして、ゲートウェイはルーター自身のアドレスであることです。ルーターのアドレスを調べるには、DHCPのままで診断スケッチを一度実行してください。ゲートウェイも表示されます。
1#include <WiFi.h>
2
3const char* WIFI_ADI = "EvAgi_2G"; // Wi-Fi adı / network name
4const char* WIFI_SIFRE = "sifre1234"; // Wi-Fi şifresi / Wi-Fi password
5
6IPAddress sabitIP(192, 168, 1, 200); // DHCP havuzunun dışında bir adres / an address outside the DHCP pool
7IPAddress agGecidi(192, 168, 1, 1); // modemin adresi / router address
8IPAddress altAgMaskesi(255, 255, 255, 0); // alt ağ maskesi / subnet mask
9IPAddress dns1(1, 1, 1, 1); // birinci DNS / primary DNS
10IPAddress dns2(8, 8, 8, 8); // ikinci DNS / secondary DNS
11
12void setup() {
13 Serial.begin(115200);
14 WiFi.mode(WIFI_STA);
15 // config() mutlaka begin()'den önce / config() must come before begin()
16 if (!WiFi.config(sabitIP, agGecidi, altAgMaskesi, dns1, dns2)) {
17 Serial.println("Statik IP ayarlanamadi / Static IP config failed");
18 }
19 WiFi.begin(WIFI_ADI, WIFI_SIFRE);
20
21 unsigned long baslangic = millis();
22 while (WiFi.status() != WL_CONNECTED && millis() - baslangic < 15000) {
23 delay(500);
24 Serial.print('.');
25 }
26 Serial.println();
27 Serial.print("IP: ");
28 Serial.println(WiFi.localIP()); // 192.168.1.200 olmalı / should be 192.168.1.200
29}
30
31void loop() {}学校や会社のネットワーク:WPA2-Enterprise(eduroam)への接続
大学のeduroam、会社のネットワーク、一部の学校のネットワークはWPA2-Enterprise(802.1X)を使っていて、共通のパスワードの代わりに利用者ごとのユーザー名とパスワードを割り当てます。こうしたネットワークでは普通のWiFi.begin(name, password)は必ず失敗し、スキャナーではセキュリティの種類がDiger / Other (Enterprise)と表示されます(Digerはトルコ語で「その他」)。ESP32 Arduinoコアのバージョン2.xと3.xには、もっとも一般的な方式であるPEAP用のbegin()があらかじめ用意されています。
1#include <WiFi.h>
2
3const char* AG_ADI = "eduroam"; // kurum ağının adı / enterprise network name
4const char* EAP_KIMLIK = "[email protected]"; // kimlik, çoğu zaman kullanıcı adıyla aynı / identity, usually same as username
5const char* EAP_KULLANICI = "[email protected]"; // kullanıcı adı / username
6const char* EAP_SIFRE = "kurum_sifren"; // kurum şifresi / enterprise password
7
8void setup() {
9 Serial.begin(115200);
10 WiFi.disconnect(true); // önceki bağlantıyı kapat / close any previous connection
11 WiFi.mode(WIFI_STA);
12 // PEAP (MSCHAPv2) ile bağlan, CA doğrulaması yok / connect with PEAP (MSCHAPv2), no CA check
13 WiFi.begin(AG_ADI, WPA2_AUTH_PEAP, EAP_KIMLIK, EAP_KULLANICI, EAP_SIFRE);
14
15 unsigned long baslangic = millis();
16 while (WiFi.status() != WL_CONNECTED && millis() - baslangic < 30000) {
17 delay(500);
18 Serial.print('.');
19 }
20 Serial.println();
21 if (WiFi.status() == WL_CONNECTED) {
22 Serial.print("Bagli / Connected, IP: ");
23 Serial.println(WiFi.localIP());
24 } else {
25 Serial.printf("Baglanamadi / Failed, durum / status: %d\n", (int)WiFi.status());
26 }
27}
28
29void loop() {}多くの機関では、identityはユーザー名と同じです。[email protected]のような匿名の外部IDを求めるところもあります。このスケッチはサーバー証明書(CA)を検証しません。接続はできますが、偽のアクセスポイントにパスワードを盗まれるおそれがあります。そのため、自分のパスワードをESP32に入れる前に情報システム部門に相談し、できれば機器用のアカウントやネットワークを申請してください。機関が証明書を必須にしている場合は、CAファイルをbegin()の6番目の引数として渡せます。ホテル、寮、カフェのように、ブラウザでログインページが開くネットワーク(キャプティブポータル)は別の問題です。ESP32はそのページに入力できないからです。また、機器のMACアドレスの事前登録を求める学校もあります。診断スケッチが表示したMACアドレスを情報システム部門に伝えてください。こうしたネットワークでは、スマホのテザリングか小型のモバイルルーターを使うのがいちばん現実的です。
スマホのテザリングテスト:原因はルーターか、ESP32か?
原因をいちばん早く絞り込むには、設定をすべて自分で管理できるネットワークにESP32をつなぐことです。スマホのテザリングはこれにぴったりです。
- テザリングには半角英数字だけの短い名前(たとえばesptest)を付け、8文字以上のシンプルなパスワード(たとえばtest12345)を設定します。
- Androidでは、テザリング(アクセスポイント)の設定にある帯域の項目(機種によっては「APバンド」や「速度と互換性」)を2.4 GHzにし、セキュリティの種類にWPA2-Personalを選びます。
- iPhoneでは、「設定」>「インターネット共有」で「ほかの人の接続を許可」をオンにします。新しい機種では「互換性を優先」もオンにしてください。これでテザリングが2.4 GHzに切り替わります。テザリングの名前は端末の名前なので、「設定」>「一般」>「情報」>「名前」でシンプルにし、ESP32がつながるまでインターネット共有の画面を開いたままにしておきます。
- 診断スケッチのネットワーク名とパスワードをテザリングの情報に書き換え、スマホをESP32から1〜2メートルの所に置いて、スケッチを書き込みます。
- シリアルモニタでステータスコードとreasonコードを読み、続いて同じスケッチを家のルーターの情報で試します。
結果は次のように判断します。ESP32がテザリングにはつながるのに家のルーターにはつながらないなら、コード、ボード、電源は問題ありません。原因はルーターの帯域、チャンネル、セキュリティ、MACフィルター、接続台数の上限のいずれかの設定にあります。テザリングにもつながらないなら、問題はESP32側です。電源とケーブルを替え、setTxPowerで出力を下げ、アンテナの前に金属がないことを確かめて、コードをもう一度確認してください。テザリングテストは、学校でつながらないプロジェクトが実際にはちゃんと動くことを示すいちばん手早い方法でもあります。
Raspberry Pi Pico WとMicroPythonでのWi-Fiテスト
Raspberry Pi Pico WとPico 2 Wも2.4 GHzにしか対応していません。この記事の帯域、パスワード、電源、テザリングについてのアドバイスは、これらのボードにもすべて当てはまります。MicroPythonでは、networkモジュールの定数を使ってステータスコードを読みます。下のスクリプトは、ESP32でもPico Wでも、Thonnyでそのまま動きます。Pico Wでは、ネットワークが見つからないときやパスワードが違うときはすぐに待機をやめます。ESP32のMicroPythonは一部のエラーのあとも再試行を続けるので、スクリプトは20秒後に最後のステータスを表示します。
1import network, time
2
3AG_ADI = "EvAgi_2G" # Wi-Fi adı / network name
4SIFRE = "sifre1234" # Wi-Fi şifresi / Wi-Fi password
5
6wlan = network.WLAN(network.STA_IF)
7wlan.active(True)
8if wlan.isconnected():
9 wlan.disconnect() # eski bağlantıyı kapat / close the previous connection
10 time.sleep(1)
11wlan.connect(AG_ADI, SIFRE)
12
13durum = wlan.status()
14for saniye in range(20): # en fazla 20 sn bekle / wait up to 20 s
15 durum = wlan.status()
16 print(saniye, "durum / status:", durum)
17 if durum in (network.STAT_GOT_IP, network.STAT_NO_AP_FOUND, network.STAT_WRONG_PASSWORD):
18 break # sonuç belli, beklemeyi bırak / result is known, stop waiting
19 time.sleep(1)
20
21if durum == network.STAT_GOT_IP:
22 print("Bagli / Connected, IP:", wlan.ifconfig()[0])
23 print("RSSI:", wlan.status("rssi"), "dBm")
24elif durum == network.STAT_NO_AP_FOUND:
25 print("Ag bulunamadi / Network not found (SSID, 2.4 GHz)")
26elif durum == network.STAT_WRONG_PASSWORD:
27 print("Sifre yanlis / Wrong password")
28else:
29 print("Baglanamadi / Failed, durum / status:", durum)Pico Wでは数値のステータスが異なります。3はIPを取得して接続済み、2は接続済みでIP待ち、-1は一般的な失敗、-2はネットワークが見つからない、-3はパスワードの間違いです。ESP32では代わりに、接続中は1001、IPを取得すると1010が表示され、問題があるときは201や202などのESP-IDFのreasonコードが出ます。Pico Wでチャンネル12と13の問題を避けるには、接続前に国コードを設定してください(rp2.country('GB')、新しいファームウェアではnetwork.country('GB')。'GB'は自分の国のコードに置き換え、日本なら'JP'です)。ときどき接続が切れる場合は、wlan.config(pm=0xa11140)で省電力をオフにできます。Pico WでWebサーバーを作るならPico WのWi-Fi Webサーバーの記事を、MicroPythonが初めてならMicroPythonの基本のガイドを見てください。
よくある問題と対処法
| 症状 | 考えられる原因 | 対処法 |
|---|---|---|
| シリアルモニタに文字化けが出る | ボーレートの不一致 | シリアルモニタを115200 baudにする |
| ステータス1、reason 201 | ネットワーク名の間違い、5 GHzのネットワーク、または遠すぎる | スキャナーを実行し、2.4 GHzのネットワーク名をそのままコピーする |
| ステータス6、reason 15が繰り返し出る | パスワードの間違い | 大文字・小文字と特殊文字に注意してパスワードを打ち直す |
| ステータス4か6、reason 202、210、211 | 対応していないセキュリティモード | ルーターをWPA2-Personal(AES)か、WPA2/WPA3の混在モードにする |
| ステータス0のまま止まる | ルーターに参加したが、DHCPからIPをもらえない | ルーターを再起動する、DHCPの割り当て範囲を広げる、または固定IPを使う |
| WiFi.begin()でリセットし、Brownout detector was triggeredと表示される | 電源が電流のピークに耐えられない | 短いケーブル、1 A以上のACアダプター、3V3とGNDの間に470 µF |
| つながるが、数分後に切れる(reason 200) | 弱い電波、モデムスリープ、または電源の問題 | setSleep(false)、setTxPower、再接続スケッチ、ルーターに近づける |
| 机の上では動くが、ケースに入れるとつながらない | 金属のケース、またはアンテナの前に電池がある | アンテナをケースの端で外向きに配置し、プラスチックのケースを使う |
| つながっているのに、スマホでWebページが開かない | スマホが別のネットワークにいる、またはゲストネットワークが端末間通信を遮断している | 両方を同じネットワークにつなぎ、シリアルモニタでIPを確認する |
| Wi-FiをオンにするとanalogRead()の値がおかしくなる | センサーがADC2のピンにつながっていて、Wi-FiがADC2を使っている | センサーをADC1のピン(GPIO 32〜39)に移す |
| ESP32-CAMや外部アンテナ付きボードで、到達距離が数センチしかない | アンテナの選択が外部になっているのに、アンテナが付いていない | IPEXコネクタにアンテナを挿すか、基板上の0 Ωのアンテナ選択抵抗を確認する |
つながったら、次は何を作る?
- Webサーバーとライブダッシュボード:センサーの値をスマホにリアルタイムで表示するWebSocketを使ったESP32 Webサーバーを作りましょう。
- インターネット時計:NTPサーバーから時刻を取得し、ディスプレイに表示します。
- 天気表示:天気APIで自分の町の気温と湿度を取得します。
- スマホへの通知:ドアが開いたらTelegramボットでメッセージを送ります。
- スマートホーム:ESP32をMQTTでHome Assistantにつなぎます。
- ルーターなしの通信:どのネットワークにも参加せず、ESP-NOWで2台のESP32どうしを通信させます。
- ワイヤレスで書き込み:ボードをケースに封じ込めたあとは、OTAアップデートでWi-Fi経由で新しいコードを送ります。
Wi-FiにつながらないESP32は最初は戸惑いますが、ステータスコードとreasonコードは、問題がどのステップにあるのかを必ず教えてくれます。1と201はネットワークが見つからない、15と202はパスワードかセキュリティモード、0はDHCP、200や突然のリセットは電波か電源を示しています。ESP32が初めてなら、ESP32とは?で基本を学べます。BlueGraysアプリでは、ESP32のWi-Fi接続、Webサーバー、mDNS、NTP時計、気象ステーション、OTAアップデート、Telegramアラームなどのプロジェクトを、ステップごとの回路図とすぐ使えるコード付きで見ることもできます。
よくある質問
ESP32がWi-Fiにつながらないのはなぜですか?
よくある原因は、5 GHzのネットワークにつなごうとしている、ネットワーク名やパスワードの打ち間違い、Wi-Fiが動き出すと電圧が落ちる弱い電源、ルーターのセキュリティ設定です。WiFi.status()の値と、切断時に報告されるreasonコードを表示すれば、どれが原因かすぐにわかります。ネットワークが見つからなければ1と201、パスワードが間違っていればたいてい6と15が表示されます。
ESP32は5 GHzのWi-Fiにつながりますか?
初代ESP32、ESP32-S2、S3、C3、C6は2.4 GHzにしか対応しておらず、5 GHzのネットワークはスキャンにまったく出てきません。デュアルバンドのモデルはESP32-C5です。ルーターが2つの帯域を別々の名前で出している場合は、ESP32に2.4 GHzのネットワーク名を指定してください。
ESP32のWiFi.status()が6になるのはどういう意味ですか?
6はWL_DISCONNECTEDで、ボードは未接続で、接続の試行がまだ続いているか、失敗したことを意味します。ESP32では、パスワードが間違っているときに4(WL_CONNECT_FAILED)ではなくこの値になることがよくあります。本当の原因を知るには、ARDUINO_EVENT_WIFI_STA_DISCONNECTEDイベントのreasonコードを確認してください。
ESP32のWi-Fiが何度も切れるのはなぜですか?
切断が繰り返される主な原因は、弱い電波、Wi-Fiの電流ピークに耐えられない電源、アンテナの前の金属や電池、ルーターのバンドステアリングの設定です。ボードをルーターに近づけ、3V3とGNDの間に100〜470 µFのコンデンサを追加し、WiFi.setSleep(false)でモデムスリープをオフにしてください。また、接続が切れても自分で復帰するようにコードを書いておきましょう。
ESP32はスマホのテザリングにつながりますか?
はい、テザリングが2.4 GHzで電波を出していればつながります。Androidではテザリングの帯域を2.4 GHzに、iPhoneでは「互換性を優先」をオンにし、テザリングの名前はシンプルにしておきます。テザリングテストは、問題が家のルーターとESP32のどちらにあるのかを見分けるいちばん早い方法です。
ESP32はeduroamや学校のネットワークにつながりますか?
ESP32は、ESP32 ArduinoコアのWiFi.begin(name, WPA2_AUTH_PEAP, identity, username, password)の形を使えば、eduroamなどのWPA2-Enterpriseネットワークに参加できます。ただし、ブラウザでログインページが開くネットワーク(キャプティブポータル)では、ESP32はそのページに入力できません。機関のネットワークを使う前に情報システム部門に相談し、できれば機器用の別アカウントを申請するのがいちばんです。
この記事を読んだ後に作れるプロジェクト
全プロジェクトを見る
ESP32 Wi-Fi STA接続(LCDディスプレイ付き)
このプロジェクトでは、ESP32をステーション(STA)モードで家庭のWi-Fiネットワークに接続し、安全で障害に強い接続を実現します。SSIDとパスワードを別のsecrets.hファイルに保存してGitへの流出を防ぎ、自動再接続の仕組みを実装します。RSSIによる信号強度とIPアドレスはI2C LCDディスプレイに表示します。内蔵LEDは状態表示に使います。実運用を想定した接続管理の仕組みです。

ESP32非同期Webサーバー(LCDにIPアドレスを表示)
このプロジェクトでは、ESP32を同じWi-Fiネットワーク上のスマートフォンやパソコンから操作できる非同期Webサーバーにします。ESPAsyncWebServerライブラリを使うと、従来のWebServer.hのように1つのリクエストで処理がブロックされず、複数のリクエストを同時に処理できます。ブラウザーでESP32のIPアドレスを開き、ON/OFFボタン(/onと/offのルート)を押すと、GPIO 5に接続したLEDを点灯・消灯できます。

ESP32 mDNS:bluegrays-esp.localでデバイスにアクセス(LCD付き)
このプロジェクトでは、ESP32にmDNS(Multicast DNS)サービスを追加し、IPアドレスの代わりにbluegrays-esp.localのような覚えやすいホスト名でアクセスできるようにします。DHCPで割り当てられるIPアドレスが変わっても、同じ名前でデバイスにアクセスできます。小さなWebサーバーも動かし、ブラウザーでhttp://bluegrays-esp.local/を開いてLEDを操作します。I2C LCDディスプレイにはホスト名、IPアドレス、LEDの状態をトルコ語と英語で表示します。iOS、macOS、LinuxはmDNSに標準対応しています。

ESP32とNTPでインターネットから正確な時刻を取得
ESP32をWi-Fiに接続し、NTP(Network Time Protocol)サーバーからミリ秒精度の現在時刻を取得します。configTzTime()関数でタイムゾーンを自動適用し、夏時間を採用する国ではPOSIX TZ文字列で夏時間と標準時間の切り替えも自動管理します。時刻はシリアルモニタと、必要に応じて接続するI2C LCDに表示します。configTzTime()がSNTPサービスを開始し、ネットワークにアクセスできる間は定期的に再同期します。

ESP32 HTTPClient+天気API(LCD付き)
このプロジェクトでは、ESP32でHTTPClient.hを使い、インターネット経由で天気API(wttr.in)をREST APIとして呼び出します。返されたJSONレスポンスをArduinoJson v7で解析し、都市名、気温、湿度、天気、風の情報をI2C LCDディスプレイにトルコ語と英語で表示します。wttr.inは無料で、APIキーも不要です。都市名を入力するだけで、その都市の現在の天気を取得できます。30分ごとに自動更新し、表示画面ごとに異なるLEDの光り方で状態を知らせます。

ESP32 OTA更新:無線でファームウェアを書き込み(LCD付き)
このプロジェクトでは、USBケーブルを使わず、Wi-Fi経由でESP32に新しいファームウェアを書き込む方法を学びます。ArduinoOTAライブラリを使うと、ESP32がネットワーク上で検出され、Arduino IDEの「ネットワークポート」タブにbluegrays-otaとして自動表示されます。初回はUSBで書き込み、その後の更新はすべて無線で行えます。設置済みのデバイスにとって非常に重要な機能です。I2C LCDディスプレイには、Wi-Fiの状態、IPアドレス、ホスト名、OTA書き込みの進捗率、エラーメッセージをトルコ語と英語で表示します。

ESP32 WebSocketカウンター(2つのボタン+LCD+Webリアルタイム表示)
このプロジェクトでは、ESP32に接続した2つのボタンでカウンターの値を変更します(GPIO 5で増加、GPIO 4で減少)。値は16x2 I2C LCDに即座に反映され、ESPAsyncWebServerのWebSocket機能により、接続中のすべてのブラウザーでもページを再読み込みせずに更新されます。HTTPポーリングの代わりに1つの接続を維持し、ESP32がws.textAll()で新しい値を全員に送信します。

ESP32動体検知アラーム(PIR + ブザー + LED + Telegram)
PIRセンサーが動きを検知するとブザーとLEDを作動させ、同時にTelegramで持ち主に即座に通知するスマート防犯アラームです。アラームが繰り返される頻度を抑えるため、30秒のクールダウン(待ち時間)を設けています。この仕組みはPIRの検知が正しいかを確認したり、誤検知をなくしたりするものではありません。家の防犯、ガレージの監視、倉庫や作業部屋などに使えます。ESP32-CAMなら写真付きの通知を追加でき、通常のESP32 DevKit V1ではテキストで通知します。
ほかの記事
BlueGraysアプリ
この回路をスマホでステップごとに作る
回路図、コード、部品リストを1つの画面で確認できます。Arduino、ESP32、Picoのプロジェクトを200件以上用意しています。

