Инструкции

Подключение ESP32 к Wi-Fi: клиент, точка доступа и переподключение

13 мин чтения·Август 2026·RoboArm R1

Wi-Fi — главная причина, по которой берут ESP32 вместо обычной Arduino. Базовое подключение умещается в пять строк, но скетч из первого примера в интернете обычно намертво зависает в setup(), если роутер недоступен, и не восстанавливает связь после разрыва. Ниже — оба режима работы, надёжная схема подключения и разбор проблем, на которые уходит больше всего времени.

Примеры рассчитаны на пакет плат esp32 by Espressif Systems 3.x; если он ещё не установлен, начните с настройки Arduino IDE для ESP32.

Три режима работы

ESP32 умеет быть клиентом чужой сети, собственной точкой доступа или тем и другим одновременно.

РежимКонстантаЧто делает
КлиентWIFI_STAПодключается к роутеру, получает IP от него
Точка доступаWIFI_APРаздаёт свою сеть, клиенты подключаются к плате
СовмещённыйWIFI_AP_STAОдновременно клиент и точка доступа

Режим клиента нужен, когда плата должна ходить в интернет или быть доступной в домашней сети. Точка доступа — когда роутера рядом нет: телефон подключается прямо к плате, например для первичной настройки устройства.

Подключение к сети: минимальный скетч

#include <WiFi.h>

const char* ssid = "MyNetwork";
const char* password = "MyPassword";

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  Serial.print("Подключение");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.print("IP-адрес: ");
  Serial.println(WiFi.localIP());
}

void loop() {
}

Такой скетч годится для проверки связи и первых экспериментов, но в готовом устройстве его использовать нельзя: цикл while блокирует выполнение навсегда, если роутер выключен или пароль неверный. Плата не мигнёт светодиодом, не ответит по Serial и будет выглядеть зависшей.

ℹ️

Строго говоря, WiFi.begin() включает режим клиента сам, и для простого подключения отдельный WiFi.mode(WIFI_STA) не обязателен. Но выключить точку доступа он не умеет — только добавляет к текущему режиму роль клиента. Поэтому если раньше в той же программе поднимался softAP(), плата окажется в режиме WIFI_AP_STA и продолжит раздавать свою сеть. Явный mode() перед begin() эту ситуацию закрывает и делает намерение очевидным для читателя кода.

⚠️

Имя сети и пароль в примерах записаны прямо в коде — так удобно проверять, но не стоит в таком виде коммитить проект в публичный репозиторий. Вынесите их в отдельный заголовочный файл (secrets.h) и добавьте его в .gitignore, либо храните в Preferences и задавайте через режим точки доступа при первой настройке.

Статусы подключения

WiFi.status() возвращает не только «подключено / не подключено» — по коду видно, на каком шаге всё сломалось.

ЗначениеЧто означает
WL_IDLE_STATUSПодключение начато, идёт процесс
WL_NO_SSID_AVAILСеть с таким именем не найдена
WL_CONNECT_FAILEDНайдена, но подключиться не удалось — обычно пароль
WL_CONNECTEDПодключено, IP получен
WL_CONNECTION_LOSTСвязь была и пропала
WL_DISCONNECTEDНе подключено

Если в мониторе порта видно WL_NO_SSID_AVAIL при том, что сеть точно есть, — почти наверняка она работает в диапазоне 5 ГГц (см. раздел о проблемах).

Подключение без блокировки loop

Рабочая схема выглядит иначе: setup() только запускает подключение, а loop() продолжает работать и периодически проверяет связь. Устройство остаётся живым, даже если сети нет.

#include <WiFi.h>

const char* ssid = "MyNetwork";
const char* password = "MyPassword";

const unsigned long reconnectInterval = 10000;
unsigned long lastReconnectAttempt = 0;

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.setAutoReconnect(true);
  WiFi.begin(ssid, password);
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) {
    unsigned long now = millis();
    if (now - lastReconnectAttempt >= reconnectInterval) {
      lastReconnectAttempt = now;
      Serial.println("Нет связи, повторное подключение");
      WiFi.disconnect();
      WiFi.begin(ssid, password);
    }
    return;
  }

  // Основная работа устройства
}

Два важных момента. Повторные попытки идут по таймеру на millis(), а не через delay() — иначе вернётся та же блокировка. И перед новым begin() вызывается disconnect(): без него стек может остаться в промежуточном состоянии и игнорировать попытки подключения.

События вместо опроса

Опрашивать WiFi.status() в цикле просто, но не всегда удобно: логика подключения размазывается по loop(). Альтернатива — подписаться на события стека и реагировать на них.

#include <WiFi.h>

void onWiFiEvent(arduino_event_id_t event, arduino_event_info_t info) {
  switch (event) {
    case ARDUINO_EVENT_WIFI_STA_CONNECTED:
      Serial.println("Подключено к точке доступа");
      break;
    case ARDUINO_EVENT_WIFI_STA_GOT_IP:
      // Адрес берём из самого события, а не через WiFi.localIP()
      Serial.print("Получен IP: ");
      Serial.println(IPAddress(info.got_ip.ip_info.ip.addr));
      break;
    case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
      Serial.println("Связь потеряна");
      break;
    default:
      break;
  }
}

void setup() {
  Serial.begin(115200);
  WiFi.onEvent(onWiFiEvent);
  WiFi.mode(WIFI_STA);
  WiFi.begin("MyNetwork", "MyPassword");
}

void loop() {
}

Обратите внимание: подключение к точке доступа и получение IP — разные события. Отправлять данные в сеть можно только после GOT_IP; сразу после STA_CONNECTED адреса ещё нет. Адрес приходит прямо в структуре события — в примере он берётся оттуда, а не отдельным запросом к стеку.

⚠️

Обработчик выполняется в отдельной задаче стека Wi-Fi, а не в loop(). Держите его коротким: никаких delay(), циклов ожидания и сетевых запросов — HTTP, MQTT, работы с сокетами. Всё это блокирует стек и роняет соединение. Правильный приём — выставить в обработчике флаг и сделать полезную работу в loop().

Статический IP

По умолчанию адрес выдаёт роутер по DHCP, и он может меняться. Если к плате нужно обращаться по фиксированному адресу, задайте его до begin():

IPAddress localIp(192, 168, 1, 50);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDns(8, 8, 8, 8);

WiFi.config(localIp, gateway, subnet, primaryDns);
WiFi.begin(ssid, password);

Адрес должен лежать в подсети роутера и вне диапазона его DHCP-пула, иначе он рано или поздно достанется другому устройству. Часто удобнее не трогать плату, а закрепить адрес за её MAC на самом роутере.

Заодно стоит задать имя устройства в сети — тогда плату будет видно в списке клиентов роутера под понятным именем. Порядок вызовов здесь принципиален: setHostname() должен идти до WiFi.mode() и WiFi.begin(), иначе он молча не подействует и в списке клиентов останется имя вида espressif.

void setup() {
  Serial.begin(115200);

  WiFi.setHostname("roboarm");  // строго до mode() и begin()
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
}

Несколько сетей: WiFiMulti

Если устройство переезжает между домом и мастерской, пропишите обе сети — плата сама выберет доступную с лучшим сигналом.

#include <WiFi.h>
#include <WiFiMulti.h>

WiFiMulti wifiMulti;

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);

  wifiMulti.addAP("HomeNetwork", "homePassword");
  wifiMulti.addAP("WorkshopNetwork", "workshopPassword");

  if (wifiMulti.run(15000) == WL_CONNECTED) {
    Serial.println(WiFi.localIP());
  }
}

void loop() {
  wifiMulti.run();
}

Аргумент run() — таймаут в миллисекундах. Вызов в loop() сам восстанавливает связь при обрыве, поэтому отдельная логика переподключения не нужна.

Точка доступа

В режиме AP плата поднимает собственную сеть. Это стандартный способ настроить устройство, когда домашнего Wi-Fi ещё нет: пользователь подключается к сети платы с телефона и открывает страницу настроек.

#include <WiFi.h>

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_AP);
  WiFi.softAP("Robot-Setup", "12345678");

  Serial.print("Адрес точки доступа: ");
  Serial.println(WiFi.softAPIP());
}

void loop() {
}

Адрес по умолчанию — 192.168.4.1. Пароль должен быть не короче восьми символов: при более коротком softAP() вернёт false и точка доступа не поднимется вообще — ни защищённой, ни открытой. Проверять результат стоит всегда:

if (!WiFi.softAP("Robot-Setup", "12345678")) {
  Serial.println("Не удалось поднять точку доступа");
}

Чтобы сделать сеть открытой намеренно, передайте только имя — параметр пароля по умолчанию равен NULL, и режим шифрования останется открытым:

WiFi.softAP("Robot-Setup");

Совмещённый режим включается одной строкой и позволяет одновременно быть в домашней сети и держать свою:

WiFi.mode(WIFI_AP_STA);
WiFi.softAP("Robot-Setup", "12345678");
WiFi.begin("MyNetwork", "MyPassword");
ℹ️

У ESP32 один радиомодуль на оба режима, поэтому в WIFI_AP_STA точка доступа работает на том же канале, что и подключение к роутеру. Скорость и стабильность обеих сетей при этом заметно падают — режим хорош для настройки, но не для постоянной работы.

Сканирование сетей

Список доступных сетей пригодится и для отладки, и для экрана выбора сети в интерфейсе устройства.

#include <WiFi.h>

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  int networkCount = WiFi.scanNetworks();
  for (int i = 0; i < networkCount; i++) {
    Serial.printf("%s  %d dBm  %s\n",
                  WiFi.SSID(i).c_str(),
                  WiFi.RSSI(i),
                  WiFi.encryptionType(i) == WIFI_AUTH_OPEN ? "открытая" : "защищённая");
  }
}

void loop() {
}

Уровень сигнала RSSI измеряется в дБм и всегда отрицательный: от −30 (плата рядом с роутером) до −90 (на грани разрыва). Значения хуже −80 дБм означают, что связь будет рваться, — двигайте плату или ставьте модуль с внешней антенной.

ℹ️

Скрытые сети в этот список по умолчанию не попадают. Чтобы их увидеть, включите показ явно: WiFi.scanNetworks(false, true) — первый аргумент отвечает за асинхронный режим, второй как раз за скрытые сети. У них будет пустой SSID(), опознать точку можно только по BSSID.

Частые проблемы

Сеть не находится, хотя она есть. Классический ESP32, а также ESP32-S3, ESP32-C3 и ESP32-C6 работают только в диапазоне 2,4 ГГц. Если роутер раздаёт 2,4 и 5 ГГц под одним именем, отключите 5 ГГц на время проверки или создайте отдельную гостевую сеть на 2,4 ГГц. Двухдиапазонный только ESP32-C5.

Пароль верный, но подключения нет. Посмотрите на режим шифрования: WPA3 (SAE) ESP32 поддерживает, но на связке «роутер в режиме WPA3-only + старая версия пакета плат» подключение иногда не проходит. Если упёрлись — переключите роутер на смешанный режим WPA2/WPA3 и обновите пакет esp32 до актуальной версии.

Имя сети на кириллице. Само по себе оно не ломается: SSID — это просто 32 байта, и стек работает с ними как с байтами. Но подводных камней два. Espressif поддерживает только кодировку UTF-8 и в своей документации прямо не рекомендует не-ASCII имена из-за неоднозначности кодировок. И лимит в 32 байта для кириллицы означает всего около 16 символов, потому что каждый занимает по два байта.

Скрытая сеть. Подключаться к ней можно обычным WiFi.begin(ssid, password) — канал и BSSID указывать не обязательно, их значения по умолчанию (0 и NULL) работают. А вот увидеть такую сеть в списке сканирования по умолчанию нельзя, для этого нужен отдельный флаг (см. раздел про сканирование).

Плата перезагружается в момент подключения. В логе будет Brownout detector was triggered. Включение радиомодуля даёт скачок тока до 300–500 мА, и питания по дешёвому USB-кабелю не хватает. Смените кабель на короткий и толстый, воткните в порт компьютера напрямую или запитайте плату отдельным источником.

Перестали работать аналоговые входы. При активном Wi-Fi недоступен ADC2 — это пины GPIO 0, 2, 4, 12–15, 25–27 у классического ESP32. Переносите аналоговые датчики на ADC1: GPIO 32–39.

Связь рвётся раз в несколько минут. Отключите энергосбережение радиомодуля, если устройство должно отвечать мгновенно:

WiFi.setSleep(false);

Ток потребления при этом вырастет примерно на 30–40 мА — для устройств с питанием от сети это приемлемая плата за стабильность.

Источники

Примеры проверены на пакете плат esp32 by Espressif Systems 3.x. Имена событий и сигнатуры функций в версиях 2.x отличаются — если пакет старый, сверяйтесь с документацией своей версии.

  • Arduino-ESP32: Wi-Fi API — режимы, события и методы класса WiFi.
  • ESP-IDF: Wi-Fi Driver — поведение стека, события и режимы энергосбережения.
  • ESP-FAQ: Wi-Fi — скрытые сети и оговорки про не-ASCII имена сетей.

Что дальше

Рекомендуемые продукты

Связанные статьи