
Собираем многофункциональную метеостанцию на Arduino с датчиками BMP280, DHT22 и фоторезистором. OLED-дисплей, переключение режимов кнопкой и полный код с объяснениями.
Профессиональные метеостанции стоят десятки тысяч рублей и содержат сложное оборудование. Однако четыре ключевых параметра окружающей среды -- температуру, влажность, атмосферное давление и уровень освещённости -- можно измерять недорогими датчиками, подключёнными к Arduino. Получается полноценная станция мониторинга окружающей среды, полезная и дома, и в школьной лаборатории.
В этом проекте мы соберём метеостанцию с тремя датчиками и OLED-дисплеем. DHT22 измеряет температуру и влажность воздуха, BMP280 -- атмосферное давление, а фоторезистор оценивает уровень освещённости. Кнопка переключает режимы отображения на экране: все данные сразу, только температура крупным шрифтом или график давления. Показания обновляются каждые 5 секунд. Проект рассчитан на учеников 7-10 классов.
Чему вы научитесь:
| Компонент | Количество | Назначение |
|---|---|---|
| Arduino Uno | 1 | Управляющий контроллер |
| Датчик BMP280 | 1 | Измерение давления и температуры |
| Датчик DHT22 | 1 | Измерение влажности и температуры |
| Фоторезистор (LDR) | 1 | Измерение освещённости |
| Резистор 10 кОм | 2 | Делитель для фоторезистора + подтяжка DHT22 |
| OLED-дисплей 0.96" 128x64 (I2C) | 1 | Отображение показаний |
| Тактовая кнопка | 1 | Переключение режимов |
| Макетная плата | 1 | Сборка схемы |
| Провода-перемычки | 12-15 шт. | Соединение компонентов |
| USB-кабель | 1 | Питание и загрузка программы |

Оба устройства работают по протоколу I2C и подключаются к одним и тем же проводам, но имеют разные адреса. Arduino различает их по адресу при инициализации.
Общие линии I2C:
Чем больше света падает на фоторезистор, тем ниже его сопротивление и тем выше напряжение на пине A0.
Мы используем встроенный подтягивающий резистор Arduino (INPUT_PULLUP), поэтому внешний резистор не нужен.

Установите библиотеки через менеджер библиотек Arduino IDE: Adafruit BMP280, Adafruit SSD1306, Adafruit GFX и DHT sensor library.
```cpp
// Метеостанция на Arduino
// Датчики: BMP280 + DHT22 + фоторезистор
// Дисплей: OLED 128x64
// Кнопка переключения режимов
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_BMP280.h>
#include <DHT.h>
// Настройки OLED-дисплея
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Настройки датчиков
#define DHT_PIN 2
#define DHT_TYPE DHT22
#define LDR_PIN A0 // Фоторезистор на аналоговом входе
#define BUTTON_PIN 3 // Кнопка переключения режимов
DHT dht(DHT_PIN, DHT_TYPE);
Adafruit_BMP280 bmp;
// Интервал обновления показаний (мс)
#define UPDATE_INTERVAL 5000
unsigned long lastUpdate = 0;
// Текущий режим отображения (0, 1, 2)
int displayMode = 0;
bool lastButtonState = HIGH;
// Переменные для хранения показаний
float temperature = 0;
float humidity = 0;
float pressureMmHg = 0;
int lightLevel = 0;
void setup() {
Serial.begin(9600);
// Кнопка с внутренней подтяжкой
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Инициализация OLED-дисплея
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("Ошибка: OLED не найден!");
while (true); // Останавливаемся при ошибке
}
// Заставка
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(15, 20);
display.println("METEOSTANTSIYA");
display.setCursor(25, 40);
display.println("Zapusk...");
display.display();
// Инициализация BMP280
if (!bmp.begin(0x76)) {
Serial.println("Ошибка: BMP280 не найден!");
Serial.println("Попробуйте адрес 0x77");
}
// Настройка BMP280 для метеонаблюдений
bmp.setSampling(
Adafruit_BMP280::MODE_NORMAL,
Adafruit_BMP280::SAMPLING_X2, // Температура: x2
Adafruit_BMP280::SAMPLING_X16, // Давление: x16 (максимальная точность)
Adafruit_BMP280::FILTER_X16, // Сглаживание: x16
Adafruit_BMP280::STANDBY_MS_500
);
// Инициализация DHT22
dht.begin();
delay(2000); // Даём датчикам прогреться
Serial.println("Метеостанция запущена!");
}
void loop() {
// Обработка нажатия кнопки
bool buttonState = digitalRead(BUTTON_PIN);
if (buttonState == LOW && lastButtonState == HIGH) {
// Кнопка нажата -- переключаем режим
displayMode = (displayMode + 1) % 3;
Serial.print("Режим: ");
Serial.println(displayMode);
delay(200); // Защита от дребезга
}
lastButtonState = buttonState;
// Обновляем показания каждые 5 секунд
unsigned long currentTime = millis();
if (currentTime - lastUpdate >= UPDATE_INTERVAL) {
lastUpdate = currentTime;
readSensors();
printToSerial();
}
// Обновляем дисплей при каждой итерации
// (для быстрой реакции на смену режима)
updateDisplay();
}
// Считывание данных со всех датчиков
void readSensors() {
// DHT22: влажность и температура
float h = dht.readHumidity();
float tDHT = dht.readTemperature();
// BMP280: давление и температура
float pressure = bmp.readPressure() / 100.0; // Паскали в гПа
float tBMP = bmp.readTemperature();
// Усредняем температуру с двух датчиков
if (isnan(tDHT)) {
temperature = tBMP;
} else {
temperature = (tDHT + tBMP) / 2.0;
}
// Влажность
humidity = isnan(h) ? 0 : h;
// Давление в мм ртутного столба
pressureMmHg = pressure * 0.750062;
// Освещённость (0-1023, пересчитываем в проценты)
lightLevel = map(analogRead(LDR_PIN), 0, 1023, 0, 100);
}
// Вывод данных в Serial Monitor
void printToSerial() {
Serial.print("Темп: ");
Serial.print(temperature, 1);
Serial.print("C | Влажн: ");
Serial.print(humidity, 1);
Serial.print("% | Давл: ");
Serial.print(pressureMmHg, 1);
Serial.print(" mmHg | Свет: ");
Serial.print(lightLevel);
Serial.println("%");
}
// Обновление дисплея в зависимости от текущего режима
void updateDisplay() {
display.clearDisplay();
switch (displayMode) {
case 0:
drawAllData(); // Все параметры на одном экране
break;
case 1:
drawBigTemp(); // Температура крупным шрифтом
break;
case 2:
drawLightMeter(); // Индикатор освещённости
break;
}
display.display();
}
// Режим 0: все данные компактно
void drawAllData() {
// Заголовок
display.setTextSize(1);
display.setCursor(10, 0);
display.println("= METEOSTANTSIYA =");
display.drawLine(0, 10, 127, 10, SSD1306_WHITE);
// Температура
display.setCursor(0, 14);
display.print("Temp: ");
display.print(temperature, 1);
display.print(" ");
display.print((char)247);
display.println("C");
// Влажность
display.setCursor(0, 26);
display.print("Vlazh: ");
display.print(humidity, 1);
display.println(" %");
// Давление
display.setCursor(0, 38);
display.print("Davl: ");
display.print(pressureMmHg, 0);
display.println(" mmHg");
// Освещённость
display.setCursor(0, 50);
display.print("Svet: ");
display.print(lightLevel);
display.println(" %");
}
// Режим 1: температура крупно
void drawBigTemp() {
display.setTextSize(1);
display.setCursor(30, 0);
display.println("TEMPERATURA");
display.drawLine(0, 10, 127, 10, SSD1306_WHITE);
// Крупная температура по центру
display.setTextSize(3);
display.setCursor(10, 22);
display.print(temperature, 1);
display.setTextSize(2);
display.print((char)247);
display.println("C");
// Мелким шрифтом -- влажность
display.setTextSize(1);
display.setCursor(20, 55);
display.print("Vlazhnost: ");
display.print(humidity, 0);
display.print("%");
}
// Режим 2: графический индикатор освещённости
void drawLightMeter() {
display.setTextSize(1);
display.setCursor(20, 0);
display.println("OSVESCHENNOST");
display.drawLine(0, 10, 127, 10, SSD1306_WHITE);
// Числовое значение
display.setTextSize(2);
display.setCursor(35, 16);
display.print(lightLevel);
display.println(" %");
// Графическая полоса-индикатор
display.drawRect(10, 40, 108, 16, SSD1306_WHITE);
int barWidth = map(lightLevel, 0, 100, 0, 104);
display.fillRect(12, 42, barWidth, 12, SSD1306_WHITE);
}
`
Шина I2C и адресация. BMP280 и OLED подключены к одной паре проводов (A4/A5), но имеют разные адреса: дисплей откликается на 0x3C, а датчик давления -- на 0x76. Библиотеки автоматически адресуют запросы нужному устройству.
Два источника температуры. И BMP280, и DHT22 измеряют температуру. Мы усредняем их показания для повышения точности. Если один из датчиков недоступен, программа использует данные второго.
Фоторезистор и делитель напряжения. Фоторезистор образует делитель напряжения с постоянным резистором 10 кОм. Функция analogRead() возвращает значение 0-1023, которое мы пересчитываем в проценты через map(). Чем ярче свет, тем выше процент.
Единицы давления. BMP280 возвращает давление в паскалях. Мы делим на 100 для получения гектопаскалей, а затем умножаем на 0.750062 для перевода в миллиметры ртутного столба -- привычную единицу для повседневного использования.
Три режима отображения. Кнопка на пине D3 переключает режимы по кругу. Режим 0 показывает все параметры на одном экране. Режим 1 -- температуру крупным шрифтом с влажностью мелко внизу. Режим 2 -- графический индикатор освещённости с полосой-прогрессом.
Неблокирующие таймеры. Вместо delay(5000) мы используем millis() и проверку интервала. Это позволяет кнопке реагировать мгновенно, даже между обновлениями датчиков.
1. Калибровка освещённости. Значения фоторезистора зависят от конкретного экземпляра. Попросите учеников измерить значения при полной темноте и ярком свете, а затем подобрать диапазон в функции map().
2. Научный эксперимент. Подключите SD-карту и записывайте показания каждые 5 минут в течение суток. На следующем уроке постройте графики в Excel и обсудите зависимости: как меняется температура от освещённости? Как давление связано с погодой?
3. Прогноз погоды. Объясните ученикам, что быстрое падение давления (более 1 гПа за час) обычно предвещает ухудшение погоды. Предложите добавить простой алгоритм прогноза.
4. Расширение. Замените Arduino на ESP32, добавьте WiFi и публикуйте данные на платформу ThingSpeak для удалённого мониторинга.
Alashed Hardware предлагает наборы с датчиками BMP280, DHT22, OLED-дисплеями и фоторезисторами -- всё необходимое для сборки метеостанции в одной коробке. В Alashed CodeStudio учитель может подготовить шаблон проекта с пустыми функциями и раздать его ученикам, чтобы каждый самостоятельно реализовал считывание датчиков и отображение на экране, развивая навыки программирования поэтапно.
Подключите школу к пилоту. Генерируйте КМЖ за 2 минуты, ведите CodeStudio уроки, заказывайте оборудование — всё в одном месте.