Освой Arduino играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

ESP32: Wi-Fi

Базовый минимальный пример
Получить IP-адреса
Узнать настройки Wi-Fi модуля
WiFiScan
Сканируем WiFi-сеть. Расширенный вариант
Точка доступа
Отслеживаем момент подключения и отключения устройства к точке доступа
Mac-адрес для точки доступа
Включаем поддержку IPv6
Сколько устройств подключено к точке доступа

Для работы с Wi-Fi понадобится встроенная библиотека WiFi.h (исходники). В большинстве примеров вам надо знать имя сети и пароль от него. После окончания работы желательно вызывать метод WiFi.disconnect(true);.

Базовый минимальный пример

Сначала рассмотрим базовый пример для общего понимания. Все пояснения в комментариях.


// Подключаем библиотеку
#include "WiFi.h"

// Указываем идентификатор и пароль от своей WiFi-сети
const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPassword";

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

  // Начинаем подключение к сети
  WiFi.begin(ssid, password);

  // Проверяем статус. Если нет соединения, то выводим сообщение о подключении
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Соединяемся к WiFi-сети...");
  }

  Serial.println("Есть подключение к WiFi-сети");
}

void loop() {}

В нашем примере соединение с сетью WiFi происходит лишь в функции setup(), которая активируется только один раз при включении платы. Соответственно, при перезагрузке маршрутизатора или потере WiFi-сигнала соединение не будет восстановлено. Чтобы этого избежать, нужно добавить в функцию loop() проверку на необходимость восстановления соединения.


while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    WiFi.begin(ssid, password);
}

Для простых примеров это не требуется, но в реально действующем коде такая проверка желательна.

Теперь можно писать более сложные примеры.

Получить IP-адреса

В пассивном режиме без входа в сеть вы получите IP-адрес, равный 0.0.0.0. Если нужно получить реальный адрес, то скетч нужно переписать. Добавим возможность входа в сеть, используя учётные данные.


#include "WiFi.h"

const char* ssid = "yourNetworkName";
const char* password =  "yourNetworkPassword";

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

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  Serial.print("Hostname: ");
  Serial.println(WiFi.getHostname());

  Serial.print("ESP Mac Address: ");
  Serial.println(WiFi.macAddress());

  Serial.print("Subnet Mask: ");
  Serial.println(WiFi.subnetMask());

  Serial.print("Gateway IP: ");
  Serial.println(WiFi.gatewayIP());
  Serial.print("DNS: ");
  Serial.println(WiFi.dnsIP());
}

void loop() {}

Теперь вы получите реальный IP-адрес. Узнав адрес через WiFi.localIP(), вы можете в командной строке ввести команду ping ESP_ADDRESS (подставьте ваш адрес), чтобы убедиться, что устройство находится в сети. Пригодится для отладки примеров.

Метод WiFi.getHostname() возвращает имя платы espressif. По идее по этому имени тоже можно обращаться через команду ping espressif, но у меня этот вариант не заработал.

Также привёл другие вызовы функций, которые встречаются в библиотеке.

Другой вариант получения IP-адреса через лямбда-функции.


#include "WiFi.h"

const char* ssid = "yourNetworkName";
const char* password =  "yourNetworkPassword";

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

  WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info) {
    Serial.println(WiFi.localIP());
    Serial.println(WiFi.getHostname());
  }, SYSTEM_EVENT_STA_GOT_IP);

  WiFi.begin(ssid, password);
}

void loop() {}

Ещё один пример получения IP-адреса разными способами. На этот раз обойдёмся без лямбда-функции. Как и в предыдущем примере мы отслеживаем событие SYSTEM_EVENT_STA_GOT_IP и при его наступлении вычисляем адрес.

Адрес вычисляем тремя способами. Первый способ самый простой, вызываем функцию localIP() и получаем готовый результат. Второй способ - получаем информацию из WiFiEventInfo_t через его поле got_ip. Третий способ - используем класс IPAddress. Два последних способа даны для общего развития, пользуйтесь первым.


#include "WiFi.h"

const char* ssid = "yourNetworkName";
const char* password =  "yourNetworkPassword";

void WiFiStationGotIP(WiFiEvent_t event, WiFiEventInfo_t info)
{
  Serial.println(WiFi.localIP()); // первый способ

  // второй способ
  Serial.print(ip4_addr1(&(info.got_ip.ip_info.ip)));
  Serial.print(".");
  Serial.print(ip4_addr2(&(info.got_ip.ip_info.ip)));
  Serial.print(".");
  Serial.print(ip4_addr3(&(info.got_ip.ip_info.ip)));
  Serial.print(".");
  Serial.print(ip4_addr4(&(info.got_ip.ip_info.ip)));

  Serial.println();
  // третий способ
  Serial.println(IPAddress(info.got_ip.ip_info.ip.addr));
}

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

  WiFi.onEvent(WiFiStationGotIP, SYSTEM_EVENT_STA_GOT_IP);

  WiFi.begin(ssid, password);
}

void loop() {}

Узнать настройки Wi-Fi модуля

Для скетча нам не понадобится вводить пароль от Wi-Fi, достаточно просто включить сам Wi-Fi на плате и узнать его Mac-адрес, запустить диагностику, узнать локальный адрес.


/*
   ESP-32 Example,
   start WiFi (without ssid and password)
   and print the MAC address to Serial.
*/

#include <WiFi.h>

byte mac[6];

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

  Serial.print("\nRun diagnostic...\n");
  WiFi.printDiag(Serial);

  Serial.println();
  Serial.print("Program Start...");

  WiFi.enableSTA(true);
  WiFi.begin();
  Serial.println("WiFi began");
  WiFi.macAddress(mac);

  Serial.print("MAC: ");
  Serial.print(mac[0], HEX);
  Serial.print(":");
  Serial.print(mac[1], HEX);
  Serial.print(":");
  Serial.print(mac[2], HEX);
  Serial.print(":");
  Serial.print(mac[3], HEX);
  Serial.print(":");
  Serial.print(mac[4], HEX);
  Serial.print(":");
  Serial.println(mac[5], HEX);

  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.print("Hostname: ");
  Serial.println(WiFi.getHostname());
}

void loop() {}

WiFiScan

Встроенный пример Examples/WiFi/WiFiScan служит для сканирования WiFi-сети. Выводит число найденных точек и их названия.

В скетче используются следующие функции:

  • WiFi.mode(WIFI_STA) - устанавливает нужный режим
  • WiFi.disconnect() - завершает соединение
  • WiFi.scanNetworks() - сканирует сети и возвращает число найденных точек доступа
  • WiFi.RSSI(i) - возвращает уровень сигнала по индексу
  • WiFi.SSID(i) - возвращает имя точки доступа по индексу

#include "WiFi.h"

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

    // Set WiFi to station mode and disconnect from an AP if it was previously connected
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);

    Serial.println("Setup done");
}

void loop()
{
    Serial.println("scan start");

    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    Serial.println("scan done");
    if (n == 0) {
        Serial.println("no networks found");
    } else {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
            // Print SSID and RSSI for each network found
            Serial.print(i + 1);
            Serial.print(": ");
            Serial.print(WiFi.SSID(i));
            Serial.print(" (");
            Serial.print(WiFi.RSSI(i));
            Serial.print(")");
            Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
            delay(10);
        }
    }
    Serial.println("");

    // Wait a bit before scanning again
    delay(5000);
}

Сканируем WiFi-сеть. Расширенный вариант

Чтобы просканировать текущую WiFi-сеть, нам понадобятся учётная запись для входа в неё (идентификатор и пароль). После успешного входа запускаем сканирование через функцию WiFi.scanNetworks(), которая вернёт информацию о всех точках доступа с сопутствующими свойствами.

Для удобства часть кода поместим в отдельные функции: scanNetworks() и connectToNetwork() (её можно использовать при использовании контроллера в качестве веб-клиента.


#include <WiFi.h>

const char* ssid = "yourNetworkName";
const char* password =  "yourNetworkPassword";

String translateEncryptionType(wifi_auth_mode_t encryptionType) {
  switch (encryptionType) {
    case (WIFI_AUTH_OPEN):
      return "Open";
    case (WIFI_AUTH_WEP):
      return "WEP";
    case (WIFI_AUTH_WPA_PSK):
      return "WPA_PSK";
    case (WIFI_AUTH_WPA2_PSK):
      return "WPA2_PSK";
    case (WIFI_AUTH_WPA_WPA2_PSK):
      return "WPA_WPA2_PSK";
    case (WIFI_AUTH_WPA2_ENTERPRISE):
      return "WPA2_ENTERPRISE";
  }
}

void scanNetworks() {
  int numberOfNetworks = WiFi.scanNetworks();

  Serial.print("Number of networks found: ");
  Serial.println(numberOfNetworks);

  for (int i = 0; i < numberOfNetworks; i++) {
    Serial.print("Network name: ");
    Serial.println(WiFi.SSID(i));

    Serial.print("Signal strength: ");
    Serial.println(WiFi.RSSI(i));

    Serial.print("MAC address: ");
    Serial.println(WiFi.BSSIDstr(i));

    Serial.print("Encryption type: ");
    String encryptionTypeDescription = translateEncryptionType(WiFi.encryptionType(i));
    Serial.println(encryptionTypeDescription);
    Serial.println("-----------------------");
  }
}

void connectToNetwork() {
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Соединяемся с Wi-Fi..");
  }

  Serial.println("Соединение с Wi-Fi установлено");
}

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

  scanNetworks();
  connectToNetwork();

  Serial.println(WiFi.macAddress());
  Serial.println(WiFi.localIP());
 
  WiFi.disconnect(true);
  Serial.println(WiFi.localIP());
}

void loop() {}

Обратите внимание, что мы получаем IP-адрес два раза. Первый раз при выходе в интернет, а второй раз - когда закрываем WiFi-соединение. Во втором случае IP-адрес будет равен 0.0.0.0

В мониторе порта выводится следующая информация (часть данных я убрал в целях безопасности).


Network name: Keenetic-7247
Signal strength: -79
MAC address: *:*:*:*:*:0A
Encryption type: WPA2_PSK
-----------------------
Network name: how2make 2.3
Signal strength: -91
MAC address: *:*:*:*:*:90
Encryption type: WPA_WPA2_PSK
-----------------------
Network name: TP-Link_4A15
Signal strength: -91
MAC address: *:*:*:*:*:84
Encryption type: WPA_WPA2_PSK
-----------------------
Соединяемся с Wi-Fi..
Соединяемся с Wi-Fi..
Соединение с Wi-Fi установлено
*:*:*:*:*:20
192.163.2.21
0.0.0.0

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

Плата ESP32 может работать как точка доступа. Для первого знакомства получим базовую информацию о точке доступа через доступные функции. Мы можем установить собственные значения для SSID и пароля (пароль можно даже не указывать, чтобы сеть была открытой).


#include "WiFi.h"
 
const char* ssid = "ESP32Cat";
const char* password =  "meow";
 
void setup() {
 
  Serial.begin(115200);
  // WiFi.softAP(ssid, password);
  WiFi.softAP(ssid); // без пароля
 
  Serial.println();
  Serial.print("IP address: ");
  Serial.println(WiFi.softAPIP());
 
}
 
void loop() {}

После загрузки скетча мы получим IP-адрес платы. В настройках компьютера или смартфона должна появиться созданная точка доступа.

Кстати, когда пробовал вариант с паролем, то точка доступа не создалась почему-то. Upd: читатель сайта прислал комментарий по этому поводу: Дело в том, что любая точка доступа в пароле должна иметь минимум 8 символов, если я правильно помню, а в примере всего 4, поэтому скетч ведёт себя некорректно. В этом вся проблема, указываете от 8 символов - код работает стабильно.

Отслеживаем момент подключения и отключения устройства к точке доступа

За соответствующие события отвечают SYSTEM_EVENT_AP_STACONNECTED и SYSTEM_EVENT_AP_STADISCONNECTED.

После загрузки скетчка в настройках телефона найдите созданную точку доступа и присоединитесь к ней. Момент подключения будет обнаружен и в мониторе порта появится сообщение о соединении.


#include "WiFi.h"

void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info) {
  Serial.println("Соединение установлено");

  // что-то делаем, например, вычисляем mac-адрес
  for (int i = 0; i < 6; i++) {
    Serial.printf("%02X", info.sta_connected.mac[i]);
    if (i < 5)Serial.print(":");
  }

  Serial.println("\n------------");
}

void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) {

  Serial.println("Соединение разорвано");

  // что-то делаем, например, снова вычисляем mac-адрес
  for (int i = 0; i < 6; i++) {
    Serial.printf("%02X", info.sta_disconnected.mac[i]);
    if (i < 5)Serial.print(":");
  }

  Serial.println("\n------------");
}

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

  WiFi.softAP("ESP32Cat");

  WiFi.onEvent(WiFiStationConnected, SYSTEM_EVENT_AP_STACONNECTED);
  WiFi.onEvent(WiFiStationDisconnected, SYSTEM_EVENT_AP_STADISCONNECTED);
}

void loop() {}

Mac-адрес для точки доступа

В первом примере с получением настроек модуля мы вычисляли Mac-адрес через функцию WiFi.macAddress. Существует также функция WiFi.softAPmacAddress(), когда плата работает в режиме точки доступа.


#include "WiFi.h"

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

  WiFi.mode(WIFI_MODE_AP);

  Serial.println(WiFi.softAPmacAddress());
}

void loop() {}

Включаем поддержку IPv6

Мы можем включить поддержку IPv6 и получить адрес в этом формате. Включение поддержки IPv6 происходит при помощи функции softAPenableIpV6(). Вызовем функцию при событии SYSTEM_EVENT_AP_START, когда активируется точка доступа.

После включения поддержки в событии SYSTEM_EVENT_AP_STA_GOT_IP6 (получение адреса) узнаём IPv6-адрес.


#include "WiFi.h"

void WiFiApStarted(WiFiEvent_t event, WiFiEventInfo_t info) {
  WiFi.softAPenableIpV6();
}

void WiFiGotIp(WiFiEvent_t event, WiFiEventInfo_t info) {
  Serial.println(WiFi.softAPIPv6());
}

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

  WiFi.onEvent(WiFiApStarted, SYSTEM_EVENT_AP_START);
  WiFi.onEvent(WiFiGotIp, SYSTEM_EVENT_AP_STA_GOT_IP6);

  WiFi.softAP("ESP32Cat");
}

void loop() {}

Сколько устройств подключено к точке доступа

Узнать число устройств, которые в данный момент подключены к точке доступа, можно через функцию softAPgetStationNum(). После запуска скетча подключите телефон и другие устройства к точке доступа, чтобы увидеть изменения.


#include "WiFi.h"

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

  WiFi.softAP("ESP32Cat");
}

void loop() {
  Serial.print("Stations connected: ");
  Serial.println(WiFi.softAPgetStationNum());
  delay(5000);
}

Дополнительные материалы

Реклама