Панель полива живёт на планшете в прихожей: восемь ярлыков, каждый ведёт на адрес платы — 192.168.1.134 и соседей. Потом роутер обновил прошивку, раздал адреса заново, и половина ярлыков умерла в один вечер. Имена .local выручили бы, но планшет на Android: браузеры там имена mDNS не резолвят — этот расклад я построчно разбирал в уроке про mDNS и имя .local. Для панели, которая должна работать всегда и у всех членов семьи, единственный честный ответ — адрес, который не меняется никогда.
Закрепить адрес можно в двух местах: на роутере (DHCP-резервация по MAC-адресу) или в скетче (WiFi.config до begin). Здесь разберём оба, найдём их границы — от забытого шлюза до дубликата адреса — и сформулирую правило выбора, к которому пришёл после трёх вот таких вечеров с мёртвыми ярлыками. Базу выдачи адресов роутером мы проходили в уроке про подключение к роутеру — здесь эта база понадобится с первой строки.
WiFi.config: четыре аргумента, о которых помнят три
Сигнатура честная: WiFi.config(local_ip, gateway, subnet, dns1, dns2). Вызывают её до WiFi.begin(), а в ручном цикле переподключения — перед каждым begin, чтобы после разрыва стек собрал соединение с теми же настройками; сам цикл с растущими паузами разобран в уроке про reconnect-машину, здесь только место config в нём. Для домашней сети расклад такой: маска почти всегда 255.255.255.0, шлюз и DNS — адрес роутера (192.168.1.1 в моём случае), а сам адрес платы — из той же подсети, подальше от пула DHCP.
Каждый пропущенный аргумент имеет цену, и она приходит не сразу. Забытый шлюз: плата в сети, локальные адреса отвечают, ярлык открывается — а наружу тишина: NTP из урока про синхронизацию времени не отвечает, запросы к API молчат. Забытый DNS: запросы по IP работают, по имени — нет, и боль приходит к MQTT-брокеру или любому http-адресу. Адрес из чужой подсети: плата сама себе, в сети её не видно. Я однажды вечером из четвёртого пункта этого списка вынес привычку печатать в Serial при старте не только локальный адрес, но и шлюз с DNS — три строки, которые экономят полчаса у щитка.
| Способ закрепить адрес | Механика | Граница |
|---|---|---|
| DHCP-резервация на роутере | адрес привязан к MAC из WiFi.macAddress(), скетч не трогаем | живёт в одном роутере: сменили роутер — настраиваем заново |
| Статика в скетче (WiFi.config) | адрес едет вместе с платой | в чужой сети сломается; шлюз и DNS надо не забыть |
| Имя .local (mDNS) | адрес не нужен вовсе | Windows и Android без дополнительного софта имена не резолвят |
| Гибрид: статика + имя | адрес постоянный, имя для людей | мой выбор для стационарных плат с веб-мордой |
#include <WiFi.h> const char* SSID_NAME = "HomeNet";const char* PASS = "12345678"; IPAddress IP(192, 168, 1, 250); // хвост подсети, вне пула DHCPIPAddress GW(192, 168, 1, 1); // роутерIPAddress MASK(255, 255, 255, 0);IPAddress DNS1(192, 168, 1, 1); // или 8.8.8.8, если роутер кривитIPAddress DNS2(8, 8, 8, 8); void podklyuchit() { WiFi.config(IP, GW, MASK, DNS1, DNS2); // перед КАЖДЫМ begin WiFi.begin(SSID_NAME, PASS); while (WiFi.status() != WL_CONNECTED && millis() < 15000) { delay(300); Serial.print("."); } Serial.println();} void setup() { Serial.begin(115200); podklyuchit(); Serial.print("ip: "); Serial.println(WiFi.localIP()); Serial.print("gw: "); Serial.println(WiFi.gatewayIP()); Serial.print("dns: "); Serial.println(WiFi.dnsIP()); if (WiFi.gatewayIP() != GW) { Serial.println("shluz ne tot - proveryaj config"); }} unsigned long tWatch = 0; void loop() { unsigned long ms = millis(); if (ms - tWatch >= 10000) { tWatch = ms; if (WiFi.status() != WL_CONNECTED) { Serial.println("svyaz net: podnimayu snova s statikoj"); podklyuchit(); } }}Почему config перед каждым begin, а не один раз в setup: после разрыва и disconnect стек собирает соединение заново, и я предпочитаю, чтобы каждая попытка начиналась с известных настроек — строка бесплатная, а её отсутствие когда-то стоило мне ночи, когда после падения связи плата вернулась с DHCP-адресом и половина дашборда перестала видеть датчик. Проверка WiFi.gatewayIP() != GW — тот же жанр самоконтроля: если шлюз не тот, значит, config не успел или сеть перехитрила вас, и лучше узнать это в Serial, чем по мёртвому NTP через неделю.
Выбор самого адреса — отдельный маленький навык. Откройте настройки роутера и посмотрите диапазон пула DHCP: у домашних моделей это обычно 192.168.1.100-199. Адрес берите из хвоста подсети — .240-.254 — или прямо делайте резервацию на этот же адрес: тогда даже чужая лаптоп-гостья не утащит ваш .250. Адрес в резервации и адрес в config должны совпадать: резервация гарантирует, что никто больше его не получит, статика — что плата его всегда просит. Мой печальный опыт с дубликатом — в note ниже.
Быстрая проверка, что статика встала: после прошивки обесточьте плату на минуту, перезапустите роутер и включите обратно — адрес в Serial обязан быть тем же. Вторая проверка честнее: на два-три дня повесьте в loop печать адреса раз в час вместе с аптаймом. Если за двое суток адрес ни разу не сменился, ярлыки можно вешать навсегда. У меня такая печать живёт постоянной строкой в журнале каждой стационарной платы: адрес, шлюз, уровень сигнала — приборная панель узла в одну строку, и любое отклонение видно в первом же журнале.
Когда адрес, а когда имя
Правило у меня теперь одно предложение: адрес — машинам, имя — людям. Дашборды, curl-скрипты, порт-форвардинг, ярлыки на планшетах без Bonjour, брокер из урока про MQTT на ESP32 — всем им нужен адрес, который не меняется, потому что поле «хост» в настройках не умеет уговариваться. Имя .local — для человека за ноутбуком: на маке или Linux это самый быстрый путь к плате, и ничего не стоит держать MDNS.begin рядом со статикой — имя и адрес прекрасно живут в одном скетче, веб-страница из урока про мини-сервер открывается обоими способами сразу.
И обратная сторона, о которой спрашивают чаще всего: что случится со статикой в чужой сети? Ничего хорошего — плата просит .250 из вашей подсети, а та сеть живёт в 192.168.0.x, и связь не поднимется вовсе. Поэтому для мобильных сборок — выставка, дача, стенд друга — я прописываю статике флаг: отключил в скетче одну константу, плата вернулась к DHCP. Стационарная плата, которая годами висит в одной сети, получает статику без флага и резервацию на роутере вдобавок: два слоя страховки, ноль сюрпризов после обновлений прошивки роутера.
Отдельный вопрос — доступ из-за периметра: порт-форвардинг на роутере висит на адресе, и DHCP-лотерея здесь уже не шутит — проброс, настроенный на .134, молча умрёт вместе со сменой адреса. Для таких схем статика вообще не обсуждается, а к ней добавляется второй этаж: нестандартный внешний порт, длинный пароль, ограничение по источнику. Локальная самоделка обойдётся резервацией, но как только плату начинают адресовать извне дома, адрес перестаёт быть удобством и становится частью контракта с наружным миром.
Итог: DHCP-грабля смены адреса решается в двух местах, и лучший результат даёт пара. Резервация на роутере защищает адрес от чужих, статика в скетче гарантирует, что плата его всегда просит, имя mDNS поверх — чтобы людям не приходилось помнить цифры. Печатайте адрес, шлюз и DNS при старте, проверяйте шлюз сравнением, берите адрес из хвоста подсети — и ярлыки перестанут умирать после каждого обновления роутера.
Домашнее задание: найдите в настройках своего роутера пул DHCP и диапазон резерваций. Выберите адрес из хвоста подсети, пропишите config с четырьмя аргументами, перезагрузите роутер трижды и следите за адресом платы в Serial — при живой статике он должен вернуться тем же. Дальше по маршруту раздела навесьте на эту плату время и телеметрию: часы с NTP наконец заработают, публикация показаний на брокер встанет в тот же скетч — обе темы уже ждут вас по соседству в разделе.
- 1. Когда вызывают WiFi.config()?
- 2. Задали адрес и маску, забыли шлюз. Какой симптом?
- 3. Что такое DHCP-резервация?
- 4. Android-браузер и адрес esp32-kladovka.local — что будет?
- 5. Два устройства в сети получили один адрес. Что происходит?
- WiFi.config(local_ip, gateway, subnet, dns1, dns2) — до WiFi.begin(), а в reconnect-цикле перед каждой попыткой
- Каждый пропущенный аргумент имеет цену: без шлюза мёртво всё наружное, без DNS не резолвятся имена
- Адрес берите из хвоста подсети вне пула DHCP и закрывайте резервацией по MAC из WiFi.macAddress()
- Печатайте ip, gateway и dns при старте и сверяйте шлюз с тем, что прописали в config, — диагноз в Serial дешевле ночи гаданий
- Правило выбора: адрес — машинам и дашбордам, имя .local — людям за mac и Linux; для стационарных плат мой выбор — гибрид
Частые вопросы по теме урока
- Когда вызывают WiFi.config()?
- Стек собирает соединение с теми настройками, что заданы до begin. После разрыва я вызываю config перед каждой попыткой — строка бесплатная, а сюрпризов нет. До WiFi.begin(), а в цикле переподключения — перед каждым begin
- Задали адрес и маску, забыли шлюз. Какой симптом?
- Без шлюза плата ходит по своей подсети и не знает, куда сдавать пакеты наружу. Локальный дашборд жив, NTP и внешние API мертвы. Локальные адреса отвечают, наружу тишина: NTP и API молчат
- Что такое DHCP-резервация?
- Роутер всегда выдаёт этому MAC один и тот же адрес. MAC платы печатает WiFi.macAddress(). Резервация не требует правок кода, но живёт только в этом роутере. Привязка адреса к MAC-адресу на роутере, скетч не трогаем
- Android-браузер и адрес esp32-kladovka.local — что будет?
- Системной поддержки mDNS в Android-браузерах нет. Именно этот случай — главный аргумент за фиксированный адрес для панелей и ярлыков. Имена .local браузеры Android не резолвят — нужен адрес или приложение с NSD
- Два устройства в сети получили один адрес. Что происходит?
- Два устройства отвечают за один адрес, и соседи по сети путаются, кому слать пакеты. Лечение: адрес из хвоста подсети вне пула и резервация на роутере. ARP-спор: соединения рвутся, страницы отдаются вперемешку