Надёжная беспроводная метеостанция на Arduino: сборка и программирование
Обзор
В этом проекте мы создадим беспроводную метеостанцию на Arduino. Наружный блок измеряет температуру и влажность датчиком DHT22 и передаёт данные по радиоканалу (NRF24L01) внутреннему блоку. Внутренний блок также оснащён DHT22, модулем реального времени DS3231 (сохраняет время при отключении питания) и 0.96″ OLED-дисплеем для вывода всех параметров.

Схемы подключения
Оба модуля (RTC и OLED) общаются по I2C (пины A4, A5 на Arduino Nano). К NRF24L01 добавлен конденсатор для стабилизации питания, к DHT22 — подтягивающий резистор. Питание: внутренний блок — 12 В от адаптера, внешний — две Li-ion батареи (~7.5 В). Благодаря режиму сна (потребление ~7 мА) внешний блок работает до 10 дней.

Печатная плата (PCB)
Для компактности разработана общая плата в EasyEDA — одна для обоих блоков, различается только прошивка. Gerber-файлы отправлены на производство в JLCPCB (качество отличное, 10 шт. за $2 при первом заказе).



Сборка корпуса
Корпуса из 8 мм МДФ: выпилены на cirkularной пиле, сделаны щели для вентиляции (важно для точности DHT22), окно под OLED, декор из алюминия. Сборка на клее и шурупах, покраска: белый — внешний, чёрный — внутренний. Питание: джек и тумблер внутри, джампер снаружи.


Программный код
Внешний блок (передатчик)
/* Outdoor unit - Transmitter
by Dejan Nedelkovski, www.HowToMechatronics.com
Libraries: RF24, DHTlib, LowPower */
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <dht.h>
#include <LowPower.h>
#define dataPin 8
dht DHT;
RF24 radio(10, 9);
const byte address[6] = "00001";
char thChar[32] = "";
String thString = "";
void setup() {
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
}
void loop() {
int readData = DHT.read22(dataPin);
int t = DHT.temperature;
int h = DHT.humidity;
thString = String(t) + String(h);
thString.toCharArray(thChar, 12);
// Отправляем 3 раза для надёжности
for (int i = 0; i <= 3; i++) {
radio.write(&thChar, sizeof(thChar));
delay(50);
}
// Сон 2 минуты (15 × 8 сек)
for (int sleepCounter = 15; sleepCounter > 0; sleepCounter--) {
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
}
}
Суть: читаем DHT22, склеиваем температуру и влажность в строку, отправляем 3 раза подряд, затем уходим в глубокий сон на 2 минуты.
Внутренний блок (приёмник + дисплей + RTC)
/* Indoor unit - Receiver
Libraries: DS3231, U8g2lib */
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <dht.h>
#include <DS3231.h>
#include <U8g2lib.h>
#include <Wire.h>
#define dataPin 8
dht DHT;
DS3231 rtc(SDA, SCL);
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
RF24 radio(10, 9);
const byte address[6] = "00001";
char text[6] = "";
int readDHT22, t, h;
String inTemp, inHum, outTemp, outHum;
String rtcTime, rtcDate;
int draw_state = 0;
unsigned long previousMillis = 0;
long interval = 3000;
// Битмапы иконок температуры и влажности (PROGMEM — во Flash)
#define Temperature_20Icon_width 27
#define Temperature_20Icon_height 47
static const unsigned char Temperature_20Icon_bits[] U8X8_PROGMEM = { ... };
#define Humidity_20Icon_width 27
#define Humidity_20Icon_height 47
static const unsigned char Humidity_20Icon_bits[] U8X8_PROGMEM = { ... };
void setup() {
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();
u8g2.begin();
rtc.begin();
}
void loop() {
// Приём данных от внешнего блока
if (radio.available()) {
radio.read(&text, sizeof(text));
outTemp = String(text[0]) + String(text[1]) + char(176) + "C";
outHum = String(text[2]) + String(text[3]) + "%";
}
// Смена экранов каждые 3 сек через millis()
unsigned long currentMillis = millis();
if (currentMillis - previousMillis > interval) {
previousMillis = currentMillis;
u8g2.firstPage();
do {
switch (draw_state) {
case 0: drawDate(); break;
case 1: drawInTemperature(); break;
case 2: drawInHumidity(); break;
case 3: drawOutTemperature(); break;
case 4: drawOutHumidity(); break;
}
} while (u8g2.nextPage());
draw_state++;
if (draw_state > 4) draw_state = 0;
}
}
// Функции отрисовки экранов (Date, In/Out Temp/Hum) используют u8g2.setFont, setCursor, print, drawXBMP
void drawDate() { ... }
void drawInTemperature() { ... }
void drawInHumidity() { ... }
void drawOutTemperature() { ... }
void drawOutHumidity() { ... }
Ключевые моменты:
- Иконки хранятся во Flash (
PROGMEM) — экономит ОЗУ. - Битмапы создавались в GIMP → экспорт .xbm → копирование в код.
- Использование
millis()вместоdelay()позволяет не пропускать входящие пакеты. - Библиотека U8g2 выводит страницы через
firstPage()/nextPage().
Итог
Получилась автономная метеостанция с точными измерениями, часами и низким потреблением внешнего блока. Все файлы (EasyEDA, код, STL) доступны в исходнике урока.
Производственный процесс
- DIY солнечная метеостанция на Raspberry Pi
- Сборка уличной метеостанции на Raspberry Pi 2 и ADS‑WS1
- Создание метеостанции на Raspberry Pi с ежедневными email-отчётами о погоде
- Продвинутая метеостанция v2.0: мониторинг температуры, давления, влажности и высоты в реальном времени
- Детектор аудиочастот на Arduino: измеряем пик громкости с высокочувствительным модулем
- Умный беспроводной выключатель света: Arduino-контроллер для удаленного управления
- Крестики-нолики на Arduino с матрицей MAX7219 и картонным корпусом
- Сборка надежного таймера обратного отсчета на Arduino с 7-сегментным дисплеем SparkFun
- Портативная метеостанция на Arduino Nano за $10: DHT22, BMP180 и LCD
- eDOT: точные Arduino-часы и метеостанция с встроенным ИК-пультом