#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RTClib.h>
#include <DHT.h>
// 定義 OLED 顯示
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// RTC 模組
RTC_DS3231 rtc;
// DHT22 溫濕度感測器
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// 按鈕設定
#define BUTTON_PIN 12
bool is12HourFormat = false; // 默認為 24 小時制
bool lastButtonState = HIGH; // 上次按鈕狀態
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50; // 消抖延遲
const int ledCount = 10; // the number of LEDs in the bar graph
int ledPins[] = {
32, 33, 25, 26, 27, 14, 12, 13, 15, 2
}; // an array of pin numbers to which LEDs are attached
void setup() {
Serial.begin(115200);
// 初始化 OLED 顯示
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("SSD1306 初始化失敗");
while (true);
}
display.clearDisplay();
// 初始化 RTC 模組
if (!rtc.begin()) {
Serial.println("RTC 初始化失敗");
while (true);
}
// 初始化 DHT22 感測器
dht.begin();
// 設置按鈕引腳
pinMode(BUTTON_PIN, INPUT_PULLUP);
// RTC 測試
if (rtc.lostPower()) {
Serial.println("RTC 未設置時間,設置當前時間...");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // 設置為編譯時的時間
}
for (int thisLed = 0; thisLed < ledCount; thisLed++)
{
pinMode(ledPins [thisLed], OUTPUT);
digitalWrite(ledPins [thisLed], HIGH);
}
}
void loop() {
// 按鈕處理
handleButton();
// 更新顯示
display.clearDisplay();
// 獲取當前時間
DateTime now = rtc.now();
// 獲取溫濕度數據
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
// 如果讀取失敗,顯示錯誤訊息
if (isnan(temperature) || isnan(humidity)) {
Serial.println("無法讀取 DHT22 感測數據");
display.setCursor(0, 0);
display.println("DHT22 Error");
display.display();
delay(2000);
return;
}
// **左上角顯示年月日**
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.print(now.year());
display.print("/");
display.print(now.month());
display.print("/");
display.println(now.day());
// **左上角第二行顯示數字時鐘**
display.setTextSize(1); // 標準字體大小
display.setCursor(0, 10); // 左上角第二行
int hour = now.hour();
if (is12HourFormat) {
hour = hour % 12;
if (hour == 0) hour = 12; // 12 小時制的 0 點為 12
}
if (hour < 10) display.print("0"); // 小時補零
display.print(hour);
display.print(":");
if (now.minute() < 10) display.print("0"); // 分鐘補零
display.print(now.minute());
display.print(":");
if (now.second() < 10) display.print("0"); // 秒數補零
display.print(now.second());
if (is12HourFormat) {
display.print(hour >= 12 ? " PM" : " AM");
}
// **右上角顯示溫濕度**
display.setCursor(70, 0); // 右上角
display.print(temperature);
display.println("C");
display.setCursor(70, 10); // 溫濕度的下一行
display.print(humidity);
display.println("%");
// **左下角顯示圓形時鐘**
drawClock(20, 45, 15, now);
// **右下角顯示天氣圖案**
drawWeatherIcon(temperature, humidity);
// 更新顯示
display.display();
// 每 1 秒更新一次
delay(1000);
}
void handleButton() {
int buttonState = digitalRead(BUTTON_PIN);
if (buttonState != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (buttonState == LOW && lastButtonState == HIGH) {
is12HourFormat = !is12HourFormat; // 切換 12/24 小時制
}
}
lastButtonState = buttonState;
}
void drawWeatherIcon(float temperature, float humidity) {
// 清空右下角範圍
display.fillRect(90, 40, 38, 24, BLACK);
// 判斷條件並繪製相應的天氣圖案
if (humidity >= 40 && humidity <= 60 && temperature >= 20 && temperature <= 35) {
// 太陽圖案
display.drawCircle(110, 52, 8, WHITE); // 太陽圓心
for (int i = 0; i < 360; i += 45) { // 太陽光線
int x1 = 110 + 8 * cos(i * PI / 180);
int y1 = 52 + 8 * sin(i * PI / 180);
int x2 = 110 + 12 * cos(i * PI / 180);
int y2 = 52 + 12 * sin(i * PI / 180);
display.drawLine(x1, y1, x2, y2, WHITE);
}
for (int thisLed = 0; thisLed<ledCount ; thisLed++)
{ // Change <= to <
digitalWrite(ledPins [thisLed], LOW);
delay(500);
}
} else if (humidity >= 60 && humidity <= 80 && temperature >= 15 && temperature <= 25) {
// 多雲圖案
display.fillCircle(105, 50, 8, WHITE); // 雲1
display.fillCircle(115, 50, 10, WHITE); // 雲2
display.fillCircle(110, 56, 6, WHITE); // 雲底部
for (int thisLed = 0; thisLed<6 ; thisLed++)
{ // Change <= to <
digitalWrite(ledPins [thisLed], LOW);
delay(500);
}
} else if (humidity >= 80 && humidity <= 100 && temperature >= 10 && temperature <= 25) {
// 雨天圖案
display.fillCircle(110, 48, 10, WHITE); // 雲
display.drawLine(105, 58, 105, 62, WHITE); // 雨滴1
display.drawLine(110, 58, 110, 62, WHITE); // 雨滴2
display.drawLine(115, 58, 115, 62, WHITE); // 雨滴3
for (int thisLed = 0; thisLed<3 ; thisLed++)
{ // Change <= to <
digitalWrite(ledPins [thisLed], LOW);
delay(500);
}
} else {
// 未符合條件,顯示一個問號
display.setCursor(95, 50);
display.setTextSize(1);
display.setTextColor(WHITE);
display.print("?");
}
}
void drawClock(int centerX, int centerY, int radius, DateTime now) {
// 繪製圓形
display.drawCircle(centerX, centerY, radius, WHITE);
// 計算時、分、秒的指針角度
float hourAngle = (now.hour() % 12) * 30 + now.minute() * 0.5; // 時針角度
float minuteAngle = now.minute() * 6; // 分針角度
float secondAngle = now.second() * 6; // 秒針角度
// 計算指針的終點
int hourX = centerX + (radius - 8) * cos((hourAngle - 90) * PI / 180);
int hourY = centerY + (radius - 8) * sin((hourAngle - 90) * PI / 180);
int minuteX = centerX + (radius - 4) * cos((minuteAngle - 90) * PI / 180);
int minuteY = centerY + (radius - 4) * sin((minuteAngle - 90) * PI / 180);
int secondX = centerX + radius * cos((secondAngle - 90) * PI / 180);
int secondY = centerY + radius * sin((secondAngle - 90) * PI / 180);
// 繪製指針
display.drawLine(centerX, centerY, hourX, hourY, WHITE); // 時針
display.drawLine(centerX, centerY, minuteX, minuteY, WHITE); // 分針
display.drawLine(centerX, centerY, secondX, secondY, WHITE); // 秒針
}