5 Экспериментов С Wifi На Esp32

Привет Хабр.

Платы ESP32 пользуются большой популярностью благодаря невысокой цене, хорошей вычислительной мощности (процессор 200 МГц), развитому SDK с поддержкой как MicroPython, так и Arduino IDE, наличию GPIO с поддержкой периферии (SPI, I2C и т.д.) и беспроводной связи.

(Wi-Fi, Bluetooth).

Сегодня мы рассмотрим, что можно сделать с такой платой, которая стоит всего около 12 долларов.



5 экспериментов с WiFi на ESP32

Мы рассмотрим разные варианты использования WiFi, от простого подключения к сети до сниффера WiFi. Для тестов вам понадобится любая плата с ESP32 (желательно с OLED-экраном, как на картинке) и Arduino IDE. Для тех, кому интересно, как это работает, продолжение под катом.

Как подключить библиотеки ESP32 к Arduino IDE писать не буду, желающие могут посмотреть Здесь .

Отмечу лишь, что у этой платы есть особенность — для загрузки кода из Arduino IDE нужно во время загрузки нажать и удерживать кнопку Boot. В остальном использование платы ничем не отличается от обычного Arduino. Теперь давайте начнем с кода.

Все примеры кода полностью готовы к использованию, их можно просто скопировать и вставить в Arduino IDE.



1. Подключитесь к Wi-Fi и узнайте точное время.

Поскольку на плате есть Wi-Fi, самое простое, что мы можем сделать, это подключиться к существующей сети Wi-Fi. Это хорошо известно и работало на ESP8266. Однако просто подключиться и ничего не делать неинтересно; мы покажем вам, как скачать точное время через NTP. Используя приведенный ниже код, можно легко превратить нашу плату ESP в настольные (или, для гиков 100-го уровня, в наручные часы :) часы.



5 экспериментов с WiFi на ESP32

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

Для работы OLED экрана необходимо установить библиотеку SSD1306 .

Переменные ssid и пароль нужно будет заменить на параметры реальной точки доступа, иначе все работает «из коробки».

  
  
  
   

#include <WiFi.h> #include <SSD1306Wire.h> #include <time.h> const char* ssid = "MYWIFI"; const char* password = "12345678"; const char* ntpServer = "pool.ntp.org"; const long gmtOffset_sec = 3600; const int daylightOffset_sec = 3600; // OLED Display 128x64 SSD1306Wire display(0x3c, 5, 4); void setup() { Serial.begin(115200); delay(10); Serial.println('\n'); WiFi.begin(ssid, password); // Connect to the network while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect delay(500); Serial.print('.

'); } Serial.println('\n'); Serial.println("Connection established"); Serial.print("IP address:\t"); Serial.println(WiFi.localIP()); // Get the NTP time configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); // OLED display init display.init(); display.clear(); display.setTextAlignment(TEXT_ALIGN_LEFT); display.setFont(ArialMT_Plain_10); display.drawString(0, 0, "Access Point connected"); display.drawString(0, 24, "AP IP address: "); display.drawString(0, 36, WiFi.localIP().

toString()); display.display(); delay(1000); } void draw_time(char *msg) { display.clear(); display.setTextAlignment(TEXT_ALIGN_CENTER); display.setFont(ArialMT_Plain_24); display.drawString(display.getWidth()/2, 0, msg); display.display(); Serial.println(msg); } void loop() { struct tm timeinfo; if (getLocalTime(&timeinfo)) { char time_str[16]; strftime(time_str, 16, "%H:%M:%S", &timeinfo); draw_time(time_str); } delay(500); }



2. Точка доступа Wi-Fi.

Конечно, мы можем не только подключиться к точке доступа, но и создать свою.

В этом примере мы запустим мини веб-сервер, который можно открыть, например, со смартфона.

Отдельно можно отметить обработку события SYSTEM_EVENT_AP_STACONNECTED, позволяющего узнать, сколько клиентов подключилось к нашей точке доступа.

Фото того, как это работает, показано на КДПВ.



#include <WiFi.h> #include <DNSServer.h> #include <SSD1306Wire.h> // Access Point credentials const char *ssid = "TEST-123"; const char *password = NULL; // "12345678"; int connections = 0; // Onboard WiFi server WiFiServer server(80); String responseHTML = "<!DOCTYPE html><html>" "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}" "</style></head>" "<body><h1>ESP32 Web Server</h1>" "<p>Hello World</p>" "</body></html>"; // OLED Display 128x64 SSD1306Wire display(0x3c, 5, 4); void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info){ connections += 1; showConnectionsCount(); } void showConnectionsCount() { char data[32]; sprintf(data, "Connections: %d", connections); draw_message(data); } void setup() { Serial.begin(115200); Serial.println(); Serial.println("Configuring access point."); // Start access point WiFi.mode(WIFI_AP); WiFi.softAP(ssid, password); WiFi.onEvent(WiFiStationConnected, SYSTEM_EVENT_AP_STACONNECTED); IPAddress ip_address = WiFi.softAPIP(); //IP Address of our accesspoint // Start web server server.begin(); Serial.print("AP IP address: "); Serial.println(ip_address); // Oled display display.init(); // Draw info display.clear(); display.setTextAlignment(TEXT_ALIGN_LEFT); display.setFont(ArialMT_Plain_10); display.drawString(0, 0, "Access Point started"); display.drawString(0, 12, ssid); display.drawString(0, 24, "AP IP address: "); display.drawString(0, 36, ip_address.toString()); display.display(); // Total number of connections showConnectionsCount(); } void draw_message(char *msg) { display.setColor(BLACK); display.fillRect(0, 50, display.getWidth(), 12); display.setColor(WHITE); display.drawString(0, 50, msg); display.display(); Serial.println(msg); } void loop() { WiFiClient client = server.available(); // Listen for incoming clients if (client) { // If a new client connects, draw_message("Client connected"); String currentLine = ""; // make a String to hold incoming data from the client while (client.connected()) { // loop while the client's connected if (client.available()) { // if there's bytes to read from the client, char c = client.read(); // read a byte, then Serial.write(c); // print it out the serial monitor if (c == '\n') { // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. // that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0) { // Send header client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println("Connection: close"); client.println(); // Display the HTML web page client.println(responseHTML); // The HTTP response ends with another blank line client.println(); break; } else { // if we got a newline, then clear currentLine currentLine = ""; } } else if (c != '\r') { // if we got anything else but a CR character, currentLine += c; // add it to the end of the currentLine } } } // Close the connection client.stop(); showConnectionsCount(); } }

При запуске программы на экране отобразятся имя и IP-адрес точки доступа.

Подключившись к точке доступа со смартфона, вы можете набрать IP в браузере и просмотреть содержимое веб-страницы.

Сервер будет работать без OLED-экрана; в этом случае информацию об отладке можно просмотреть с помощью последовательного монитора в Arduino IDE.

3. Точка доступа Wi-Fi с DNS.

Предыдущий пример можно улучшить, включив поддержку DNS. В этом случае IP вводить не обязательно; вместо этого вы можете использовать полное имя, например www.myesp32.com .



5 экспериментов с WiFi на ESP32

В источнике используется класс Веб сервер , что позволяет значительно сократить код обработки запроса.



#include <WiFi.h> #include <DNSServer.h> #include <WebServer.h> WebServer webServer(80); const char *ssid = "TEST-123"; const char *password = NULL; // "12345678"; IPAddress apIP(192, 168, 1, 4); DNSServer dnsServer; const char *server_name = "www.myesp32.com"; // Can be "*" to all DNS requests String responseHTML = "<!DOCTYPE html><html>" "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}" "</style></head>" "<body><h1>ESP32 Web Server</h1>" "<p>Hello World</p>" "</body></html>"; void setup() { WiFi.mode(WIFI_AP); WiFi.softAP(ssid, password); delay(100); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); const byte DNS_PORT = 53; dnsServer.start(DNS_PORT, server_name, apIP); webServer.onNotFound([]() { webServer.send(200, "text/html", responseHTML); }); webServer.begin(); } void loop() { dnsServer.processNextRequest(); webServer.handleClient(); }



4. Wi-Fi-сниффер

Еще один интересный пример использования WiFi приведен на странице https://github.com/ESP-EOS/ESP32-WiFi-Sniffer .

WiFi на ESP32 можно переключить в так называемый беспорядочный режим, который позволяет незаметно отслеживать пакеты WiFi без подключения к самой сети.

В частности, вы можете увидеть MAC-адреса ближайших устройств:

5 экспериментов с WiFi на ESP32

Это может быть полезно, например, для «умного дома», чтобы узнать, когда хозяин вернулся домой.

Некоторые компании используют MAC-адреса устройств для мониторинг посетителей , чтобы затем показывать им таргетированную рекламу в Google. Исходный код можно скачать со страницы https://github.com/ESP-EOS/ESP32-WiFi-Sniffer .



5. Монитор пакетов Wi-Fi

Другой пример использования беспорядочного режима — графический мониторинг активности канала; как и в предыдущем случае, подключение к самой сети не требуется.



5 экспериментов с WiFi на ESP32

Исходный код был взят Здесь , из него убрали поддержку записи на SD (ее и так нет на плате) и исправили ошибку с графической библиотекой.

Переключить номер канала для мониторинга можно либо нажатием кнопки (на плате ее тоже нет :), либо отправкой соответствующего номера через Serial Monitor в Arduino IDE. Источник

#include <esp_wifi.h> #include <esp_wifi_types.h> #include <esp_system.h> #include <esp_event.h> #include <esp_event_loop.h> #include <nvs_flash.h> #include <stdio.h> #include <string> #include <cstddef> #include <Wire.h> #include <Preferences.h> using namespace std; #define MAX_CH 14 // 1 - 14 #define SNAP_LEN 2324 // max len of each recieved packet #define BUTTON_PIN 5 // button to change the channel #define USE_DISPLAY // comment out if you don't want to use OLED //#define FLIP_DISPLAY // comment out if you don't like to flip it #define MAX_X 128 #define MAX_Y 64 #if CONFIG_FREERTOS_UNICORE #define RUNNING_CORE 0 #else #define RUNNING_CORE 1 #endif #ifdef USE_DISPLAY #include <SSD1306Wire.h> #endif esp_err_t event_handler(void* ctx, system_event_t* event) { return ESP_OK; } // OLED Display 128x64 #ifdef USE_DISPLAY SSD1306Wire display(0x3c, 5, 4); #endif Preferences preferences; bool useSD = false; bool buttonPressed = false; bool buttonEnabled = true; uint32_t lastDrawTime; uint32_t lastButtonTime; uint32_t tmpPacketCounter; uint32_t pkts[MAX_X]; // here the packets per second will be saved uint32_t deauths = 0; // deauth frames per second unsigned int ch = 1; // current 802.11 channel int rssiSum; /* ===== functions ===== */ double getMultiplicator() { uint32_t maxVal = 1; for (int i = 0; i < MAX_X; i++) { if (pkts[i] > maxVal) maxVal = pkts[i]; } if (maxVal > MAX_Y) return (double)MAX_Y / (double)maxVal; else return 1; } void setChannel(int newChannel) { ch = newChannel; if (ch > MAX_CH || ch < 1) ch = 1; preferences.begin("packetmonitor32", false); preferences.putUInt("channel", ch); preferences.end(); esp_wifi_set_promiscuous(false); esp_wifi_set_channel(ch, WIFI_SECOND_CHAN_NONE); esp_wifi_set_promiscuous_rx_cb(&wifi_promiscuous); esp_wifi_set_promiscuous(true); } void wifi_promiscuous(void* buf, wifi_promiscuous_pkt_type_t type) { wifi_promiscuous_pkt_t* pkt = (wifi_promiscuous_pkt_t*)buf; wifi_pkt_rx_ctrl_t ctrl = (wifi_pkt_rx_ctrl_t)pkt->rx_ctrl; if (type == WIFI_PKT_MGMT && (pkt->payload[0] == 0xA0 || pkt->payload[0] == 0xC0 )) deauths++; if (type == WIFI_PKT_MISC) return; // wrong packet type if (ctrl.sig_len > SNAP_LEN) return; // packet too long uint32_t packetLength = ctrl.sig_len; if (type == WIFI_PKT_MGMT) packetLength -= 4; // fix for known bug in the IDF https://github.com/espressif/esp-idf/issues/886 //Serial.print(".

"); tmpPacketCounter++; rssiSum += ctrl.rssi; } void draw() { #ifdef USE_DISPLAY double multiplicator = getMultiplicator(); int len; int rssi; if (pkts[MAX_X - 1] > 0) rssi = rssiSum / (int)pkts[MAX_X - 1]; else rssi = rssiSum; display.clear(); display.setTextAlignment(TEXT_ALIGN_RIGHT); display.drawString( 10, 0, (String)ch); display.drawString( 14, 0, ("|")); display.drawString( 30, 0, (String)rssi); display.drawString( 34, 0, ("|")); display.drawString( 82, 0, (String)tmpPacketCounter); display.drawString( 87, 0, ("[")); display.drawString(106, 0, (String)deauths); display.drawString(110, 0, ("]")); display.drawString(114, 0, ("|")); display.drawString(128, 0, (useSD ? "SD" : "")); display.setTextAlignment(TEXT_ALIGN_LEFT); display.drawString( 36, 0, ("Pkts:")); display.drawLine(0, 63 - MAX_Y, MAX_X, 63 - MAX_Y); for (int i = 0; i < MAX_X; i++) { len = pkts[i] * multiplicator; display.drawLine(i, 63, i, 63 - (len > MAX_Y ? MAX_Y : len)); if (i < MAX_X - 1) pkts[i] = pkts[i + 1]; } display.display(); #endif } void setup() { // Serial Serial.begin(115200); // Settings preferences.begin("packetmonitor32", false); ch = preferences.getUInt("channel", 1); preferences.end(); // System & WiFi nvs_flash_init(); tcpip_adapter_init(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); //ESP_ERROR_CHECK(esp_wifi_set_country(WIFI_COUNTRY_EU)); ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); ESP_ERROR_CHECK(esp_wifi_start()); esp_wifi_set_channel(ch, WIFI_SECOND_CHAN_NONE); // I/O pinMode(BUTTON_PIN, INPUT_PULLUP); // display #ifdef USE_DISPLAY display.init(); #ifdef FLIP_DISPLAY display.flipScreenVertically(); #endif /* show start screen */ display.clear(); display.setFont(ArialMT_Plain_16); display.drawString(6, 6, "PacketMonitor32"); display.setFont(ArialMT_Plain_10); display.drawString(24, 34, "Made with <3 by"); display.drawString(29, 44, "@Spacehuhn"); display.display(); delay(1000); #endif // second core xTaskCreatePinnedToCore( coreTask, /* Function to implement the task */ "coreTask", /* Name of the task */ 2500, /* Stack size in words */ NULL, /* Task input parameter */ 0, /* Priority of the task */ NULL, /* Task handle. */ RUNNING_CORE); /* Core where the task should run */ // start Wifi sniffer esp_wifi_set_promiscuous_rx_cb(&wifi_promiscuous); esp_wifi_set_promiscuous(true); } void loop() { vTaskDelay(portMAX_DELAY); } void coreTask( void * p ) { uint32_t currentTime; while(true) { currentTime = millis(); // check button if (digitalRead(BUTTON_PIN) == LOW) { if (buttonEnabled) { if (!buttonPressed) { buttonPressed = true; lastButtonTime = currentTime; } else if (currentTime - lastButtonTime >= 2000) { draw(); buttonPressed = false; buttonEnabled = false; } } } else { if (buttonPressed) { setChannel(ch + 1); draw(); } buttonPressed = false; buttonEnabled = true; } // draw Display if ( currentTime - lastDrawTime > 1000 ) { lastDrawTime = currentTime; // Serial.printf("\nFree RAM %u %u\n", heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT), heap_caps_get_minimum_free_size(MALLOC_CAP_32BIT));// for debug purposes pkts[MAX_X - 1] = tmpPacketCounter; draw(); Serial.println((String)pkts[MAX_X - 1]); tmpPacketCounter = 0; deauths = 0; rssiSum = 0; } // Serial input if (Serial.available()) { ch = Serial.readString().

toInt(); if (ch < 1 || ch > 14) ch = 1; setChannel(ch); } } }

Одна плата ESP32 может мониторить только 1 канал, но учитывая дешевизну этих плат, это вполне возможно сделать.

так :

5 экспериментов с WiFi на ESP32

Источник: github.com/spacehuhn/WiFiSatellite

Заключение

Как видите, по соотношению возможностей и цены ESP32 довольно интересен и во всяком случае гораздо функциональнее обычного Arduino. «Эксперименты с Wi-Fi тоже весьма интересны; на плате можно держать не только полноценно работающий веб-сервер (даже с поддержкой веб-сокеты ), но и более подробно изучить работу WiFi и MAC. В целом, модули ESP32 интересны, когда возможностей Arduino уже недостаточно, а использование Raspberry Pi с Linux по-прежнему избыточно.

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

ESP32 с камерой

5 экспериментов с WiFi на ESP32

Всем удачных экспериментов.

Теги: #Сетевые технологии #ESP32 #Сделай сам или Сделай сам #программирование #Гаджеты #arduino #Разработка для Arduino #wifi

Вместе с данным постом часто просматривают: