亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

打造可靠的 Arduino 无线气象站

概述

本教程将学习如何制作一个基于 Arduino 的无线气象站。你可以观看下方视频或阅读以下图文教程。

室外温湿度由 DHT22 传感器测量,通过 NRF24L01 收发模块无线发送至室内单元。室内单元同样配备 DHT22 测量室内温湿度,并搭载 DS3231 实时时钟模块(掉电也能走时)。所有数据显示在 0.96 英寸 OLED 屏幕上。

打造可靠的 Arduino 无线气象站

Arduino 无线气象站电路图

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

打造可靠的 Arduino 无线气象站

所需组件链接:

RTC 模块和 OLED 均通过 I2C 协议与 Arduino 通信,接在 Nano 的 A4、A5 引脚。NRF24L01 旁并联电容稳定供电,DHT22 数据脚接上拉电阻保证正常工作。

供电方面:室内单元用 12V 直流适配器;室外单元用两节锂电池(约 7.5V),因周期性发射并让 Arduino 进入睡眠模式(功耗仅约 7mA),可运行约 10 天。

定制 PCB 设计

为整理元件,根据电路图用 EasyEDA 免费在线软件设计了定制 PCB。同一块 PCB 可用于室内外单元,仅需烧录不同固件。

打造可靠的 Arduino 无线气象站

设计完成后导出 Gerber 文件用于制板。可查看本项目的 EasyEDA 工程文件。

随后在 JLCPCB(本视频赞助商)下单。上传 Gerber 文件后可在查看器中检查,确认无误后选择参数下单,首单最高可获 10 块 PCB 仅需 $2。

打造可靠的 Arduino 无线气象站
打造可靠的 Arduino 无线气象站

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

打造可靠的 Arduino 无线气象站

组装电子元件

先在 PCB 焊排针,方便模块插拔。

打造可靠的 Arduino 无线气象站

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

打造可靠的 Arduino 无线气象站

制作外壳

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

打造可靠的 Arduino 无线气象站
打造可靠的 Arduino 无线气象站

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

打造可靠的 Arduino 无线气象站

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

打造可靠的 Arduino 无线气象站
打造可靠的 Arduino 无线气象站

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

打造可靠的 Arduino 无线气象站

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 项目并学到了新知识。欢迎在评论区提问。

制造工艺

  1. 树莓派太阳能气象站DIY指南
  2. 树莓派 2 搭建户外气象站:接入 ADS‑WS1 串口气象站
  3. 树莓派自制气象站:每日邮件推送温湿雨数据
  4. 高级气象站 v2.0:BME280 实时监测温度/气压/湿度/海拔 (Windows 10 IoT Core)
  5. Arduino 音频频率检测:基于 FFT 测量最响亮声音峰值
  6. 智能无线开关控制器:Arduino + 433MHz 远程操控墙壁开关
  7. Arduino 井字棋游戏:MAX7219 点阵屏 + 纸板外壳 DIY
  8. 使用 SparkFun 7 段数码管构建可靠的 Arduino 倒计时器
  9. 自制 10 美元便携 Arduino 气象站 - Nano + DHT22 + BMP180 + LCD
  10. eDOT:基于 Arduino 的精密时钟与气象站,内置红外遥控
  11. Arduino + ThingSpeak 实时气象监测站
  12. 自制 Arduino 遥控保时捷赛车:从改装底盘到手机 App 控制全攻略