Перейти к основному содержимому

Комнатный Wi-Fi-монитор на ESP32

В этом проекте ESP32 измеряет температуру, влажность и давление датчиком BME280. Плата подключается к домашней Wi-Fi-сети и показывает данные на локальной веб-странице. С той же страницы можно включить светодиод.

Работающий веб-сервер ESP32 с показаниями BME280

Фото реального результата: educ8s, ESP32 Web Server with BME280 Sensor.

Схема исходной опубликованной сборки ESP32, BME280 и светодиода

Схема автора исходного проекта. Таблица ниже повторяет те же сигнальные выводы GPIO27, GPIO26 и GPIO32 и является основной проверкой монтажа.

Результат

После включения ESP32 печатает в монитор порта адрес, например:

Wi-Fi connected
Open: http://192.168.1.54/

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

Устройство работает только внутри локальной сети. Настраивать переадресацию портов на маршрутизаторе не нужно.

Детали

ДетальКоличествоТочная конфигурация
ESP32 DevKit V11модуль ESP-WROOM-32, USB-разъём и стабилизатор на плате
BME2801четырёхвыводный I2C-модуль, рассчитанный на 3,3 В
Красный светодиод 5 мм1обычный индикаторный
Резистор 220 Ом1последовательно со светодиодом
Макетная плата и провода1 наборсоединения
USB-кабель с передачей данных1питание и прошивка
Wi-Fi 2,4 ГГц1 сетьимя и пароль должны быть известны

ESP32 не подключается к сети только 5 ГГц. Маршрутизатор может одновременно раздавать 2,4 и 5 ГГц — это нормально.

Безопасность

  • Питайте датчик BME280 от 3V3, а не от 5V.
  • Никогда не подключайте светодиод без последовательного резистора.
  • Не публикуйте скетч с настоящим паролем Wi-Fi.
  • Веб-сервер не имеет авторизации. Не выводите его в Интернет и не подключайте вместо светодиода опасную нагрузку.
  • При любых изменениях монтажа отключайте USB.

Схема соединений

ОткудаКуда
ESP32 3V3BME280 VCC или VIN
ESP32 GNDBME280 GND
ESP32 GPIO27BME280 SDA или SDI
ESP32 GPIO26BME280 SCL или SCK
ESP32 GPIO32резистор 220 Ом
второй вывод резисторадлинная ножка светодиода, анод
короткая ножка светодиода, катодESP32 GND

У разных плат порядок выводов BME280 отличается. Читайте подписи на самой плате. Убедитесь, что это BME280: похожий BMP280 не измеряет влажность.

Сборка

  1. Установите ESP32 поперёк центрального разрыва макетной платы.
  2. Подключите BME280 четырьмя проводами.
  3. Установите светодиод так, чтобы его ножки находились в разных рядах.
  4. Соедините GPIO32 с анодом только через резистор 220 Ом.
  5. Проверьте отсутствие перемычки между 3V3 и GND.
  6. Подключите USB и не трогайте схему во время работы.

Подготовка Arduino IDE

  1. Откройте Файл → Настройки.
  2. Добавьте в поле адресов менеджера плат официальный адрес:
https://espressif.github.io/arduino-esp32/package_esp32_index.json
  1. В менеджере плат установите пакет esp32 by Espressif Systems.
  2. Выберите ESP32 Dev Module и правильный последовательный порт.
  3. В менеджере библиотек установите Adafruit BME280 Library и Adafruit Unified Sensor.

Программа

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

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

const char *WIFI_NAME = "YOUR_WIFI_NAME";
const char *WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";

const byte SDA_PIN = 27;
const byte SCL_PIN = 26;
const byte LED_PIN = 32;

WebServer server(80);
Adafruit_BME280 bme;

float temperature = 0;
float humidity = 0;
float pressure = 0;
unsigned long lastMeasurementAt = 0;
const unsigned long MEASUREMENT_PERIOD_MS = 2000;

const char PAGE[] PROGMEM = R"HTML(
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Комнатный монитор ESP32</title>
<style>
body { font: 18px sans-serif; max-width: 620px; margin: 2rem auto;
padding: 0 1rem; background: #eef3f7; color: #17202a; }
main { background: white; padding: 1.5rem; border-radius: 16px; }
.value { font-size: 1.4rem; margin: .8rem 0; }
button { font-size: 1rem; padding: .7rem 1rem; margin-right: .5rem; }
</style>
</head>
<body>
<main>
<h1>Комнатный монитор</h1>
<div class="value">Температура: <span id="t">—</span> °C</div>
<div class="value">Влажность: <span id="h">—</span> %</div>
<div class="value">Давление: <span id="p">—</span> гПа</div>
<div class="value">Светодиод: <span id="led">—</span></div>
<button onclick="setLed(1)">Включить</button>
<button onclick="setLed(0)">Выключить</button>
</main>
<script>
async function update() {
const response = await fetch('/api');
const data = await response.json();
document.getElementById('t').textContent = data.temperature.toFixed(1);
document.getElementById('h').textContent = data.humidity.toFixed(1);
document.getElementById('p').textContent = data.pressure.toFixed(1);
document.getElementById('led').textContent = data.led ? 'включён' : 'выключен';
}
async function setLed(state) {
await fetch('/led?state=' + state);
update();
}
update();
setInterval(update, 2000);
</script>
</body>
</html>
)HTML";

void readSensor() {
temperature = bme.readTemperature();
humidity = bme.readHumidity();
pressure = bme.readPressure() / 100.0F;
}

void handleRoot() {
server.send_P(200, "text/html; charset=utf-8", PAGE);
}

void handleApi() {
String json = "{";
json += "\"temperature\":" + String(temperature, 2);
json += ",\"humidity\":" + String(humidity, 2);
json += ",\"pressure\":" + String(pressure, 2);
json += ",\"led\":";
json += digitalRead(LED_PIN) == HIGH ? "true" : "false";
json += "}";
server.send(200, "application/json", json);
}

void handleLed() {
if (!server.hasArg("state")) {
server.send(400, "text/plain", "state is required");
return;
}
digitalWrite(LED_PIN, server.arg("state") == "1" ? HIGH : LOW);
server.send(204);
}

void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);

Wire.begin(SDA_PIN, SCL_PIN);
bool found = bme.begin(0x76);
if (!found) found = bme.begin(0x77);
if (!found) {
Serial.println("BME280 not found. Check wiring.");
while (true) delay(100);
}
readSensor();

WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_NAME, WIFI_PASSWORD);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}

Serial.println();
Serial.println("Wi-Fi connected");
Serial.print("Open: http://");
Serial.print(WiFi.localIP());
Serial.println("/");

server.on("/", handleRoot);
server.on("/api", handleApi);
server.on("/led", handleLed);
server.onNotFound([]() {
server.send(404, "text/plain", "Not found");
});
server.begin();
}

void loop() {
server.handleClient();

unsigned long now = millis();
if (now - lastMeasurementAt >= MEASUREMENT_PERIOD_MS) {
lastMeasurementAt = now;
readSensor();
}
}

Первый запуск

  1. Загрузите программу. Если IDE долго показывает Connecting..., нажмите и удерживайте кнопку BOOT на ESP32 до начала записи.
  2. Откройте монитор порта на 115200 бод и нажмите EN или RESET.
  3. Дождитесь строки с IP-адресом.
  4. Подключите телефон к той же Wi-Fi-сети и откройте адрес целиком, включая точки.
  5. Сравните значения на странице и в комнате.
  6. Нажмите кнопки включения и выключения — внешний светодиод должен изменить состояние.
  7. Закройте страницу, снова откройте её и убедитесь, что сервер продолжает работать.

Проверка API

Откройте адрес вида:

http://192.168.1.54/api

Вместо примера подставьте адрес своей платы. Ответ выглядит так:

{"temperature":23.41,"humidity":45.20,"pressure":1008.62,"led":false}

Именно этот адрес каждые две секунды запрашивает веб-страница.

Диагностика

СимптомЧто проверить
BME280 not foundпитание 3,3 В, GPIO27/GPIO26, адрес 0x76/0x77, тип датчика
Бесконечные точки Connectingимя и пароль, наличие сети 2,4 ГГц, расстояние до маршрутизатора
IP есть, страница не открываетсятелефон в той же локальной сети, мобильный VPN отключён, адрес введён без ошибок
Страница есть, данные не меняютсяоткройте /api и монитор порта, проверьте соединение BME280
Светодиод не горитполярность светодиода, резистор и провод GPIO32
Плата постоянно перезапускаетсядругой USB-кабель или блок питания; проверьте отсутствие короткого замыкания

Практика

  1. Откройте /api с двух устройств одновременно и сравните ответы.
  2. Измените период обновления страницы с 2000 на 5000 мс и наблюдайте сетевые запросы в инструментах разработчика браузера.
  3. Переместите датчик дальше от ESP32 и сравните температуру через 15 минут.

Проверьте себя

  • Почему BME280 питается от 3,3 В?
  • Чем локальный IP-адрес отличается от адреса сайта в Интернете?
  • Для чего нужен путь /api?
  • Где в скетче хранится пароль Wi-Fi и почему его нельзя публиковать?
  • Что делает server.handleClient?

Словарь статьи

  • ESP32 — микроконтроллер со встроенными Wi-Fi и Bluetooth.
  • Локальная сеть — устройства, соединённые через один домашний или школьный маршрутизатор.
  • IP-адрес — сетевой адрес устройства.
  • HTTP-сервер — программа, принимающая запросы браузера.
  • API — здесь отдельный адрес, возвращающий данные в JSON.
  • JSON — текстовый формат с именами и значениями.

Связанные темы

Источники