Blog Archive

Arduino Indonesia. Gambar tema oleh Storman. Diberdayakan oleh Blogger.

Supported by Electronics 3 in 1

1. Jasa pencetakan PCB single layer dengan harga paling murah.

(Metode Pembuatan dengan Transfer Toner)
>PCB design sendiri (siap cetak) : Rp.150,-/Cm2
>PCB design dari kami : Rp.250,-/Cm2

(Metode Sablon Full Masking dan Silk Screen minimal pemesanan 100 Pcs)
>PCB design sendiri (siap cetak) : Rp.200,-/Cm2
>PCB design dari kami : Rp.250,-/Cm2

2. Jasa perancangan, perakitan, dan pembuatan trainer pembelajaran elektronika untuk SMK dan Mahasiswa.

3. Jasa perancangan, perakitan, dan pembuatan berbagai macam kontroller, sensor, aktuator, dan tranduser.
>Design Rangkaian / Sistem Elektronika
>Design Rangkaian / Sistem Instrumentasi
>Design Rangkaian / Sistem Kendali
>Kerjasama Riset (data atau peralatan)
>Kerjasama Produksi Produk-Produk KIT Elektronika
>Produksi Instrumentasi Elektronika

4. Jasa Pembuatan Proyek, Tugas Akhir, Tugas Laboratorium, PKM, Karya Ilmiah, SKRIPSI, dll

Like My Facebook

Popular Posts

Kamis, 11 Desember 2025

Langkah Mudah Menggunakan DS3231 Real Time Clock di ESP32/ESP8266 dengan MicroPython

Modul RTC DS3231 adalah modul pencatat waktu yang sangat akurat, dilengkapi fitur untuk mengatur alarm, menghasilkan sinyal gelombang kotak (square wave) dengan berbagai frekuensi, serta membaca suhu internal. Dalam tutorial ini, Anda akan mempelajari cara mengatur dan mengambil waktu, mengonfigurasi alarm, serta membaca data suhu dari modul tersebut.

Pengenalan Modul Real-Time Clock (RTC)

Modul RTC seperti DS3231 dan DS1307 memiliki rangkaian jam internal yang berfungsi menjaga waktu secara mandiri. Umumnya, modul ini dilengkapi dudukan baterai (backup battery) agar tetap dapat mempertahankan pencatatan waktu meskipun board ESP32/ESP8266 mengalami reset atau kehilangan daya.



DS3231 dan DS1307 adalah dua modul RTC yang paling umum digunakan pada berbagai aplikasi mikrokontroler. Keduanya kompatibel dengan ESP32 dan ESP8266 serta berkomunikasi menggunakan protokol I2C. DS3231 menawarkan akurasi yang lebih tinggi karena dilengkapi kompensasi suhu (temperature-compensated), dan juga mendukung konfigurasi alarm eksternal yang sangat berguna untuk berbagai aplikasi otomatisasi.

Pengenalan Modul RTC DS3231

Gambar berikut menunjukkan modul RTC DS3231. Modul ini menggunakan kristal osilator 32 kHz dengan kompensasi suhu (TCXO) untuk menjaga waktu dengan tingkat presisi tinggi, bahkan ketika terjadi perubahan suhu lingkungan. Selain fungsi pencatat waktu, DS3231 juga menyediakan pembacaan suhu internal.



Selain mampu menjaga tanggal dan waktu dengan akurasi tinggi, modul ini juga memiliki memori internal untuk menyimpan hingga dua alarm, serta dapat menghasilkan sinyal gelombang kotak (square wave) pada beberapa frekuensi: 1 Hz, 4 kHz, 8 kHz, dan 32 kHz.

 

RTC berkomunikasi menggunakan protokol I2C dengan alamat default 0x68.

Modul ini juga dilengkapi EEPROM 24C32 berkapasitas 32 byte yang dapat digunakan untuk menyimpan data non-volatile. EEPROM tersebut dapat diakses melalui I2C menggunakan alamat 0x57.

DS3231 Battery Holder

DS3231 memiliki dudukan baterai untuk menjaga fungsi pencatat waktu tetap berjalan secara akurat meskipun terjadi pemadaman atau pelepasan daya pada mikrokontroler. Seluruh pengaturan waktu dan alarm tetap tersimpan.

Untuk modul ini, gunakan baterai tipe LIR2032, karena baterai tersebut dapat diisi ulang. Jangan menggunakan CR2032, karena baterai tersebut tidak bersifat rechargeable.

 

Jika Anda tetap ingin menggunakan baterai CR2032 yang tidak dapat diisi ulang, Anda harus menonaktifkan rangkaian pengisian baterai pada modul. Caranya adalah dengan melepas resistor (biasanya ditandai sebagai R4 pada sebagian modul) yang terletak di sebelah dioda.

 

Alarm DS3231

DS3231 mendukung dua alarm internal, yaitu Alarm 1 dan Alarm 2. Kedua alarm ini dapat dikonfigurasi untuk aktif pada waktu atau tanggal tertentu. Saat alarm aktif, pin SQW pada modul akan mengeluarkan sinyal LOW.

 

Sinyal ini dapat dibaca oleh ESP32/ESP8266 untuk memicu interrupt atau bahkan membangunkan mikrokontroler dari mode deep sleep. Fitur ini sangat berguna untuk menjalankan wake-up berkala, tugas otomatis berbasis waktu, serta notifikasi satu kali (karena alarm dapat di-reset setelah dipicu).

Alamat I2C Modul DS3231

Secara default, alamat I2C untuk RTC DS3231 adalah 0x68, sedangkan alamat EEPROM pada modul adalah 0x57. Anda dapat menjalankan program I2C scanner untuk memverifikasi alamat tersebut.

Pinout Modul RTC DS3231

Tabel berikut menjelaskan susunan pin (pinout) pada modul RTC DS3231.

 

Menghubungkan Modul RTC DS3231 ke ESP32 atau ESP8266


Berikut adalah komponen yang diperlukan untuk mengikuti tutorial ini:

- Board ESP32 atau ESP8266

- Modul RTC DS3231

- Kabel jumper

- Breadboard

Sambungkan modul DS3231 ke board ESP32 atau ESP8266 sesuai konfigurasi pin yang direkomendasikan. Anda dapat menggunakan tabel berikut sebagai acuan atau merujuk pada diagram rangkaian yang disediakan.

 


ESP32

 



ESP8266 NodeMCU



Bekerja dengan RTC

Dalam menggunakan modul RTC pada sebuah proyek, terdapat dua langkah penting yang harus dilakukan:

1. Mengatur waktu awal: pengaturan waktu dapat dilakukan secara manual dengan memasukkan waktu saat ini (atau waktu lain yang diinginkan) melalui kode program, menggunakan waktu lokal dari sistem, atau mengambil waktu secara otomatis dari server NTP.

2. Mempertahankan waktu: agar RTC tetap menyimpan waktu dengan benar meskipun kehilangan suplai daya, modul harus dipasangkan dengan baterai cadangan. Modul RTC umumnya sudah dilengkapi dudukan baterai tipe koin.

Modul MicroPython untuk DS3231

Terdapat beberapa pustaka (library) yang dapat digunakan untuk mempermudah komunikasi dengan modul RTC DS3231. Dalam tutorial ini, kita akan menggunakan versi modifikasi dari modul urtc.py yang dikembangkan oleh Adafruit.

Ikuti langkah-langkah berikut untuk menginstal modul yang diperlukan:

Mengunduh dan Mengunggah urtc.py

- Unduh file urtc.py dari tautan yang tersedia.

- Salin kode tersebut ke dalam file baru di Thonny IDE.

- Buka File > Save as… kemudian pilih MicroPython Device.

- Simpan file dengan nama urtc.py (jangan mengubah nama file).

 

# Forked and adapted from https://github.com/adafruit/Adafruit-uRTC/tree/master


import collections

import time



DateTimeTuple = collections.namedtuple("DateTimeTuple", ["year", "month",

    "day", "weekday", "hour", "minute", "second", "millisecond"])



def datetime_tuple(year=None, month=None, day=None, weekday=None, hour=None,

                   minute=None, second=None, millisecond=None):

    return DateTimeTuple(year, month, day, weekday, hour, minute,

                         second, millisecond)



def _bcd2bin(value):

    return (value or 0) - 6 * ((value or 0) >> 4)



def _bin2bcd(value):

    return (value or 0) + 6 * ((value or 0) // 10)



def tuple2seconds(datetime):

    return time.mktime((datetime.year, datetime.month, datetime.day,

        datetime.hour, datetime.minute, datetime.second, datetime.weekday, 0))



def seconds2tuple(seconds):

    (year, month, day, hour, minute,

     second, weekday, _yday) = time.localtime(seconds)

    return DateTimeTuple(year, month, day, weekday, hour, minute, second, 0)



class _BaseRTC:

    _SWAP_DAY_WEEKDAY = False


    def __init__(self, i2c, address=0x68):

        self.i2c = i2c

        self.address = address


    def _register(self, register, buffer=None):

        if buffer is None:

            return self.i2c.readfrom_mem(self.address, register, 1)[0]

        self.i2c.writeto_mem(self.address, register, buffer)


    def _flag(self, register, mask, value=None):

        data = self._register(register)

        if value is None:

            return bool(data & mask)

        if value:

            data |= mask

        else:

            data &= ~mask

        self._register(register, bytearray((data,)))



    def datetime(self, datetime=None):

        if datetime is None:

            buffer = self.i2c.readfrom_mem(self.address,

                                           self._DATETIME_REGISTER, 7)

            if self._SWAP_DAY_WEEKDAY:

                day = buffer[3]

                weekday = buffer[4]

            else:

                day = buffer[4]

                weekday = buffer[3]

            return datetime_tuple(

                year=_bcd2bin(buffer[6]) + 2000,

                month=_bcd2bin(buffer[5]),

                day=_bcd2bin(day),

                weekday=_bcd2bin(weekday),

                hour=_bcd2bin(buffer[2]),

                minute=_bcd2bin(buffer[1]),

                second=_bcd2bin(buffer[0]),

            )

        datetime = datetime_tuple(*datetime)

        buffer = bytearray(7)

        buffer[0] = _bin2bcd(datetime.second)

        buffer[1] = _bin2bcd(datetime.minute)

        buffer[2] = _bin2bcd(datetime.hour)

        if self._SWAP_DAY_WEEKDAY:

            buffer[4] = _bin2bcd(datetime.weekday)

            buffer[3] = _bin2bcd(datetime.day)

        else:

            buffer[3] = _bin2bcd(datetime.weekday)

            buffer[4] = _bin2bcd(datetime.day)

        buffer[5] = _bin2bcd(datetime.month)

        buffer[6] = _bin2bcd(datetime.year - 2000)

        self._register(self._DATETIME_REGISTER, buffer)



class DS1307(_BaseRTC):

    _NVRAM_REGISTER = 0x08

    _DATETIME_REGISTER = 0x00

    _SQUARE_WAVE_REGISTER = 0x07


    def stop(self, value=None):

        return self._flag(0x00, 0b10000000, value)


    def memory(self, address, buffer=None):

        if buffer is not None and address + len(buffer) > 56:

            raise ValueError("address out of range")

        return self._register(self._NVRAM_REGISTER + address, buffer)



class DS3231(_BaseRTC):

    _CONTROL_REGISTER = 0x0e

    _STATUS_REGISTER = 0x0f

    _DATETIME_REGISTER = 0x00

    _TEMPERATURE_MSB_REGISTER = 0x11

    _TEMPERATURE_LSB_REGISTER = 0x12

    _ALARM_REGISTERS = (0x08, 0x0b)

    _SQUARE_WAVE_REGISTER = 0x0e


    def lost_power(self):

        return self._flag(self._STATUS_REGISTER, 0b10000000)


    def alarm(self, value=None, alarm=0):

        return self._flag(self._STATUS_REGISTER,

                          0b00000011 & (1 << alarm), value)


    def interrupt(self, alarm=0):

        return self._flag(self._CONTROL_REGISTER,

                          0b00000100 | (1 << alarm), 1)


    def no_interrupt(self):

        return self._flag(self._CONTROL_REGISTER, 0b00000011, 0)


    def stop(self, value=None):

        return self._flag(self._CONTROL_REGISTER, 0b10000000, value)


    def datetime(self, datetime=None):

        if datetime is not None:

            status = self._register(self._STATUS_REGISTER) & 0b01111111

            self._register(self._STATUS_REGISTER, bytearray((status,)))

        return super().datetime(datetime)


    def alarm_time(self, datetime=None, alarm=0):

        if datetime is None:

            buffer = self.i2c.readfrom_mem(self.address,

                                           self._ALARM_REGISTERS[alarm], 3)

            day = None

            weekday = None

            second = None

            if buffer[2] & 0b10000000:

                pass

            elif buffer[2] & 0b01000000:

                day = _bcd2bin(buffer[2] & 0x3f)

            else:

                weekday = _bcd2bin(buffer[2] & 0x3f)

            minute = (_bcd2bin(buffer[0] & 0x7f)

                      if not buffer[0] & 0x80 else None)

            hour = (_bcd2bin(buffer[1] & 0x7f)

                    if not buffer[1] & 0x80 else None)

            if alarm == 0:

                # handle seconds

                buffer = self.i2c.readfrom_mem(

                    self.address, self._ALARM_REGISTERS[alarm] - 1, 1)

                second = (_bcd2bin(buffer[0] & 0x7f)

                          if not buffer[0] & 0x80 else None)

            return datetime_tuple(

                day=day,

                weekday=weekday,

                hour=hour,

                minute=minute,

                second=second,

            )

        datetime = datetime_tuple(*datetime)

        buffer = bytearray(3)

        buffer[0] = (_bin2bcd(datetime.minute)

                     if datetime.minute is not None else 0x80)

        buffer[1] = (_bin2bcd(datetime.hour)

                     if datetime.hour is not None else 0x80)

        if datetime.day is not None:

            if datetime.weekday is not None:

                raise ValueError("can't specify both day and weekday")

            buffer[2] = _bin2bcd(datetime.day)

        elif datetime.weekday is not None:

            buffer[2] = _bin2bcd(datetime.weekday) | 0b01000000

        else:

            buffer[2] = 0x80

        self._register(self._ALARM_REGISTERS[alarm], buffer)

        if alarm == 0:

            # handle seconds

            buffer = bytearray([_bin2bcd(datetime.second)

                                if datetime.second is not None else 0x80])

            self._register(self._ALARM_REGISTERS[alarm] - 1, buffer)

    

    def get_temperature(self):

        """

        Reads the temperature from the DS3231's temperature registers.

        Returns the temperature as a float in Celsius.

        """

        msb = self._register(self._TEMPERATURE_MSB_REGISTER)  # 0x11

        lsb = self._register(self._TEMPERATURE_LSB_REGISTER)  # 0x12

        

        if msb is None or lsb is None:

            print("Error: Register read returned None")

            return None

        

        temp = msb + ((lsb >> 6) * 0.25)

        if msb & 0x80:

            temp -= 256

        

        return temp


class PCF8523(_BaseRTC):

    _CONTROL1_REGISTER = 0x00

    _CONTROL2_REGISTER = 0x01

    _CONTROL3_REGISTER = 0x02

    _DATETIME_REGISTER = 0x03

    _ALARM_REGISTER = 0x0a

    _SQUARE_WAVE_REGISTER = 0x0f

    _SWAP_DAY_WEEKDAY = True


    def __init__(self, *args, **kwargs):

        super().__init__(*args, **kwargs)

        self.init()


    def init(self):

        # Enable battery switchover and low-battery detection.

        self._flag(self._CONTROL3_REGISTER, 0b11100000, False)


    def reset(self):

        self._flag(self._CONTROL1_REGISTER, 0x58, True)

        self.init()


    def lost_power(self, value=None):

        return self._flag(self._CONTROL3_REGISTER, 0b00010000, value)


    def stop(self, value=None):

        return self._flag(self._CONTROL1_REGISTER, 0b00010000, value)


    def battery_low(self):

        return self._flag(self._CONTROL3_REGISTER, 0b00000100)


    def alarm(self, value=None):

        return self._flag(self._CONTROL2_REGISTER, 0b00001000, value)


    def datetime(self, datetime=None):

        if datetime is not None:

            self.lost_power(False) # clear the battery switchover flag

        return super().datetime(datetime)


    def alarm_time(self, datetime=None):

        if datetime is None:

            buffer = self.i2c.readfrom_mem(self.address,

                                           self._ALARM_REGISTER, 4)

            return datetime_tuple(

                weekday=_bcd2bin(buffer[3] &

                                 0x7f) if not buffer[3] & 0x80 else None,

                day=_bcd2bin(buffer[2] &

                             0x7f) if not buffer[2] & 0x80 else None,

                hour=_bcd2bin(buffer[1] &

                              0x7f) if not buffer[1] & 0x80 else None,

                minute=_bcd2bin(buffer[0] &

                                0x7f) if not buffer[0] & 0x80 else None,

            )

        datetime = datetime_tuple(*datetime)

        buffer = bytearray(4)

        buffer[0] = (_bin2bcd(datetime.minute)

                     if datetime.minute is not None else 0x80)

        buffer[1] = (_bin2bcd(datetime.hour)

                     if datetime.hour is not None else 0x80)

        buffer[2] = (_bin2bcd(datetime.day)

                     if datetime.day is not None else 0x80)

        buffer[3] = (_bin2bcd(datetime.weekday) | 0b01000000

                     if datetime.weekday is not None else 0x80)

        self._register(self._ALARM_REGISTER, buffer)

 

Setelah modul tersebut berhasil diunggah ke board, Anda sudah dapat memanfaatkan seluruh fungsinya di dalam program untuk berinteraksi dengan modul RTC DS3231.

DS3231 RTC: Mengatur dan Membaca Waktu dengan MicroPython

Kode berikut digunakan untuk menyinkronkan waktu RTC dengan waktu sistem, kemudian membaca tanggal, waktu, dan suhu secara berkala setiap satu detik.

 

# Rui Santos & Sara Santos - Random Nerd Tutorials

# Complete project details at https://RandomNerdTutorials.com/micropython-esp32-esp8266-ds3231/

# Code to synchronize the RTC with the local time


import time

import urtc

from machine import I2C, Pin


days_of_week = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']


# Initialize RTC (connected to I2C) - ESP32

i2c = I2C(0, scl=Pin(22), sda=Pin(21))

# Initialize RTC (connected to I2C) - uncomment for ESP8266

#i2c = I2C(scl=Pin(5), sda=Pin(4))


rtc = urtc.DS3231(i2c)


# Set the current time using a specified time tuple

# Time tuple: (year, month, day, day of week, hour, minute, seconds, milliseconds)

#initial_time = (2025, 1, 30, 1, 12, 30, 0, 0)


# Or get the local time from the system

initial_time_tuple = time.localtime()  # tuple (microPython)

initial_time_seconds = time.mktime(initial_time_tuple)  # local time in seconds


# Convert to tuple compatible with the library

initial_time = urtc.seconds2tuple(initial_time_seconds)


# Sync the RTC

rtc.datetime(initial_time)


while True:

    current_datetime = rtc.datetime()

    temperature = rtc.get_temperature()

    

    # Display time details

    print('Current date and time:')

    print('Year:', current_datetime.year)

    print('Month:', current_datetime.month)

    print('Day:', current_datetime.day)

    print('Hour:', current_datetime.hour)

    print('Minute:', current_datetime.minute)

    print('Second:', current_datetime.second)

    print('Day of the Week:', days_of_week[current_datetime.weekday])

    print(f"Current temperature: {temperature}°C")    

    # Format the date and time

    formatted_datetime = (

        f"{days_of_week[current_datetime.weekday]}, "

        f"{current_datetime.year:04d}-{current_datetime.month:02d}-{current_datetime.day:02d} "

        f"{current_datetime.hour:02d}:{current_datetime.minute:02d}:{current_datetime.second:02d} "

    )

    print(f"Current date and time: {formatted_datetime}")

    

    print(" \n");

    time.sleep(1)

 

Cara Kerja Kode

Berikut penjelasan singkat mengenai bagian-bagian penting dari kode tersebut.

Import Library

Pertama, kita perlu mengimpor modul urtc yang sebelumnya telah diunggah ke board karena modul tersebut menyediakan fungsi-fungsi untuk berkomunikasi dengan RTC. Selain itu, kita juga mengimpor kelas Pin dan I2C untuk membangun komunikasi I2C dengan modul RTC.

 

import time

import urtc

from machine import I2C, Pin

 

Inisialisasi Modul RTC

Selanjutnya, lakukan inisialisasi komunikasi I2C dan buat sebuah objek bernama rtc yang merepresentasikan modul DS3231. Pada contoh ini, digunakan pin I2C bawaan (default) dari ESP32 maupun ESP8266.

 

# Initialize RTC (connected to I2C) - ESP32

i2c = I2C(0, scl=Pin(22), sda=Pin(21))

# Initialize RTC (connected to I2C) - uncomment for ESP8266

#i2c = I2C(scl=Pin(5), sda=Pin(4))

rtc = urtc.DS3231(i2c)

 

Jika Anda menggunakan ESP32, inisialisasi objek I2C dapat dilakukan dengan perintah berikut:

 

i2c = I2C(0, scl=Pin(22), sda=Pin(21))

 

Jika Anda menggunakan ESP8266, objek I2C dapat dibuat dengan perintah berikut:

 

i2c = I2C(scl=Pin(5), sda=Pin(4))

 

Sinkronisasi Waktu

Untuk menyinkronkan waktu pada RTC, gunakan metode datetime() pada objek rtc dan berikan sebuah time tuple sebagai argumen dengan format berikut:

 

(year, month, day, day of week, hour, minute, seconds, milliseconds)

 

Catatan: format tuple ini berbeda dari format tuple yang digunakan pada modul *time* di MicroPython.

 

Anda dapat mengatur waktu secara manual ke tanggal dan jam tertentu menggunakan contoh berikut:

 

#initial_time = (2025, 1, 30, 1, 12, 30, 0, 0)

 

Atau, Anda dapat menyinkronkan RTC dengan waktu lokal pada sistem. Pada contoh ini, kita akan melakukan sinkronisasi menggunakan waktu lokal tersebut. 

Pertama, ambil time tuple dari waktu lokal dengan menggunakan fungsi time.localtime().

 

initial_time_tuple = time.localtime()  # tuple (microPython)

 

Tuple yang dihasilkan memiliki format berbeda dari yang digunakan oleh modul urtc.

Karena itu, langkah pertama adalah mengonversinya ke satuan detik menggunakan fungsi time.mktime().

 

initial_time_seconds = time.mktime(initial_time_tuple)  # local time in seconds

 

Terakhir, konversikan nilai detik tersebut ke dalam format tuple yang kompatibel dengan pustaka urtc menggunakan fungsi seconds2tuple(). Fungsi ini menerima jumlah detik sejak epoch sebagai argumen dan menghasilkan tuple waktu yang sesuai untuk modul RTC.

 

initial_time = urtc.seconds2tuple(initial_time_seconds)

 

Selanjutnya, variabel initial_time yang sudah berisi waktu lokal dalam format tuple yang kompatibel dengan modul urtc dapat diberikan ke fungsi datetime() seperti contoh berikut.

 

# Convert to tuple compatible with the library

initial_time = urtc.seconds2tuple(initial_time_seconds)


# Sync the RTC

rtc.datetime(initial_time)

 

Catatan: Setelah waktu berhasil disetel untuk pertama kali, Anda hanya perlu menyetelnya kembali jika RTC kehilangan daya (pastikan baterai terpasang untuk mencegah hal ini). Anda dapat memeriksa apakah RTC kehilangan daya dengan memanggil fungsi lost_power() pada objek rtc. Fungsi ini akan mengembalikan nilai True atau False.

Membaca Waktu

Setelah baris-baris kode sebelumnya dijalankan, modul RTC sudah tersinkronisasi dengan waktu lokal. Anda dapat memanggil rtc.datetime() untuk memperoleh waktu saat ini dari RTC. Fungsi ini mengembalikan objek yang berisi seluruh elemen waktu.

 

Untuk mengambil dan menampilkan setiap elemen waktu secara terpisah, Anda dapat menggunakan contoh berikut:

 

current_datetime = rtc.datetime()

print('Current date and time:')

print('Year:', current_datetime.year)

print('Month:', current_datetime.month)

print('Day:', current_datetime.day)

print('Hour:', current_datetime.hour)

print('Minute:', current_datetime.minute)

print('Second:', current_datetime.second)

print('Day of the Week:', days_of_week[current_datetime.weekday])

 

Anda juga dapat memperoleh data suhu dari modul dengan memanggil metode get_temperature() pada objek rtc.

 

temperature = rtc.get_temperature()

 

Data suhu yang diperoleh ditampilkan dalam satuan derajat Celsius.

 

print(f"Current temperature: {temperature}°C")

 

Pada contoh ini, waktu dan suhu terkini akan ditampilkan setiap satu detik.

time.sleep(1)

Menjalankan Kode

Jalankan kode tersebut untuk menyinkronkan waktu pada RTC dengan waktu lokal, serta membaca waktu dan suhu saat ini.

 


Setelah proses sinkronisasi dilakukan, RTC akan mempertahankan waktu secara mandiri selama baterai cadangan terpasang. Dengan demikian, Anda tidak perlu melakukan sinkronisasi ulang, dan cukup memanggil rtc.datetime() untuk mendapatkan waktu saat ini.



DS3231 dengan ESP32/ESP8266 – Mengatur Alarm

Modul RTC DS3231 mendukung hingga dua alarm internal, yaitu Alarm 1 dan Alarm 2. Ketika waktu saat ini mencapai waktu yang telah ditetapkan, modul akan mengubah status pin SQW dari HIGH menjadi LOW (aktif rendah). Perubahan status pada pin ini dapat dideteksi oleh ESP32/ESP8266 untuk menjalankan tugas tertentu saat alarm aktif.

 


Catatan penting terkait fitur alarm pada DS3231:

- Modul RTC mendukung penyimpanan hingga dua alarm.

- Hanya satu alarm yang dapat diaktifkan pada satu waktu.

- Setelah sebuah alarm dipicu, Anda harus menghapus (*clear*) flag-nya untuk mencegah alarm aktif kembali dan menghindari potensi crash pada ESP32/ESP8266.

- Sebelum mengaktifkan alarm lain, alarm yang sedang aktif harus dinonaktifkan terlebih dahulu.

DS3231 – Konfigurasi Alarm

Contoh berikut menunjukkan cara mengatur dua alarm. Pada contoh ini, LED bawaan pada board akan berubah status (toggle) saat alarm aktif.

 

# Rui Santos & Sara Santos - Random Nerd Tutorials

# Complete project details at https://RandomNerdTutorials.com/micropython-esp32-esp8266-ds3231/

 

import time

import urtc

from machine import Pin, I2C


# Pin setup for SQW pin and LED

CLOCK_INTERRUPT_PIN = 4  # Adjust to your specific GPIO pin for SQW (ESP32)

#CLOCK_INTERRUPT_PIN = 14  # Adjust to your specific GPIO pin for SQW (ESP8266)


LED_PIN = 2  # Adjust to your specific GPIO pin for the LED


# Initialize RTC (connected to I2C) - ESP32

i2c = I2C(0, scl=Pin(22), sda=Pin(21))

# Initialize RTC (connected to I2C) - uncomment for ESP8266

#i2c = I2C(scl=Pin(5), sda=Pin(4))


rtc = urtc.DS3231(i2c)


# Setup GPIO for SQW and LED

sqw_pin = Pin(CLOCK_INTERRUPT_PIN, Pin.IN, Pin.PULL_UP)

led_pin = Pin(LED_PIN, Pin.OUT)

led_pin.off()


# Alarm times (year, month, day, weekday, hour, minute, second, millisecond)

alarm1_time = urtc.datetime_tuple(2025, 1, 2, None, 14, 58, 0, 0)  # Alarm 1 uses full datetime

alarm2_time = urtc.datetime_tuple(2025, 1, 2, None, 14, 59, 0, 0)   # Alarm 2 uses day, hour, minute, weekday


# Print the current time from the RTC

def print_current_time():

    now = rtc.datetime()

    formatted_time = f"{now.year}-{now.month:02}-{now.day:02} {now.hour:02}:{now.minute:02}:{now.second:02}"

    print(f"Current time: {formatted_time}")


#Callback for handling alarm interrupt.

def on_alarm(pin):

    print("Interrupt detected.")

    

    if rtc.alarm(alarm=0):  # Check if Alarm 1 triggered

        print("Alarm 1 triggered.")

        rtc.alarm(False, alarm=0)  # Clear Alarm 1 flag

        led_pin.value(not led_pin.value())  # Toggle LED for Alarm 1

    

    if rtc.alarm(alarm=1):  # Check if Alarm 2 triggered

        print("Alarm 2 triggered.")

        rtc.alarm(False, alarm=1)  # Clear Alarm 2 flag

        led_pin.value(not led_pin.value())  # Toggle LED for Alarm 2


# Setup alarms on the DS3231

def setup_alarms():

    # Clear any existing alarms

    rtc.alarm(False, 0)

    rtc.alarm(False, 1)

    rtc.no_interrupt()


    # Set the desired alarm times

    rtc.alarm_time(alarm1_time, 0)  # Alarm 1

    rtc.alarm_time(alarm2_time, 1)  # Alarm 2


    # Enable interrupts for the alarms

    rtc.interrupt(0)

    rtc.interrupt(1)

    print("Alarms set successfully.")


# Attach the interrupt callback

sqw_pin.irq(trigger=Pin.IRQ_FALLING, handler=on_alarm)


try:

    # Sync time to compile time if RTC lost power

    if rtc.lost_power():

        initial_time_tuple = time.localtime()  # tuple (MicroPython)

        initial_time_seconds = time.mktime(initial_time_tuple)  # local time in seconds

        initial_time = urtc.seconds2tuple(initial_time_seconds)  # Convert to tuple compatible with the library

        rtc.datetime(initial_time)  # Sync the RTC


    print("RTC initialized. Setting alarms...")

    setup_alarms()


    while True:

        print_current_time()

        time.sleep(5)

except KeyboardInterrupt:


    print("Exiting program.")

 

Cara Kerja Kode

Berikut penjelasan mengenai bagian-bagian penting dari kode yang digunakan untuk mengatur alarm.

Pendefinisian Pin

Pertama, tentukan GPIO pada mikrokontroler yang terhubung ke pin SQW pada modul DS3231. Pin ini akan berubah status ketika alarm aktif. Pada contoh ini, digunakan GPIO 4 untuk ESP32 dan GPIO 14 untuk ESP8266. Anda dapat menggunakan pin lain selama sesuai dengan konfigurasi rangkaian.

 

CLOCK_INTERRUPT_PIN = 4  # Adjust to your specific GPIO pin for SQW (ESP32)

#CLOCK_INTERRUPT_PIN = 14  # Adjust to your specific GPIO pin for SQW (ESP8266)

 

Pin SQW kemudian dikonfigurasi sebagai input dengan resistor pull-up internal, karena sinyal pada pin tersebut bersifat active-low.

 

sqw_pin = Pin(CLOCK_INTERRUPT_PIN, Pin.IN, Pin.PULL_UP)

 

Waktu Alarm

Selanjutnya, dibuat dua variabel bertipe datetime_tuple untuk menyimpan konfigurasi waktu alarm seperti contoh berikut.

 

# Alarm times (year, month, day, weekday, hour, minute, second, millisecond)

alarm1_time = urtc.datetime_tuple(2025, 1, 2, None, 14, 44, 0, 0)  # Alarm 1 uses full datetime

alarm2_time = urtc.datetime_tuple(2025, 1, 2, None, 14, 46, 0, 0)   # Alarm 2 uses day, hour, minute, weekday

 

Sesuaikan nilai waktu alarm sesuai kebutuhan Anda.

Mengonfigurasi Pin SQW sebagai Interrupt

Selanjutnya, pin SQW dikonfigurasi sebagai sumber interrupt dengan memanggil metode irq().

sqw_pin.irq(trigger=Pin.IRQ_FALLING, handler=on_alarm)

Metode irq() menerima beberapa argumen penting:

- handler: fungsi yang akan dijalankan ketika interrupt terdeteksi — pada contoh ini adalah fungsi on_alarm().

- trigger: menentukan jenis pemicu interrupt. Terdapat tiga mode yang tersedia; dalam kasus ini digunakan IRQ_FALLING untuk memicu interrupt ketika pin berubah dari HIGH ke LOW.

Fungsi on_alarm

Fungsi on_alarm() digunakan untuk memeriksa alarm mana yang aktif, kemudian menonaktifkannya untuk menghapus flag alarm tersebut. Fungsi ini akan dieksekusi setiap kali alarm dipicu.

 

def on_alarm(pin):

 

Baris kode berikut memeriksa apakah Alarm 1 telah dipicu. Dengan cara ini, kita dapat langsung menonaktifkan alarm tersebut dan menjalankan tugas tertentu pada waktu yang telah ditentukan.

 

if rtc.alarm(alarm=0):  # Check if Alarm 1 triggered

    print("Alarm 1 triggered.")

    rtc.alarm(False, alarm=0)  # Clear Alarm 1 flag

    led_pin.value(not led_pin.value())  # Toggle LED for Alarm 1

 

Baris kode ini menghapus flag Alarm 1, mengembalikannya ke kondisi awal.

 

rtc.alarm(False, alarm=0)  # Clear Alarm 1 flag

 

Selanjutnya, kita mengubah status LED (toggle) sesuai kondisi alarm.

 

led_pin.value(not led_pin.value())  # Toggle LED for Alarm 1

 

Langkah yang sama juga diterapkan untuk Alarm 2.

 

if rtc.alarm(alarm=1):  # Check if Alarm 2 triggered

    print("Alarm 2 triggered.")

    rtc.alarm(False, alarm=1)  # Clear Alarm 2 flag

    led_pin.value(not led_pin.value())  # Toggle LED for Alarm 2

 

Konfigurasi Alarm

Kita membuat fungsi bernama setup_alarms() untuk mengatur alarm.

Sebelum mengatur alarm baru, pastikan semua alarm sebelumnya telah dihapus dengan kode berikut.

 

def setup_alarms():

    # Clear any existing alarms

    rtc.alarm(False, 0)

    rtc.alarm(False, 1)

    rtc.no_interrupt()

 

Selanjutnya, kita dapat mengatur waktu alarm dengan memanggil metode alarm_time() pada objek rtc. Berikan sebagai argumen waktu alarm (variabel bertipe datetime_tuple) dan nomor alarm (0 = Alarm 1; 1 = Alarm 2).

 

# Set the desired alarm times

rtc.alarm_time(alarm1_time, 0)  # Alarm 1

rtc.alarm_time(alarm2_time, 1)  # Alarm 2

 

Kita mengaktifkan interrupt untuk alarm, sehingga pin SQW akan terpicu saat salah satu alarm aktif.

 

rtc.interrupt(0)

rtc.interrupt(1)

 

Selanjutnya, terdapat blok try yang akan menyesuaikan waktu RTC jika diperlukan (misalnya jika modul kehilangan daya) sekaligus mengatur alarm.

 

try:

    # Sync time to compile time if RTC lost power

    if rtc.lost_power():

        initial_time_tuple = time.localtime()  # tuple (MicroPython)

        initial_time_seconds = time.mktime(initial_time_tuple)  # local time in seconds

        initial_time = urtc.seconds2tuple(initial_time_seconds)  # Convert to tuple compatible with the library

        rtc.datetime(initial_time)  # Sync the RTC


    print("RTC initialized. Setting alarms...")

    setup_alarms()

 

Selanjutnya, kita akan menampilkan waktu saat ini secara berkala setiap lima detik.

 

while True:

    print_current_time()

    time.sleep(5)

 

Deteksi alarm berjalan di latar belakang karena kita telah mengonfigurasi interrupt pada pin SQW.

Menguji Alarm

Atur alarm dengan waktu yang dekat agar dapat langsung melihat fungsinya. Setelah itu, jalankan kodenya.

 


Waktu saat ini akan ditampilkan di shell setiap lima detik. Ketika alarm aktif, pesan akan muncul di shell dan LED bawaan pada ESP32 atau ESP8266 akan berubah status (toggle).



DS3231 – Mengatur Alarm Periodik

Untuk mengatur alarm periodik, kita dapat menyesuaikan waktu alarm setiap kali alarm sebelumnya dipicu. Misalnya, jika ingin memicu alarm setiap 10 menit, caranya adalah:

1. Baca waktu saat ini ketika alarm dipicu.

2. Atur alarm baru dengan menambahkan 600 detik (10 menit) ke waktu saat ini.

3. Setelah 10 menit, alarm akan kembali dipicu.

4. Ulangi langkah-langkah sebelumnya.

Langkah-langkah ini akan diimplementasikan pada contoh berikut.

 

# Rui Santos & Sara Santos - Random Nerd Tutorials

# Complete project details at https://RandomNerdTutorials.com/micropython-esp32-esp8266-ds3231/

 

from machine import Pin, I2C

import time

import urtc


# Pin setup for SQW pin and LED

CLOCK_INTERRUPT_PIN = 4  # Adjust to your specific GPIO pin for SQW (ESP32)

#CLOCK_INTERRUPT_PIN = 14  # Adjust to your specific GPIO pin for SQW (ESP8266)


LED_PIN = 2  # Adjust to your specific GPIO pin for the LED


# Initialize RTC (connected to I2C) - ESP32

i2c = I2C(0, scl=Pin(22), sda=Pin(21))

# Initialize RTC (connected to I2C) - uncomment for ESP8266

#i2c = I2C(scl=Pin(5), sda=Pin(4))


rtc = urtc.DS3231(i2c)


# Setup GPIO for SQW and LED

sqw_pin = Pin(CLOCK_INTERRUPT_PIN, Pin.IN, Pin.PULL_UP)

led_pin = Pin(LED_PIN, Pin.OUT)

led_pin.off()


# Add minutes to a datetime tuple and return a new datetime tuple

def add_minutes_to_time(dt, minutes):

    timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, 0, 0))

    new_timestamp = timestamp + (minutes * 60)

    new_time = time.localtime(new_timestamp)

    return urtc.datetime_tuple(new_time[0], new_time[1], new_time[2], None, new_time[3], new_time[4], new_time[5], 0)


# Print the current time from the RTC

def print_current_time():

    now = rtc.datetime()

    formatted_time = f"{now.year}-{now.month:02}-{now.day:02} {now.hour:02}:{now.minute:02}:{now.second:02}"

    print(f"Current time: {formatted_time}")


# Callback for handling alarm interrupt

def on_alarm(pin):

    print("Alarm triggered! Toggling the LED.")

    led_pin.value(not led_pin.value())  # Toggle the LED


    # Clear the alarm flag and schedule the next alarm

    if rtc.alarm(alarm=0):  # Check if Alarm 0 triggered

        print("Clearing Alarm 0 flag.")

        rtc.alarm(False, alarm=0)  # Clear alarm flag


        # Schedule the alarm to repeat 10 minutes from now

        now = rtc.datetime()

        next_alarm_time = add_minutes_to_time(now, 10)  # Add 10 minutes

        rtc.alarm_time(next_alarm_time, alarm=0)  # Set new alarm

        rtc.interrupt(0)  # Ensure Alarm 0 interrupt is enabled

        print(f"Next alarm set for: {next_alarm_time}")


def setup_alarm():

    # Clear any existing alarms

    rtc.alarm(False, 0)

    rtc.no_interrupt()


    # Get the current time and set the first alarm 1 minute from now

    now = rtc.datetime()

    first_alarm_time = add_minutes_to_time(now, 1)  # Set first alarm for 1 minute from now

    rtc.alarm_time(first_alarm_time, alarm=0)  # Alarm 0


    # Enable the interrupt for Alarm 0

    rtc.interrupt(0)


    print(f"Alarm set for: {first_alarm_time}")


# Attach the interrupt callback

sqw_pin.irq(trigger=Pin.IRQ_FALLING, handler=on_alarm)


try:

    # Sync time to compile time if RTC lost power

    if rtc.lost_power():

        initial_time_tuple = time.localtime()  # tuple (MicroPython)

        initial_time_seconds = time.mktime(initial_time_tuple)  # local time in seconds

        initial_time = urtc.seconds2tuple(initial_time_seconds)  # Convert to tuple compatible with the library

        rtc.datetime(initial_time)  # Sync the RTC


    print("RTC initialized. Setting up the periodic alarm...")

    setup_alarm()


    while True:

        print_current_time()

        time.sleep(5)

except KeyboardInterrupt:

    print("Exiting program.")

 

Pada contoh ini, kita membuat fungsi bernama add_minutes_to_time() yang berfungsi menambahkan sejumlah menit ke waktu saat ini.

 

# Add minutes to a datetime tuple and return a new datetime tuple

def add_minutes_to_time(dt, minutes):

    timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, 0, 0))

    new_timestamp = timestamp + (minutes * 60)

    new_time = time.localtime(new_timestamp)

    return urtc.datetime_tuple(new_time[0], new_time[1], new_time[2], None, new_time[3], new_time[4], new_time[5], 0)

 

Fungsi ini menerima dua argumen: sebuah datetime tuple (misalnya waktu saat ini) dan jumlah menit yang ingin ditambahkan untuk mengatur alarm baru di masa depan. Fungsi akan mengembalikan datetime tuple baru yang berisi waktu alarm yang telah diperbarui.

 

Saat alarm dipicu, fungsi on_alarm() akan dijalankan. Pertama-tama, fungsi ini akan mengubah status LED bawaan pada board.

 

# Callback for handling alarm interrupt

def on_alarm(pin):

    print("Alarm triggered! Toggling the LED.")

    led_pin.value(not led_pin.value())  # Toggle the LED

 

Selanjutnya, fungsi ini akan menonaktifkan alarm dengan menghapus flag Alarm 1.

 

if rtc.alarm(alarm=0):  # Check if Alarm 0 triggered

    print("Clearing Alarm 0 flag.")

    rtc.alarm(False, alarm=0)  # Clear alarm flag

 

Setelah itu, fungsi akan mengatur waktu alarm baru dengan menambahkan sejumlah menit tertentu ke waktu saat ini. Pada contoh ini, alarm diatur untuk aktif setiap 10 menit.

 

# Schedule the alarm to repeat 10 minutes from now

now = rtc.datetime()

next_alarm_time = add_minutes_to_time(now, 10)  # Add 10 minutes

rtc.alarm_time(next_alarm_time, alarm=0)  # Set new alarm

rtc.interrupt(0)  # Ensure Alarm 0 interrupt is enabled

print(f"Next alarm set for: {next_alarm_time}")

 

Anda dapat dengan mudah mengubah interval ini dengan menyesuaikannya pada baris kode berikut sesuai waktu yang diinginkan.

 

next_alarm_time = add_minutes_to_time(now, 10)  # Add 10 minutes

Menguji Alarm Periodik

Atur alarm pada waktu yang dekat agar dapat langsung melihat fungsinya. Setelah itu, jalankan kodenya.

 


Alarm pertama akan aktif setelah satu menit, sedangkan alarm berikutnya akan aktif setiap 10 menit. Saat alarm dipicu, status LED bawaan pada board juga akan diubah (toggle).











Siap Untuk Membuat Proyek Impianmu Menjadi Kenyataan?

Klik di sini untuk chat langsung via WhatsApp dan dapatkan dukungan langsung dari tim ahli kami! 

 

0 on: "Langkah Mudah Menggunakan DS3231 Real Time Clock di ESP32/ESP8266 dengan MicroPython"