ESP8266 — Wi-Fi связь клиент-сервер между двумя платами (NodeMCU)

Узнайте, как установить Wi-Fi связь (HTTP) между двумя платами ESP8266 NodeMCU для обмена данными без необходимости подключения к интернету (роутер не нужен).

Вы настроите одну ESP8266 как точку доступа (сервер), а вторую ESP8266 — как станцию (клиент). Затем сервер и клиент будут обмениваться данными (показаниями датчиков) через HTTP-запросы. Мы будем программировать платы ESP8266 с помощью Arduino IDE.

ESP8266 — Wi-Fi связь клиент-сервер между двумя платами

В этом примере мы будем отправлять показания датчика BME280 с одной платы на другую. Приёмник будет отображать показания на OLED-дисплее.

Если у вас есть плата ESP32, вы можете прочитать это специализированное руководство: ESP32 Client-Server Wi-Fi Communication.

Посмотрите видеодемонстрацию

Чтобы увидеть, как работает проект, вы можете посмотреть следующую видеодемонстрацию:

Обзор проекта

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

ESP8266 — обзор проекта клиент-сервер
  • ESP8266-сервер создаёт собственную беспроводную сеть (ESP8266 Soft-Access Point). Таким образом, другие Wi-Fi устройства могут подключаться к этой сети (SSID: ESP8266-Access-Point, Пароль: 123456789).

  • ESP8266-клиент настроен как станция. Поэтому он может подключаться к беспроводной сети ESP8266-сервера.

  • Клиент может выполнять HTTP GET-запросы к серверу для запроса данных датчиков или любой другой информации. Для этого нужно только использовать IP-адрес сервера и сделать запрос по определённому маршруту: /temperature, /humidity или /pressure.

  • Сервер прослушивает входящие запросы и отправляет соответствующий ответ с показаниями.

  • Клиент получает показания и отображает их на OLED-дисплее.

В качестве примера, ESP8266-клиент запрашивает температуру, влажность и давление у сервера, выполняя запросы по IP-адресу сервера с добавлением /temperature, /humidity и /pressure, соответственно (HTTP GET).

ESP8266-сервер прослушивает эти маршруты, и когда поступает запрос, он отправляет соответствующие показания датчика через HTTP-ответ.

Необходимые компоненты

Компоненты для проекта ESP8266 клиент-сервер

Для этого руководства вам понадобятся следующие компоненты:

Вы можете использовать приведённые выше ссылки или перейти напрямую на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!

Установка библиотек

Для этого руководства вам необходимо установить следующие библиотеки:

Библиотеки асинхронного веб-сервера

Мы будем использовать следующие библиотеки для обработки HTTP-запросов:

Вы можете установить эти библиотеки через менеджер библиотек Arduino. Перейдите в Sketch > Include Library > Manage Libraries и найдите библиотеки по имени.

Вам также может быть интересно: DHT11/DHT22 Asynchronous Web Server с ESP8266

Библиотеки BME280

Следующие библиотеки можно установить через менеджер библиотек Arduino. Перейдите в Sketch > Include Library > Manage Libraries и найдите библиотеку по имени.

Вам также может быть интересно: Руководство по BME280 с ESP8266

Библиотеки I2C SSD1306 OLED

Для работы с OLED-дисплеем вам понадобятся следующие библиотеки. Их можно установить через менеджер библиотек Arduino. Перейдите в Sketch > Include Library > Manage Libraries и найдите библиотеку по имени.

Вам также может быть интересно: Полное руководство по SSD1306 OLED-дисплею с ESP8266

#1 ESP8266-сервер (точка доступа)

ESP8266-сервер с датчиком BME280 (температура, влажность, давление)

ESP8266-сервер — это точка доступа (AP), которая прослушивает запросы по URL-адресам /temperature, /humidity и /pressure. Когда он получает запросы на эти URL, он отправляет последние показания датчика BME280.

Для тестирования мы используем датчик BME280, но вы можете использовать любой другой датчик, изменив несколько строк кода (например: DHT11/DHT22 или DS18B20).

Схема подключения

Подключите ESP8266 к датчику BME280, как показано на следующей схеме.

Схема подключения ESP8266 BME280

BME280

ESP8266

VIN/VCC

3.3V

GND

GND

SCL

GPIO 5 (D1)

SDA

GPIO 4 (D2)

Скетч Arduino для #1 ESP8266-сервера

Загрузите следующий код на вашу плату.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp8266-nodemcu-client-server-wi-fi/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/

// Import required libraries
#include <ESP8266WiFi.h>
#include "ESPAsyncWebServer.h"

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

// Set your access point network credentials
const char* ssid = "ESP8266-Access-Point";
const char* password = "123456789";

/*#include <SPI.h>
#define BME_SCK 18
#define BME_MISO 19
#define BME_MOSI 23
#define BME_CS 5*/

Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

String readTemp() {
  return String(bme.readTemperature());
  //return String(1.8 * bme.readTemperature() + 32);
}

String readHumi() {
  return String(bme.readHumidity());
}

String readPres() {
  return String(bme.readPressure() / 100.0F);
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);
  Serial.println();

  // Setting the ESP as an access point
  Serial.print("Setting AP (Access Point)…");
  // Remove the password parameter, if you want the AP (Access Point) to be open
  WiFi.softAP(ssid, password);

  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);

  server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", readTemp().c_str());
  });
  server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", readHumi().c_str());
  });
  server.on("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", readPres().c_str());
  });

  bool status;

  // default settings
  // (you can also pass in a Wire library object like &Wire2)
  status = bme.begin(0x76);
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  // Start server
  server.begin();
}

void loop(){

}

Просмотреть исходный код

Как работает код

Начните с подключения необходимых библиотек. Подключите библиотеку ESP8266WiFi.h и библиотеку ESPAsyncWebServer.h для обработки входящих HTTP-запросов.

#include <ESP8266WiFi.h>
#include "ESPAsyncWebServer.h"

Подключите следующие библиотеки для работы с датчиком BME280.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

В следующих переменных определите учётные данные вашей сети точки доступа:

const char* ssid = "ESP8266-Access-Point";
const char* password = "123456789";

Мы устанавливаем SSID как ESP8266-Access-Point, но вы можете дать ему любое другое имя. Вы также можете изменить пароль. По умолчанию установлен 123456789.

Создайте экземпляр для датчика BME280 с именем bme.

Adafruit_BME280 bme;

Создайте асинхронный веб-сервер на порту 80.

AsyncWebServer server(80);

Затем создайте три функции, которые возвращают температуру, влажность и давление в виде переменных типа String.

String readTemp() {
  return String(bme.readTemperature());
  //return String(1.8 * bme.readTemperature() + 32);
}

String readHumi() {
  return String(bme.readHumidity());
}

String readPres() {
  return String(bme.readPressure() / 100.0F);
}

В функции setup() инициализируйте Serial Monitor для демонстрационных целей.

Serial.begin(115200);

Настройте ESP8266 как точку доступа с SSID и паролем, определёнными ранее.

WiFi.softAP(ssid, password);

Затем настройте маршруты, по которым ESP8266 будет прослушивать входящие запросы.

Например, когда ESP8266-сервер получает запрос по URL /temperature, он отправляет температуру, возвращённую функцией readTemp(), как char (поэтому мы используем метод c_str()).

server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readTemp().c_str());
});

То же самое происходит, когда ESP получает запрос по URL /humidity и /pressure.

server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readHumi().c_str());
});
server.on("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readPres().c_str());
});

Следующие строки инициализируют датчик BME280.

bool status;

// default settings
// (you can also pass in a Wire library object like &Wire2)
status = bme.begin(0x76);
if (!status) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
}

Наконец, запустите сервер.

server.begin();

Поскольку это асинхронный веб-сервер, в loop() ничего нет.

void loop(){

}

Тестирование ESP8266-сервера

Загрузите код на вашу плату и откройте Serial Monitor. Вы должны увидеть примерно следующее:

Тестирование ESP8266-сервера — Serial Monitor Arduino IDE

Это означает, что точка доступа была успешно настроена.

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

На вашем смартфоне перейдите в настройки Wi-Fi и подключитесь к ESP8266-Access-Point. Пароль — 123456789.

Подключение к точке доступа ESP8266

Находясь подключённым к точке доступа, откройте браузер и введите 192.168.4.1/temperature

Вы должны увидеть значение температуры в вашем браузере:

Тестирование запроса температуры к ESP8266-серверу

Попробуйте этот URL-путь для влажности 192.168.4.1/humidity:

Тестирование запроса влажности к ESP8266-серверу

Наконец, перейдите по URL 192.168.4.1/pressure:

Тестирование запроса давления к ESP8266-серверу

Если вы получаете корректные показания, значит всё работает правильно. Теперь вам нужно подготовить вторую плату ESP8266 (клиент), чтобы она автоматически выполняла эти запросы и отображала результаты на OLED-дисплее.

#2 ESP8266-клиент (станция)

ESP8266-клиент — получение показаний по HTTP GET запросу BME280

ESP8266-клиент — это Wi-Fi станция, которая подключается к ESP8266-серверу. Клиент запрашивает температуру, влажность и давление у сервера, выполняя HTTP GET-запросы по URL-маршрутам /temperature, /humidity и /pressure. Затем он отображает показания на OLED-дисплее.

Схема подключения

Подключите OLED-дисплей к вашей плате ESP8266, как показано на следующей схеме.

Схема подключения ESP8266 OLED-дисплей

Pin

ESP8266

Vin

3.3V

GND

GND

SCL

GPIO 5 (D1)

SDA

GPIO 4 (D2)

Скетч Arduino для #2 ESP8266-клиента

Загрузите следующий код на вторую ESP8266 (клиент):

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp8266-client-server-wi-fi/

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>

#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti WiFiMulti;

const char* ssid = "ESP8266-Access-Point";
const char* password = "123456789";

//Your IP address or domain name with URL path
const char* serverNameTemp = "http://192.168.4.1/temperature";
const char* serverNameHumi = "http://192.168.4.1/humidity";
const char* serverNamePres = "http://192.168.4.1/pressure";

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

String temperature;
String humidity;
String pressure;

unsigned long previousMillis = 0;
const long interval = 5000;

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

  // Address 0x3C for 128x64, you might need to change this value (use an I2C scanner)
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  display.clearDisplay();
  display.setTextColor(WHITE);

  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("Connected to WiFi");
}

void loop() {
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis >= interval) {
     // Check WiFi connection status
    if ((WiFiMulti.run() == WL_CONNECTED)) {
      temperature = httpGETRequest(serverNameTemp);
      humidity = httpGETRequest(serverNameHumi);
      pressure = httpGETRequest(serverNamePres);
      Serial.println("Temperature: " + temperature + " *C - Humidity: " + humidity + " % - Pressure: " + pressure + " hPa");

      display.clearDisplay();

      // display temperature
      display.setTextSize(2);
      display.setCursor(0,0);
      display.print("T: ");
      display.print(temperature);
      display.print(" ");
      display.setTextSize(1);
      display.cp437(true);
      display.write(248);
      display.setTextSize(2);
      display.print("C");

      // display humidity
      display.setTextSize(2);
      display.setCursor(0, 25);
      display.print("H: ");
      display.print(humidity);
      display.print(" %");

      // display pressure
      display.setTextSize(2);
      display.setCursor(0, 50);
      display.print("P:");
      display.print(pressure);
      display.setTextSize(1);
      display.setCursor(110, 56);
      display.print("hPa");

      display.display();

      // save the last HTTP GET Request
      previousMillis = currentMillis;
    }
    else {
      Serial.println("WiFi Disconnected");
    }
  }
}

String httpGETRequest(const char* serverName) {
  WiFiClient client;
  HTTPClient http;

  // Your IP address with path or Domain name with URL path
  http.begin(client, serverName);

  // Send HTTP POST request
  int httpResponseCode = http.GET();

  String payload = "--";

  if (httpResponseCode>0) {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
    payload = http.getString();
  }
  else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();

  return payload;
}

Просмотреть исходный код

Как работает код

Подключите необходимые библиотеки для Wi-Fi соединения и выполнения HTTP-запросов:

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>

#include <ESP8266WiFiMulti.h>

Вам нужно создать экземпляр WiFiMulti.

ESP8266WiFiMulti WiFiMulti;

Введите учётные данные сети ESP8266-сервера. Если вы изменили учётные данные по умолчанию на ESP8266-сервере, вам нужно изменить их здесь, чтобы они совпадали.

const char* ssid = "ESP8266-Access-Point";
const char* password = "123456789";

Затем сохраните URL-адреса, по которым клиент будет выполнять HTTP-запросы. ESP8266-сервер имеет IP-адрес 192.168.4.1, и мы будем выполнять запросы по URL /temperature, /humidity и /pressure.

const char* serverNameTemp = "http://192.168.4.1/temperature";
const char* serverNameHumi = "http://192.168.4.1/humidity";
const char* serverNamePres = "http://192.168.4.1/pressure";

Подключите библиотеки для работы с OLED-дисплеем:

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Установите размер OLED-дисплея:

#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels

Создайте объект display с размерами, определёнными ранее, и с протоколом связи I2C.

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

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

String temperature;
String humidity;
String pressure;

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

const long interval = 5000;

В функции setup() инициализируйте OLED-дисплей:

// Address 0x3C for 128x64, you might need to change this value (use an I2C scanner)
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
  Serial.println(F("SSD1306 allocation failed"));
  for(;;); // Don't proceed, loop forever
}
display.clearDisplay();
display.setTextColor(WHITE);

Примечание: если ваш OLED-дисплей не работает, проверьте его I2C-адрес с помощью скетча I2C-сканера и измените код соответственно.

Подключите ESP8266-клиент к сети ESP8266-сервера.

while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
}
Serial.println("");
Serial.println("Connected to WiFi");

В функции loop() выполняются HTTP GET-запросы. Мы создали функцию httpGETRequest(), которая принимает в качестве аргумента URL-путь, по которому мы хотим сделать запрос, и возвращает ответ в виде String.

Вы можете использовать следующую функцию в своих проектах для упрощения кода:

String httpGETRequest(const char* serverName) {
  WiFiClient client;
  HTTPClient http;

  // Your IP address with path or Domain name with URL path
  http.begin(client, serverName);

  // Send HTTP POST request
  int httpResponseCode = http.GET();

  String payload = "--";

  if (httpResponseCode>0) {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
    payload = http.getString();
  }
  else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();

  return payload;
}

Мы используем эту функцию для получения показаний температуры, влажности и давления от сервера.

temperature = httpGETRequest(serverNameTemp);
humidity = httpGETRequest(serverNameHumi);
pressure = httpGETRequest(serverNamePres);

Выведите эти показания в Serial Monitor для отладки.

Serial.println("Temperature: " + temperature + " *C - Humidity: " + humidity + " % - Pressure: " + pressure + " hPa");

Затем отобразите температуру на OLED-дисплее:

display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.print("T: ");
display.print(temperature);
display.print(" ");
display.setTextSize(1);
display.cp437(true);
display.write(248);
display.setTextSize(2);
display.print("C");

Влажность:

display.setTextSize(2);
display.setCursor(0, 25);
display.print("H: ");
display.print(humidity);
display.print(" %");

Наконец, показание давления:

display.setTextSize(2);
display.setCursor(0, 50);
display.print("P:");
display.print(pressure);
display.setTextSize(1);
display.setCursor(110, 56);
display.print("hPa");

display.display();

Мы используем таймеры вместо задержек для выполнения запроса каждые x секунд. Поэтому у нас есть переменные previousMillis, currentMillis и функция millis(). У нас есть статья, которая показывает разницу между таймерами и задержками, которая может быть вам полезна (или прочитайте ESP8266 Timers).

Загрузите скетч на #2 ESP8266 (клиент), чтобы проверить, всё ли работает правильно.

Тестирование ESP8266-клиента

Имея обе платы достаточно близко друг к другу и подключёнными к питанию, вы увидите, что ESP #2 получает новые показания температуры, влажности и давления каждые 5 секунд от ESP #1.

Вот что вы должны увидеть в Serial Monitor ESP8266-клиента.

Тестирование ESP8266-клиента — Serial Monitor Arduino IDE

Показания датчика также отображаются на OLED-дисплее.

ESP8266 клиент-сервер — пример обмена данными показаний датчиков

Вот и всё! Ваши две платы ESP8266 общаются друг с другом.

Заключение

В этом руководстве мы показали вам, как отправлять данные с одной платы ESP8266 на другую с помощью HTTP-запросов. Этот проект может быть очень полезен, если вам нужно настроить беспроводную связь между двумя или более платами, а роутера поблизости нет.

В демонстрационных целях мы показали, как отправлять показания датчика BME280, но вы можете использовать любой другой датчик или отправлять любые другие данные. Другие рекомендуемые датчики:

У нас есть аналогичное руководство для ESP32, которое может быть вам полезно:

Надеемся, вам понравилось это руководство. Мы готовим ещё больше подобных руководств. Следите за обновлениями и подпишитесь на наш блог!

Спасибо за чтение.