Двое суток камера на даче не выходила в сеть, и всё это время плата честно крутила один и тот же цикл: while (WiFi.status() != WL_CONNECTED) delay(300);. Ни сообщения в мониторе, ни телеметрии — молчание, как у выключенной. Вскрытие заняло минуту и оказалось скучным: роутер переименовали, SSID сменился, а код, который status() возвращал всё это время, некому было прочитать — его никто не печатал. С тех пор ни один сетевой скетч у меня не стартует без расшифровки статуса: WiFi.status() возвращает число, и каждое число — конкретный диагноз, а не абстрактное «не работает».
Анатомию WiFi.begin() и выбор сети я разбирал в уроке про подключение к роутеру — туда же уходит нюанс со скрытыми сетями, повторять его не буду. Здесь — словарь его ответов: что означает каждый код, в какой момент он выпадает и что делать, когда он застревает. Отдельная честная оговорка: движения статуса идут внутри драйвера, и видны снаружи только через опрос — никаких событий в Serial сами по себе не прилетают, печатать приходится самому. Если первый сетевой скетч вы ещё не писали, стартуйте с урока про первый WiFi и возвращайтесь со статусом в руках.
Коды 0 и 1: между begin и итогом
WL_IDLE_STATUS, число 0 — код транзитный. Он говорит «begin() запущен, финального ответа ещё нет»: вызов ушёл в драйвер, ассоциация идёт, и status() между этими событиями отвечает нулём. У него есть и вторая, менее очевидная роль — на части портов ядра тем же нулём помечают исчерпанные попытки: драйвер перестал повторять подключение, а новый begin() ещё не звали. Поэтому ноль читают только вместе с контекстом: сразу после begin() он значит «ждите», спустя минуты тишины — «попытки кончились».
WL_NO_SSID_AVAIL, число 1 — эфир прослушан, вашей сети в нём нет. Опечатка в SSID, роутер выключен из розетки, сеть скрыта и не светится в широковещательных маяках. Код ценен тем, что он финальный: драйвер уже всё проверил, и ждать после него бессмысленно. Отсюда привычка: увидели единицу — печатайте SSID байтово (я однажды ловил невидимый пробел в конце строки, скопированной из чата), потом роутер, и только потом перезапускайте попытки.
| Число | Имя | Что означает | Что делать |
|---|---|---|---|
| 0 | WL_IDLE_STATUS | begin() ушёл в драйвер, итога ещё нет; на части портов — попытки исчерпаны | читать с контекстом: сразу после begin — ждать, после минут тишины — пинать begin() |
| 1 | WL_NO_SSID_AVAIL | SSID не виден в эфире: опечатка, роутер выключен, скрытая сеть | проверять имя байтово и роутер; ждать бесполезно |
| 3 | WL_CONNECTED | подключено, IP получен | работать, но помните: это не сертификат здоровья |
| 4 | WL_CONNECT_FAILED | ассоциация сорвалась — чаще всего неверный пароль | проверить пароль и перезапустить begin() |
| 5 | WL_CONNECTION_LOST | связь была, потом оборвалась | считать обрывы и запускать reconnect |
| 6 | WL_DISCONNECTED | не подключено, попыток не идёт | стартовое состояние и после disconnect() |
const char* SSID_NAME = "HomeNet";const char* PASS = "12345678"; String statusName(int s) { if (s == 0) return "0 WL_IDLE_STATUS — итога begin() ещё нет"; if (s == 1) return "1 WL_NO_SSID_AVAIL — сети нет в эфире"; if (s == 3) return "3 WL_CONNECTED — подключено"; if (s == 4) return "4 WL_CONNECT_FAILED — пароль или ассоциация"; if (s == 5) return "5 WL_CONNECTION_LOST — связь оборвалась"; if (s == 6) return "6 WL_DISCONNECTED — не подключено"; return String(s) + " — незнакомый код";} void setup() { Serial.begin(115200); WiFi.begin(SSID_NAME, PASS); int tries = 0; while (WiFi.status() != WL_CONNECTED && tries < 20) { delay(500); tries++; Serial.print("попытка "); Serial.print(tries); Serial.print(": "); Serial.println(statusName(WiFi.status())); } if (WiFi.status() == WL_CONNECTED) { Serial.print("на связи, IP: "); Serial.println(WiFi.localIP()); } else { Serial.println("20 попыток за 10 секунд — код выше скажет, почему нет"); }} void loop() {}Тройка — единственный рабочий код: ассоциация пройдена, DHCP выдал адрес, WiFi.localIP() отвечает. Шестёрка — её противоположность: не подключено и попыток не идёт; это и стартовое состояние до первого begin(), и результат WiFi.disconnect(). Между ними живут четвёрка с пятёркой: WL_CONNECT_FAILED — ассоциация сорвалась, в девяти случаях из десяти из-за пароля, WL_CONNECTION_LOST — связь была и потерялась, режим для счётчика обрывов.
Ловушка, которую я проверил на собственной шкуре: тройка — не сертификат здоровья. Роутер выключают из розетки, а status() ещё десятки секунд отмахивается WL_CONNECTED — стек не знает, что сосед по TCP умер, пока не истекут таймауты подтверждений. Поэтому в уроке про ping-монитор здоровье проверяют трафиком: доехали пакеты — связь жива. Статус — за вводную, пинг — за приговор. Кстати, четвёрка и единица различаются как раз для такого быстрого диагноза: одна говорит «роутер тут, но пароль не подошёл», другая — «роутера нет вовсе», и это два разных звонка соседу. А когда расшифровка перестанет помещаться в голову, коды складываются в retry-логику с паузами — её расклад в уроке про коды ответов и backoff.
Вечный while и чем его заменить
Конструкция while (WiFi.status() != WL_CONNECTED); без лимита — самая дорогая строка, которую можно написать в setup(). При коде 1 она зависает навсегда: сети нет, ждать нечего, а плата об этом не догадывается. Я ставлю лимит двойной — в попытках и во времени: двадцать подходов по полсекунды, дальше диагноз в Serial и выход. Датчик на улице лучше поработает без сети, отчитавшись о проблеме, чем замолчит вовсе: решение о дальнейшей судьбе принимает основная логика, а не сетевой блок.
Второе правило — сравнивать статусы только именованными константами: WiFi.status() == WL_CONNECTED, а не s == 3. Числа в шпаргалке — из ESP32-ядра, а портов и прошивок много, и нумерация между ними расходится; этим же грешат скетчи, переехавшие с одной платы на другую без оглядки — разбор таких переездов в уроке про перенос скетча с ESP8266. Имя собирается компилятором в правильное число для каждой платы и остаётся читаемым в коде через полгода. Читаемость вообще решает: если в мониторе болтается голое число без имени и метки времени, диагноз вы всё равно будете ставить пальцем в небо — азы чтения порта в уроке про Serial-монитор.
Куда словарь встраивается дальше. Мой минимальный набор — функция вроде statusName из скетча плюс одна привычка: печатать статус перед каждым сетевым действием — GET-ом, публикацией, запросом времени. Через неделю такого журнала характер проблем виден как по учебнику: единицы, всплывающие в три часа ночи, — это роутер уходит в перезагрузку по расписанию провайдера; четвёрки после смены пароля — сосед всё-таки догадался и сменил свой; россыпь пятёрок по вечерам — микроволновка на кухне, глушащая 2,4 ГГц ровно в момент греения пельменей. Голый код без метки времени — просто число, которым можно только пугать; код с меткой и повторами — дневник, по которому ставится диагноз за пару минут.
Второе применение — приёмочный контроль чужого кода. Копируя пример с форума, первым делом ищите в нём обработку статуса: если после begin() стоит бесконечный while и ни одной ветки на коды, пример писали для идеального мира и идеального роутера. Работоспособный скетч различает хотя бы три исхода: тройка — работаем; единица — жалуемся громко, ждём подольше и перепроверяем имя; всё остальное — повторяем по таймеру с паузой. Три ветки, а разница огромная: первый вариант замолчит навсегда при первом сбое, второй выйдет из-под удара и доложит, что произошло и когда.
И последнее: словарь статусов — фундамент, на котором стоит следующий этаж. Различение кодов — ровно то, чего не хватает флагу авто-реконнекта, и ровно то, что даёт ручная машина: в уроке про reconnect-машину эти три ветки превращаются в состояния с растущими паузами и счётчиком попыток. Без словаря машина не собирается вовсе, а с ним собирается за один вечер — идите дальше, всё готово.
- 1. WiFi.begin() вызван, ассоциация ещё идёт. Что вернёт status()?
- 2. Роутер выключили из розетки секунду назад. Что вернёт status() на подключённой плате?
- 3. Когда выпадает код 1, WL_NO_SSID_AVAIL?
- 4. Чем плох вечный while (WiFi.status() != WL_CONNECTED) без лимита?
- 5. Почему сравнение WiFi.status() == 3 — плохая привычка?
- WiFi.status() возвращает число 0-6: ноль — между begin() и итогом, единица — SSID не найден, тройка — подключено, четвёрка — сорвавшаяся ассоциация, пятёрка — обрыв, шестёрка — не подключено
- Ноль читают с контекстом: сразу после begin() — норма, через минуты тишины — попытки исчерпаны
- Тройка не сертификат здоровья: после гибели роутера она держится десятки секунд, здоровье проверяют трафиком
- Код 1 — финальный: ждите опечатки в SSID и скрытых сетей, а не чудес от ожидания
- Сравнивают именованными константами, не числами; вечный while в setup() заменяют лимитом попыток с диагнозом в Serial
Частые вопросы по теме урока
- WiFi.begin() вызван, ассоциация ещё идёт. Что вернёт status()?
- Ноль — транзитный код между вызовом begin() и его итогом. На части портов ядра нулём же помечают исчерпанные попытки, поэтому читайте его вместе с контекстом и меткой времени. 0 WL_IDLE_STATUS
- Роутер выключили из розетки секунду назад. Что вернёт status() на подключённой плате?
- Статус — про ассоциацию, а не про здоровье канала. Десятки секунд после гибели роутера тройка продолжает возвращаться, поэтому живость проверяют пингом или трафиком, а не status(). Ещё 3: стек узнаёт о смерти соседа лишь после таймаутов подтверждений
- Когда выпадает код 1, WL_NO_SSID_AVAIL?
- Единица — финальный вердикт по эфиру: опечатка в имени, роутер выключен или сеть скрыта. Код финальный, ждать после него бессмысленно — проверяйте имя байтово и роутер. Эфир прослушан, SSID в нём не найден
- Чем плох вечный while (WiFi.status() != WL_CONNECTED) без лимита?
- Драйвер уже всё проверил и ждёт нечего, а цикл продолжает стоять. Лимит в попытках и времени позволяет плате выйти с диагнозом и работать без сети. При коде 1 он зависает навсегда: сети нет, а выхода из цикла нет
- Почему сравнение WiFi.status() == 3 — плохая привычка?
- Числа в шпаргалке — из ESP32-ядра; другой порт может нумеровать иначе. Имя WL_CONNECTED компилятор превращает в правильное число для конкретной платы и оставляет код читаемым. Нумерация кодов расходится между портами и прошивками — сравнивают именованной константой