Скетч с чужой страницы я тогда скопировал целиком, загрузил — и полчаса смотрел, как монитор порта печатает точку за точкой. Плата живая, роутер в метре от меня, пароль знаю наизусть, а связи нет. Виноват оказался не код, а диапазон: тот роутер отдавал единственную сеть только в 5 ГГц, которую радиомодуль ESP32 физически не слышит. Час был потрачен зря именно потому, что я не понимал, за что отвечает каждая из десяти строк сетевого скетча, — и не мог задать себе правильный вопрос.
Здесь мы пройдём весь путь по шагам — от того, что мы уже делали в первом уроке раздела, только медленнее и честнее. Сначала договоримся, откуда берутся SSID и пароль и какие у них ограничения — два из них ломают подключение чаще, чем ошибки в коде. Потом разберём рабочий скетч: что именно делает WiFi.begin(), зачем нужен статусный цикл и как плата получает адрес по DHCP. Заглянем в таблицу кодов WiFi.status(), соберём чек-лист отладки и повторим подключение в симуляторе прямо на этой странице. Всё дальнейшее в разделе — HTTP-запросы, веб-сервер, MQTT — стоит ровно на этих строках, поэтому они заслуживают отдельного урока.
SSID и пароль: ограничения, о которых молчат туториалы
SSID — имя сети, и у него жёсткий потолок: 32 байта. Пароль при WPA2 — от 8 до 63 символов, и регистр значим: DachaNet и dachanet для роутера — две разные сети. На этой мелочи я потерял один вечер полностью: набирал пароль с клавиатуры по памяти, а лежал в нём один символ в верхнем регистре. С тех пор я сверяю пароль через заметку в телефоне, а не глазами, — зрение додумывает регистр само.
Второй рубеж проверки — диапазон. ESP32 работает только в 2.4 ГГц, стандарты b/g/n; диапазон 5 ГГц плата не видит вовсе, ни при каком коде. Если роутер двухдиапазонный, у него либо одно имя на обе частоты, либо пара вида Home и Home_5G — плату ведите на ту сеть, что на 2.4. Скрытый SSID — отдельный случай: WiFi.begin(ssid, password) с ним справляется, но сопряжение занимает заметно дольше, потому что плате нельзя подсмотреть имя из чужих маячков — она называет его сама. И последнее: кириллица в имени сети легальна для роутера, но для платы это лотерея — я такие SSID переименовываю латиницей и не вспоминаю о проблеме больше никогда.
Где живут SSID и пароль — тоже вопрос не мелкий. В скетче из этого урока они в константах: компилятор кладёт строки в память программ, RAM не трогает, и для личной платы этого достаточно. Но публиковать такой скетч с настоящим паролем — плохая идея: я однажды выложил лог с реальным именем домашней сети, и стирать следы пришлось дольше, чем писать пост. Для устройств, где сеть выбирает пользователь, данные кладут в энергонезависимую память и заводят страницу настройки — эта связка ждёт нас в уроке про точку доступа.
Две детали про сам вызов, которые экономят вечер. WiFi.begin() работает и с одним аргументом — для открытых сетей без пароля: WiFi.begin(ssid), и роутер общежития без шифрования подключится без воображаемого пароля. А ещё begin можно вызывать повторно — с новым SSID плата бросит текущую сеть и пойдёт искать другую; на этом держится смена сетей на ходу, которую мы будем использовать в портале настройки из урока про точку доступа. Только помните: между двумя begin() ядро успевает не всё почистить, поэтому смена сети на лету — приём для отладки, а не для боевого устройства.
Десять строк подключения: скетч и разбор
#include <WiFi.h> const char* ssid = "DachaNet"; // имя сети, регистр значимconst char* password = "superParol123"; // 8-63 символа для WPA2 void setup() { Serial.begin(115200); pinMode(2, OUTPUT); // GPIO 2 — встроенный светодиод WiFi.begin(ssid, password); // старт: процесс, а не мгновенный результат while (WiFi.status() != WL_CONNECTED) { // ждём готовности delay(500); Serial.print("."); // точка — «ещё работаю» } digitalWrite(2, HIGH); // светится — связь поднята Serial.println(); Serial.print("ip="); Serial.println(WiFi.localIP()); // адрес, выданный роутером по DHCP} void loop() { // сеть уже настроена — здесь будет ваш код}WiFi.begin(ssid, password) не подключает плату мгновенно — она только запускает процесс: сканирование эфира, поиск сети с таким именем, сопряжение с роутером, обмен ключами WPA2. (Цикл while, на котором держится ожидание, мы собирали в уроке про циклы — тут он работает один в один.) На практике это занимает от двух до шести секунд, на старом роутере при слабом сигнале — дольше. Функция возвращается сразу, поэтому за ней всегда стоит цикл ожидания: пока WiFi.status() не вернёт WL_CONNECTED, печатаем точку каждые полсекунды. Первое сопряжение после подачи питания всегда дольше повторного: роутер помнит клиента по MAC-адресу, и устойчивая связь на уже знакомой сети поднимается за одну-две секунды. Точка — не украшение, а витальный знак: без неё невозможно отличить «процесс идёт» от «скетч завис», а с ней разница видна через пять секунд после загрузки.
Как только статус сменился на WL_CONNECTED, у платы есть адрес — его выдал роутер по DHCP, протоколу автоматической раздачи адресов. WiFi.localIP() возвращает его объектом IPAddress, который Serial печатает строкой вроде 192.168.1.100; в симуляторе ниже адрес всегда такой, на реальной плате — тот, что раздаёт ваш роутер. Запишите адрес платы: по нему вы будете открывать её веб-страницу из урока про сервер. Но адрес непостоянен — после перезапуска роутера DHCP может выдать другой, и старый адрес в закладке браузера молча перестанет отвечать. Для устройства, которое должно быть доступно всегда, я делаю на роутере DHCP-резервацию: привязку адреса к MAC-адресу платы, который печатает WiFi.macAddress(). Резервация есть в настройках любого домашнего роутера — от бюджетных TP-Link до Keenetic, и занимает она минуту.
Про светодиод на GPIO 2 тоже пара слов. Он мигает, пока сопряжение идёт, и горит ровно, когда связь поднята, — готовый индикатор для коробки с электроникой, которая висит на стене без экрана. Один взгляд на плату отвечает на вопрос «сеть есть?» без монитора порта и без логов; сам монитор при этом остаётся главным инструментом отладки — если вы ещё не настроили его по-настоящему, закройте этот пробел по уроку про первый запуск монитора порта.
| Значение | Число | Что значит | Что делать |
|---|---|---|---|
| WL_CONNECTED | 3 | Сеть поднята, адрес получен | Работать |
| WL_DISCONNECTED | 6 | Не подключались или отключились | Ждать или звать begin() снова |
| WL_NO_SSID_AVAIL | 1 | Сети с таким SSID нет в эфире | Проверить имя и диапазон 2.4 ГГц |
| WL_CONNECT_FAILED | 4 | Роутер отказал в сопряжении | Проверить пароль |
| WL_IDLE_STATUS | 0 | Пауза между попытками | Просто подождать |
| WL_CONNECTION_LOST | 5 | Связь оборвалась после подключения | Реконнект — тема следующего урока |
Подключение в симуляторе
- 1. Что происходит сразу после вызова WiFi.begin(ssid, password)?
- 2. Роутер раздаёт единственную сеть в диапазоне 5 ГГц. Что увидит ESP32?
- 3. Какой минимальной длины должен быть пароль сети для сопряжения по WPA2?
- 4. WiFi.status() возвращает WL_NO_SSID_AVAIL. Самая вероятная причина?
- 5. Адрес 192.168.1.100, выданный DHCP, меняется после каждого перезапуска роутера. Как закрепить его навсегда?
- WiFi.begin(ssid, pass) только запускает подключение; готовность ловит цикл while (WiFi.status() != WL_CONNECTED)
- ESP32 видит только 2.4 ГГц: сеть в 5 ГГц для платы не существует, каким бы правильным ни был код
- SSID — до 32 байт, пароль WPA2 — 8-63 символа, регистр значим: сверяйте через заметку, а не глазами
- IP выдаёт роутер по DHCP; для постоянного адреса — DHCP-резервация по WiFi.macAddress()
- Монитор порта — 115200 бод; точки в логе означают, что сопряжение ещё идёт
Если десяти строк достаточно, чтобы плата встала в сеть, — база у вас есть, и дальше раздел открывается обеими руками. Но первый выход в сеть — это только полдела: роутер перезагружается, провайдер обновляет прошивку в три часа ночи, и плата остаётся в тишине до ручного перезапуска. Как сделать так, чтобы она поднимала связь сама, без вашего участия, — тема следующего урока про автоматический реконнект: там тот же WiFi.status(), но превращённый в автомат состояний на millis().
Кому интереснее дорога в сторону — плата как сервер, к которому подключается телефон без всякого роутера, — держите в закладках урок про точку доступа SoftAP. А если сеть у вас уже живёт стабильно, следующим шагом логично ходить за данными: в уроке про HTTP-запросы та же плата спрашивает у сервера погоду и вытаскивает числа из ответа. Фундамент для всех трёх маршрутов общий — это строки, которые мы только что разобрали по косточкам.
Частые вопросы по теме урока
- Что происходит сразу после вызова WiFi.begin(ssid, password)?
- begin() только запускает процесс и сразу возвращается. Результат проверяют циклом: while (WiFi.status() != WL_CONNECTED) { delay(500); } Запускается процесс сопряжения, готовность проверяют через WiFi.status()
- Роутер раздаёт единственную сеть в диапазоне 5 ГГц. Что увидит ESP32?
- Радиомодуль ESP32 — 802.11 b/g/n, все три стандарта живут в 2.4 ГГц. Диапазон 5 ГГц плата не слышит физически, код тут бессилен. Не увидит сеть вообще: радиомодуль платы работает только в 2.4 ГГц
- Какой минимальной длины должен быть пароль сети для сопряжения по WPA2?
- WPA2 требует от 8 до 63 символов. Короткий пароль роутер просто не примет — плате вернётся отказ в сопряжении. 8 символов
- WiFi.status() возвращает WL_NO_SSID_AVAIL. Самая вероятная причина?
- WL_NO_SSID_AVAIL значит, что сканирование не нашло сеть с таким именем. Опечатка в SSID или сеть в 5 ГГц дают ровно этот статус. Сети с таким именем нет в эфире — проверьте SSID и диапазон 2.4 ГГц
- Адрес 192.168.1.100, выданный DHCP, меняется после каждого перезапуска роутера. Как закрепить его навсегда?
- Резервация на роутере привязывает адрес к MAC (его печатает WiFi.macAddress()). Адрес в константе скетча ничего не решает — раздаёт адрес именно роутер и продолжит выдавать всё, что решит. Сделать DHCP-резервацию на роутере по MAC-адресу платы