⏰ ماژول ساعت DS3231 با آردوینو: ساعت و تاریخی که با قطع برق هم گم نمیشود
آردوینو خودش ساعت ندارد. میتواند بشمارد از وقتی روشن شده چند میلیثانیه گذشته، ولی نمیداند امروز چندم است و ساعت چند. با قطع برق هم همهچیز صفر میشود. ماژول ساعت DS3231 این مشکل را حل میکند: یک ساعت دقیق کوچک با باتری سکهای که حتی وقتی آردوینو خاموش است، جلو میرود. در این آموزش DS3231 با آردوینو را با کتابخانهی RTClib راه میاندازیم، تاریخ و ساعت و دما را میخوانیم و یک ساعت زنگدار میسازیم.
RTC چیست و DS3231 چرا دقیق است؟
RTC یعنی Real-Time Clock، «ساعت زمان واقعی». داخل DS3231 یک کریستال کوارتز هست که مثل آونگ ساعتهای قدیمی با فرکانس ثابت میلرزد و تراشه این لرزشها را میشمارد. کریستالها با گرما و سرما کمی تند و کند میشوند؛ DS3231 یک دماسنج داخلی دارد و این خطا را خودش جبران میکند. نتیجه دقتی حدود یک دقیقه در سال است، خیلی بهتر از DS1307 قدیمی. یک باتری سکهای CR2032 هم پشت ماژول است که سالها ساعت را زنده نگه میدارد.
ارتباط با آردوینو از راه I2C است: فقط دو سیم SDA و SCL، و آدرس ماژول 0x68. یعنی میتوانی LCD I2C را هم روی همان دو سیم بگذاری. اگر میخواهی بدانی چرا نمیشود فقط با millis ساعت ساخت، جواب در همان پاراگراف اول است: millis حافظه ندارد.
پایههای ماژول
| پایه | کار |
|---|---|
| VCC | تغذیهی ۳٫۳ تا ۵ ولت |
| GND | زمین |
| SDA | دادهی I2C |
| SCL | ساعت I2C |
| SQW | خروجی موج مربعی یا وقفهی آلارم (فعلاً لازم نیست) |
| 32K | خروجی ۳۲ کیلوهرتز (فعلاً لازم نیست) |
قطعهها و سیمکشی
- یک آردوینو Uno
- یک ماژول ساعت RTC DS3231
- برای ساعت زنگدار: یک بازر و یک دکمه
| پایهی DS3231 | آردوینو Uno |
|---|---|
| VCC | 5V |
| GND | GND |
| SDA | A4 |
| SCL | A5 |
در آردوینو Uno، پایههای I2C ثابتاند: A4 داده و A5 ساعت. روی بردهای جدیدتر Uno دو پایهی SDA و SCL کنار AREF هم هست که در واقع همان A4 و A5 است.
کد کامل
کتابخانهی RTClib (از Adafruit) را از Library Manager نصب کن. این برنامه هر ثانیه روز هفته، تاریخ، ساعت و دمای داخل ماژول را در سریال مانیتور (۹۶۰۰) چاپ میکند.
// DS3231 real-time clock: weekday, date, time and temperature.
#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;
const char* DAYS[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
int lastSecond = -1;
void print2(int n) { // 7 -> "07"
if (n < 10) Serial.print("0");
Serial.print(n);
}
void setup() {
Serial.begin(9600);
if (!rtc.begin()) {
Serial.println("RTC not found! Check SDA/SCL.");
while (true) delay(1000);
}
if (rtc.lostPower()) {
// First run or empty battery: copy the computer's time at compile time.
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
Serial.println("Time was set.");
}
}
void loop() {
DateTime now = rtc.now();
if (now.second() == lastSecond) return; // print once per second
lastSecond = now.second();
Serial.print(DAYS[now.dayOfTheWeek()]);
Serial.print(" ");
Serial.print(now.year()); Serial.print("/");
print2(now.month()); Serial.print("/");
print2(now.day()); Serial.print(" ");
print2(now.hour()); Serial.print(":");
print2(now.minute()); Serial.print(":");
print2(now.second());
Serial.print(" temp: ");
Serial.print(rtc.getTemperature());
Serial.println(" C");
}
توضیح بخشهای مهم
RTC_DS3231 rtc;شیء ساعت. دقت کن کلاس درست را بنویسی؛RTC_DS1307برای ماژول قدیمیتر است.rtc.begin()اگر ماژول روی باس I2C جواب ندهد،falseبرمیگرداند. بهجای اینکه برنامه با ساعت غلط ادامه دهد، پیام میدهیم و میایستیم.rtc.lostPower()وقتیtrueاست که ساعت از کار افتاده بوده (باتری نبوده یا تازه گذاشته شده). فقط در همین حالت ساعت را تنظیم میکنیم.DateTime(F(__DATE__), F(__TIME__))تاریخ و ساعتِ لحظهی کامپایل را از کامپیوتر برمیدارد. چون آپلود چند ثانیه طول میکشد، ساعت چند ثانیه عقب میافتد که برای بیشتر پروژهها مهم نیست. برای عدد دقیق میتوانی دستی بنویسی:DateTime(2026, 10, 20, 14, 30, 0).rtc.now()زمان فعلی را یکجا میخواند؛ بعد باhour()،minute()و بقیه هر بخش را جدا میگیریم.dayOfTheWeek()از ۰ (یکشنبه) تا ۶ (شنبه) است؛ برای همین آرایهیDAYSبا Sun شروع میشود.getTemperature()دمای داخل تراشه است؛ برای حدودی دانستن دمای اتاق خوب است، ولی دماسنج دقیق نیست.- تابع
print2جلوی عددهای یکرقمی صفر میگذارد تا ساعت به شکل 08:05:09 دیده شود، نه 8:5:9.
پروژهی کوچک: ساعت زنگدار
هر روز ساعت ۷:۳۰ بازر روی D8 بوقبوق میکند و LED روی برد چشمک میزند تا دکمهی روی D2 را بزنی. برای تست، ساعت زنگ را یک دقیقه بعد از الان بگذار.
// Daily alarm: buzzer + LED at ALARM_HOUR:ALARM_MINUTE, button stops it.
#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;
const int BUZZER = 8;
const int LED = 13;
const int BUTTON = 2;
const int ALARM_HOUR = 7;
const int ALARM_MINUTE = 30;
bool ringing = false;
int lastDayRung = -1; // ring only once per day
void setup() {
pinMode(BUZZER, OUTPUT);
pinMode(LED, OUTPUT);
pinMode(BUTTON, INPUT_PULLUP);
Serial.begin(9600);
if (!rtc.begin()) {
Serial.println("RTC not found!");
while (true) delay(1000);
}
if (rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
void loop() {
DateTime now = rtc.now();
if (!ringing && now.hour() == ALARM_HOUR && now.minute() == ALARM_MINUTE
&& now.day() != lastDayRung) {
ringing = true;
lastDayRung = now.day();
Serial.println("Wake up!");
}
if (ringing) {
bool beat = (millis() / 250) % 2 == 0; // beep-beep pattern
digitalWrite(LED, beat ? HIGH : LOW);
if (beat) tone(BUZZER, 1000);
else noTone(BUZZER);
if (digitalRead(BUTTON) == LOW) { // the button stops the alarm
ringing = false;
noTone(BUZZER);
digitalWrite(LED, LOW);
Serial.println("Alarm off");
}
}
}
متغیر lastDayRung نمیگذارد بعد از خاموش کردن، در همان دقیقه دوباره زنگ بزند. اگر میخواهی ساعت روی نمایشگر هم دیده شود، کد را با سونسگمنت TM1637 یا LCD I2C ترکیب کن؛ LCD I2C و DS3231 هر دو روی همان A4 و A5 مینشینند.
اجرا در شبیهساز
در تینکرکد فارسی هر دو مدار آمادهاند: ساعت واقعی DS3231 و ساعت زنگدار. اگر درسمحور جلو میروی، درس ساعت واقعی با RTC و بعد درس ساعت زنگدار را ببین. باز کردن و خواندن رایگان است؛ ماژول RTC جزو ماژولهای حرفهای است و اجرایش اشتراک Plus میخواهد. در شبیهساز، lostPower() بار اول true است، درست مثل ماژولی که تازه باتری گرفته.
اشکالهای رایج
- پیام RTC not found: SDA و SCL جابهجا یا شلاند. SDA به A4 و SCL به A5.
- تاریخ به شکل 2165/165/165 چاپ میشود: ماژول جواب نمیدهد و همهی بیتها یک خوانده شدهاند؛ باز هم سیمکشی و تغذیه.
- هر بار که برد را ریست میکنی، ساعت به زمان کامپایل برمیگردد:
adjustرا بیرون از شرطlostPowerگذاشتهای. - بعد از قطع برق ساعت صفر میشود: باتری نیست یا تمام شده. روی بعضی ماژولهای ارزان (مدل ZS-042) یک مدار شارژ هست که برای باتری غیرقابلشارژ CR2032 مناسب نیست؛ یا از باتری قابلشارژ LIR2032 استفاده کن، یا از یک بزرگتر بخواه مقاومت شارژ را بردارد.
- از کلاس
RTC_DS1307برای DS3231 استفاده کردهای: بعضی چیزها کار میکند ولیlostPowerو دما نه.
برای دیدن مقدارها به شکل نمودار، سریال مانیتور و پلاتر را بخوان. همهی کتابخانههای پشتیبانیشده در صفحهی کتابخانهها و بقیهی پروژهها در صفحهی آردوینو هستند.
ساعت واقعی DS3231 را باز کن ⏰سؤالهای رایج
چرا با millis ساعت نسازم؟
millis با قطع برق صفر میشود و در طول روز چند ثانیه خطا جمع میکند. DS3231 باتری دارد، با خاموش شدن آردوینو هم جلو میرود و دقتش حدود یک دقیقه در سال است.
ساعت را فقط یک بار تنظیم کنم یا هر بار؟
فقط یک بار. در کد، adjust را داخل شرط lostPower بگذار تا فقط وقتی باتری خالی یا تازه گذاشته شده، ساعت دوباره تنظیم شود.
ماژول RTC در شبیهساز کار میکند؟
بله، با همان کتابخانهی RTClib. خواندن مثالها و درسها رایگان است؛ اجرای ماژول RTC جزو ماژولهای حرفهای است و اشتراک Plus میخواهد.