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

Arduino 气象时钟 – 实时显示日期、时间、温度与湿度

组件与物料

Arduino UNO ×1
DHT11 温湿度传感器(4针) ×1
Maxim Integrated DS3231 RTC 模块 ×1
10kΩ 电阻 ×1
Adafruit 128×64 OLED 图形显示屏 ×1
面包板 ×1
杜邦线 ×15

项目简介

想自制一个能同时显示日期、时间、温度和湿度的设备吗?其实比你想象的简单得多。只需几个关键组件即可实现。

Arduino Uno / Nano

首先需要一块微控制器板。本项目使用 Arduino Uno,Arduino Nano 也有足够的引脚可用。

DHT11 传感器

DHT11 是一款高性价比的数字温湿度传感器,利用电容式湿度传感元件和热敏电阻测量环境空气。读取间隔建议不低于 2 秒。

重要提示:设置好时间后,务必将设置时间的代码行注释掉并重新编译上传,否则 RTC 每次上电都会重置为编译时的时间。

DS3231 RTC 模块

DS3231 是一款高精度实时时钟模块,内置温度补偿晶振(TCXO)和晶体,比常见的 DS1307 精准得多。自带电池备份,主电源断开后仍能准确走时。

128×64 OLED 图形显示屏

用于显示所有数据。屏幕紧凑、亮度高,支持 SPI 或 I2C 接口(背部可切换)。也可使用其他兼容 Adafruit 库的同尺寸 OLED 屏。

代码

文件:Time_Temp_Humidity_Display.ino

// Arduino 气象时钟 - 日期、时间、温湿度显示
///////////////////////// OLED 部分 /////////////////////////
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// 软件 SPI 引脚定义
#define OLED_MOSI 9
#define OLED_CLK 10
#define OLED_DC 11
#define OLED_CS 12
#define OLED_RESET 13
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

#if (SSD1306_LCDHEIGHT != 64)
#error("高度不正确,请修复 Adafruit_SSD1306.h!");
#endif

//////////////////////////// DHT11 部分 ////////////////////////////
#include <dht.h>
dht DHT;
#define DHT11_PIN 3
int DHTtimer = 0;

//////////////////////////// DS3231 RTC 部分 ////////////////////////////
// SDA 接 A4,SCL 接 A5
#define DS3231_I2C_ADDRESS 0x68

// 十进制转 BCD
byte decToBcd(byte val) {
  return ((val / 10 * 16) + (val % 10));
}

// BCD 转十进制
byte bcdToDec(byte val) {
  return ((val / 16 * 10) + (val % 16));
}

void setup() {
  Wire.begin();
  Serial.begin(9600);
  
  // 首次设置时间,设置后请注释掉下面这行并重新上传
  // setDS3231time(0, 24, 12, 6, 3, 2, 17); // 秒、分、时、周、日、月、年
  
  display.begin(SSD1306_SWITCHCAPVCC);
  display.clearDisplay();
}

// 设置 DS3231 时间
void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year) {
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0);
  Wire.write(decToBcd(second));
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(hour));
  Wire.write(decToBcd(dayOfWeek));
  Wire.write(decToBcd(dayOfMonth));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}

// 读取 DS3231 时间
void readDS3231time(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) {
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}

// 显示时间、日期、星期、月份及模拟时钟
void displayTime() {
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  
  // 串口输出
  Serial.print(hour, DEC); Serial.print(":");
  if (minute < 10) Serial.print("0");
  Serial.print(minute, DEC); Serial.print(":");
  if (second < 10) Serial.print("0");
  Serial.print(second, DEC); Serial.print(" ");
  Serial.print(dayOfMonth, DEC); Serial.print("/");
  Serial.print(month, DEC); Serial.print("/");
  Serial.print(year, DEC); Serial.print(" 星期: ");
  
  // OLED 显示日期、年份
  display.setCursor(50, 0);
  display.print(dayOfMonth, DEC); display.print(",");
  display.setCursor(91, 0);
  display.print("20"); display.print(year, DEC);
  
  // 显示时分秒
  if (hour < 10) { display.setCursor(40, 10); display.print("0"); display.print(hour, DEC); display.print(":"); }
  else { display.setCursor(40, 10); display.print(hour, DEC); display.print(":"); }
  if (minute < 10) { display.setCursor(58, 10); display.print("0"); display.print(minute, DEC); display.print(":"); }
  else { display.setCursor(58, 10); display.print(minute, DEC); display.print(":"); }
  if (second < 10) { display.setCursor(75, 10); display.print("0"); display.print(second, DEC); }
  else { display.setCursor(75, 10); display.print(second, DEC); }
  
  // 星期显示
  switch (dayOfWeek) {
    case 1: Serial.println("Sunday"); display.setCursor(20, 0); display.print("Sun"); break;
    case 2: Serial.println("Monday"); display.setCursor(20, 0); display.print("Mon"); break;
    case 3: Serial.println("Tuesday"); display.setCursor(20, 0); display.print("Tue"); break;
    case 4: Serial.println("Wednesday"); display.setCursor(20, 0); display.print("Wed"); break;
    case 5: Serial.println("Thursday"); display.setCursor(20, 0); display.print("Thu"); break;
    case 6: Serial.println("Friday"); display.setCursor(20, 0); display.print("Fri"); break;
    case 7: Serial.println("Saturday"); display.setCursor(20, 0); display.print("Sat"); break;
  }
  
  // 月份显示
  switch (month) {
    case 1: display.setCursor(70, 0); display.print("Jan"); break;
    case 2: display.setCursor(70, 0); display.print("Feb"); break;
    case 3: display.setCursor(70, 0); display.print("Mar"); break;
    case 4: display.setCursor(70, 0); display.print("Apr"); break;
    case 5: display.setCursor(70, 0); display.print("May"); break;
    case 6: display.setCursor(70, 0); display.print("Jun"); break;
    case 7: display.setCursor(70, 0); display.print("Jul"); break;
    case 8: display.setCursor(70, 0); display.print("Aug"); break;
    case 9: display.setCursor(70, 0); display.print("Sep"); break;
    case 10: display.setCursor(70, 0); display.print("Oct"); break;
    case 11: display.setCursor(70, 0); display.print("Nov"); break;
    case 12: display.setCursor(70, 0); display.print("Dec"); break;
  }
  
  // 绘制模拟时钟指针(时、分、秒)
  float x1, y1, a, b;
  const float pi = 3.14;
  // 时针
  a = ((hour - 15) * 30);
  b = (a * pi) / 180;
  x1 = 40 + (9 * cos(b));
  y1 = 41 + (9 * sin(b));
  display.drawLine(40, 41, x1, y1, WHITE);
  // 分针
  a = ((minute - 15) * 6);
  b = (a * pi) / 180;
  x1 = 40 + (17 * cos(b));
  y1 = 41 + (17 * sin(b));
  display.drawLine(40, 41, x1, y1, WHITE);
  // 秒针
  a = ((second - 15) * 6);
  b = (a * pi) / 180;
  x1 = 40 + (19 * cos(b));
  y1 = 41 + (19 * sin(b));
  display.drawLine(40, 41, x1, y1, WHITE);
  
  // 绘制静止的表盘刻度
  display.drawCircle(40, 41, 22, WHITE);
  display.drawCircle(40, 41, 1, WHITE);
  display.drawLine(40, 20, 40, 25, WHITE); // 12
  display.drawLine(40, 63, 40, 58, WHITE); // 6
  display.drawLine(62, 41, 57, 41, WHITE); // 3
  display.drawLine(19, 41, 24, 41, WHITE); // 9
  // 其余刻度省略...
}

// 读取温湿度并在 OLED 显示
void ReadTempAndHum() {
  int chk = DHT.read11(DHT11_PIN);
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(70, 45);
  display.print("Temp: "); display.print(DHT.temperature, 0); display.print("C");
  display.setCursor(70, 56);
  display.print("Hum: "); display.print(DHT.humidity, 0); display.print("%");
  Serial.print(" Humidity: "); Serial.print(DHT.humidity, 1); Serial.print("%");
  Serial.print(" | Temperature: "); Serial.print(DHT.temperature, 1); Serial.println("C");
}

void loop() {
  displayTime();
  DHTtimer++;
  if (DHTtimer > 1) { // 每 2 秒读取一次 DHT11
    display.fillRect(70, 45, 128, 64, BLACK);
    ReadTempAndHum();
    DHTtimer = 0;
  }
  delay(1000);
  display.display();
  // 清除时间日期区域,准备下一帧
  display.fillRect(0, 0, 64, 64, BLACK);
  display.fillRect(0, 0, 128, 20, BLACK);
}

原理图与 PCB

原理图文件:breadboard_diagram_QvhPKCpIo5.fzz、schematic_XKU0jQoWbx.fzz
注意:Fritzing 中未找到完全匹配的 OLED 模块,OLED 接线请参考代码中的引脚定义注释。

所需库文件

制造工艺

  1. 树莓派温湿度网络监测站DIY全指南
  2. 树莓派与 Python 构建远程温度传感器:分步实战指南
  3. 树莓派配合 EzTemp 实现无缝温湿度监测
  4. 树莓派构建网络化实验室气象站:温湿度气压实时监测
  5. 树莓派多传感器温度光照监测系统:DS18B20 实战构建
  6. 验证并校准湿度传感器以实现精准测量
  7. 电容器充放电实验:动手探究 RC 电路时域特性
  8. C++日期时间完全指南:ctime库、tm结构体与格式化输出
  9. Python 获取当前日期和时间:实用指南
  10. Python 日期时间处理实战:time 与 calendar 模块完全指南
  11. 掌握 Java 日期时间:构造器、方法与最佳实践
  12. 确保新疫苗与生物制剂及时生产