打造可靠的 Arduino 无线气象站
概述
本教程将学习如何制作一个基于 Arduino 的无线气象站。你可以观看下方视频或阅读以下图文教程。
室外温湿度由 DHT22 传感器测量,通过 NRF24L01 收发模块无线发送至室内单元。室内单元同样配备 DHT22 测量室内温湿度,并搭载 DS3231 实时时钟模块(掉电也能走时)。所有数据显示在 0.96 英寸 OLED 屏幕上。

Arduino 无线气象站电路图
来看电路图及工作原理。各模块详细教程请参考:NRF24L01 教程、DHT22 教程、DS3231 教程。

所需组件链接:
- NRF24L01 收发模块
- DHT22 传感器
- DS3231 实时时钟
- Arduino Nano
RTC 模块和 OLED 均通过 I2C 协议与 Arduino 通信,接在 Nano 的 A4、A5 引脚。NRF24L01 旁并联电容稳定供电,DHT22 数据脚接上拉电阻保证正常工作。
供电方面:室内单元用 12V 直流适配器;室外单元用两节锂电池(约 7.5V),因周期性发射并让 Arduino 进入睡眠模式(功耗仅约 7mA),可运行约 10 天。
定制 PCB 设计
为整理元件,根据电路图用 EasyEDA 免费在线软件设计了定制 PCB。同一块 PCB 可用于室内外单元,仅需烧录不同固件。

设计完成后导出 Gerber 文件用于制板。可查看本项目的 EasyEDA 工程文件。
随后在 JLCPCB(本视频赞助商)下单。上传 Gerber 文件后可在查看器中检查,确认无误后选择参数下单,首单最高可获 10 块 PCB 仅需 $2。


几天后 PCB 到货,质量极佳,与设计完全一致。

组装电子元件
先在 PCB 焊排针,方便模块插拔。

焊上电容和上拉电阻后,即可将模块插到排针上。

制作外壳
用 8mm MDF 板配合圆锯切割面板。为保证测量准确,侧板需开槽通风,钻孔后用锉刀修整。


面板开 OLED 显示窗,并裁剪铝片装饰。

用木工胶和螺丝组装,喷漆(室外白、室内黑),装入 PCB。


室内背板装电源插座和开关,室外用跳线做开关。硬件完成,接下来讲解程序。

Arduino 无线气象站代码
室外单元(发射端)代码
/* Arduino 无线通信教程 室外单元 - 发射端 by Dejan Nedelkovski, www.HowToMechatronics.com
Libraries: NRF24L01 - TMRh20/RF24, https://github.com/tmrh20/RF24/
DHT22 - DHTlib, https://github.com/RobTillaart/Arduino/tree/master/libraries/DHTlib
LowPower - https://github.com/rocketscream/Low-Power */
#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);
for (int i = 0; i <= 3; i++) {
radio.write(&thChar, sizeof(thChar));
delay(50);
}
// 睡眠 2 分钟:15 * 8s = 120s
for (int sleepCounter = 15; sleepCounter > 0; sleepCounter--) {
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
}
}
说明:室外单元为发射端,包含 RF24、DHT、LowPower 库。setup 初始化无线地址。loop 读取温湿度,合并为字符串转字符数组,连发 3 次确保接收。最后用 LowPower 库让 Arduino 睡眠 2 分钟降低功耗。
室内单元(接收端)代码
/* Arduino 无线通信教程 室内单元 - 接收端 by Dejan Nedelkovski, www.HowToMechatronics.com
Libraries: DS3231 - https://www.rinkydinkelectronics.com/library.php?id=73
U8G2 - https://github.com/olikraus/u8g2 */
#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, /* reset=*/ 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;
// 温度图标位图
#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]) + "%";
}
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;
}
}
void drawDate() {
String dowa = rtc.getDOWStr(); dowa.remove(3);
rtcDate = dowa + " " + rtc.getDateStr();
u8g2.setFont(u8g2_font_timB14_tr);
u8g2.setCursor(0, 15);
rtcTime = rtc.getTimeStr(); rtcTime.remove(5);
u8g2.print(rtcDate);
u8g2.setFont(u8g2_font_fub30_tf);
u8g2.setCursor(8, 58);
u8g2.print(rtcTime);
}
void drawInTemperature() {
readDHT22 = DHT.read22(dataPin);
t = DHT.temperature;
inTemp = String(t) + char(176) + "C";
u8g2.setFont(u8g2_font_helvR14_tr);
u8g2.setCursor(24, 15); u8g2.print("INDOOR");
u8g2.setFont(u8g2_font_fub30_tf);
u8g2.setCursor(36, 58); u8g2.print(inTemp);
u8g2.drawXBMP(0, 17, Temperature_20Icon_width, Temperature_20Icon_height, Temperature_20Icon_bits);
}
void drawInHumidity() {
h = DHT.humidity;
inHum = String(h) + "%";
u8g2.setFont(u8g2_font_helvR14_tr);
u8g2.setCursor(24, 15); u8g2.print("INDOOR");
u8g2.setFont(u8g2_font_fub30_tf);
u8g2.setCursor(36, 58); u8g2.print(inHum);
u8g2.drawXBMP(0, 17, Humidity_20Icon_width, Humidity_20Icon_height, Humidity_20Icon_bits);
}
void drawOutTemperature() {
u8g2.setFont(u8g2_font_helvR14_tr);
u8g2.setCursor(12, 15); u8g2.print("OUTDOOR");
u8g2.setFont(u8g2_font_fub30_tf);
u8g2.setCursor(36, 58); u8g2.print(outTemp);
u8g2.drawXBMP(0, 17, Temperature_20Icon_width, Temperature_20Icon_height, Temperature_20Icon_bits);
}
void drawOutHumidity() {
u8g2.setFont(u8g2_font_helvR14_tr);
u8g2.setCursor(12, 15); u8g2.print("OUTDOOR");
u8g2.setFont(u8g2_font_fub30_tf);
u8g2.setCursor(36, 58); u8g2.print(outHum);
u8g2.drawXBMP(0, 17, Humidity_20Icon_width, Humidity_20Icon_height, Humidity_20Icon_bits);
}
说明:室内单元为接收端,额外包含 DS3231 和 U8G2 库。定义温湿度图标位图(可用 GIMP 绘制导出 .xbm,再用记事本复制到代码)。用 PROGMEM 存入 Flash 节省 SRAM。setup 初始化无线、OLED、RTC。loop 轮询无线数据,解析前两字符为温度、后两字符为湿度。用 millis() 每 3 秒切换显示 5 个页面(日期/时间、室内温度、室内湿度、室外温度、室外湿度),避免 delay() 阻塞导致漏包。各页面由自定义函数绘制。
希望你喜欢这个 Arduino 项目并学到了新知识。欢迎在评论区提问。
制造工艺
- 树莓派太阳能气象站DIY指南
- 树莓派 2 搭建户外气象站:接入 ADS‑WS1 串口气象站
- 树莓派自制气象站:每日邮件推送温湿雨数据
- 高级气象站 v2.0:BME280 实时监测温度/气压/湿度/海拔 (Windows 10 IoT Core)
- Arduino 音频频率检测:基于 FFT 测量最响亮声音峰值
- 智能无线开关控制器:Arduino + 433MHz 远程操控墙壁开关
- Arduino 井字棋游戏:MAX7219 点阵屏 + 纸板外壳 DIY
- 使用 SparkFun 7 段数码管构建可靠的 Arduino 倒计时器
- 自制 10 美元便携 Arduino 气象站 - Nano + DHT22 + BMP180 + LCD
- eDOT:基于 Arduino 的精密时钟与气象站,内置红外遥控
- Arduino + ThingSpeak 实时气象监测站
- 自制 Arduino 遥控保时捷赛车:从改装底盘到手机 App 控制全攻略