Pycom DS3231 RTC

December 15, 2019

pycom | python | rtc | DS3231 | wipy | lopy

In this project I show you how to easily use a DS3231 I²C real-time clock (RTC) with your Pycom board (WiPy, LoPy, etc.) when no internet connection is permanently available for syncing the internal RTC. The DS3231 is a low-cost, extremely accurate I²C real-time clock (RTC) with an integrated temperature-compensated crystal oscillator (TCXO) and crystal. The device incorporates a battery input and maintains accurate timekeeping when the main power to the device is interrupted.

The application uses the DS3231 or the internet to set the time on your Pycom board.

Here it is how the code will work:

  • check for DS3231 module; if no module found, try to connect to an ntp server for syncing the time
  • if DS3231 module found, sync the time with the module and try to connect to an ntp server for keeping the time accurate

The entire code can be downloaded from GitHub.

Here are few code snipets:

import the needed modules:

import time
from DS3231 import DS3231
from machine import RTC, idle
from network import WLAN

define a function to easily connect to the router:

def do_connect(ssid=None, password=None, timeout=3000, wait=True):
    if not wlan.isconnected():
        print('connecting to network...')
        try:
            wlan.connect(ssid, auth=(WLAN.WPA2, password), timeout=timeout)
            while not wlan.isconnected() and wait:
                idle() # save power while waiting
            return True
        except Exception as e:
            print("Failed to connect to network")
            return False

a function which tries to sync the time with an ntp server

def setRTCLocalTime():
    if do_connect(ssid='<your_ssid>', password='<your_ssid_password>', timeout=5000, wait=True):
        rtc.ntp_sync("pool.ntp.org")
        print('RTC Set from NTP to UTC:', rtc.now())
        wlan.disconnect()

the main code looks like this:

if DS3231().loadTime():
    if not rtc.synced():
        setRTCLocalTime()
        DS3231().saveTime()

    print("time synced with DS3231: ", time.localtime())
else:
    setRTCLocalTime()   #try to sync with ntp server if DS3231 module not found
    print("time from RTC: ", time.localtime())

Here it is the DS3231 module I used and how to connect it to the Pycom Expansion Board:

DS3231 module
DS3231 on expansion board

The full code for DS3231 library:

from machine import I2C
from machine import RTC
DS3231_I2C_ADDR = 104

def bcd2dec(bcd):
    return (((bcd & 0b11110000)>>4)*10 + (bcd & 0b00001111));

def dec2bcd(dec):
    t = dec // 10;
    o = dec - t * 10;
    return (t << 4) + o;

class DS3231:
    def __init__(self):
        self.ds3231 = I2C(0, I2C.MASTER, baudrate=100000, pins=('P23', 'P22')) #sda=P23, scl=P22
        self.rtc = RTC()

    def loadTime(self):
        if DS3231_I2C_ADDR in self.ds3231.scan():
            data = self.ds3231.readfrom_mem(DS3231_I2C_ADDR, 0, 7)
            ss=bcd2dec(data[0] & 0b01111111)
            mm=bcd2dec(data[1] & 0b01111111)
            if data[2] & 0b01000000 > 0:
                hh=bcd2dec(data[2] & 0b00011111)
                if data[2] & 0b00100000 >0:
                    hh+=12
            else:
                hh=bcd2dec(data[2] & 0b00111111)
            DD=bcd2dec(data[4] & 0b00111111)
            MM=bcd2dec(data[5] & 0b00011111)
            YY=bcd2dec(data[6])
            if data[5] & 0b10000000 > 0:
                YY=YY+2000
            else:
                YY=YY+1900
            self.rtc.init((YY,MM,DD,hh,mm,ss))
            return True
        else:
            print("DS3231 not found on I2C bus at %d" % DS3231_I2C_ADDR)
            return False


    def saveTime(self):
        (YY,MM,DD,hh,mm,ss,micro,tz) = self.rtc.now()
        self.ds3231.writeto_mem(DS3231_I2C_ADDR, 0,dec2bcd(ss))
        self.ds3231.writeto_mem(DS3231_I2C_ADDR, 1,dec2bcd(mm))
        self.ds3231.writeto_mem(DS3231_I2C_ADDR, 2,dec2bcd(hh))
        self.ds3231.writeto_mem(DS3231_I2C_ADDR, 4,dec2bcd(DD))
        if YY >= 2000:
            self.ds3231.writeto_mem(DS3231_I2C_ADDR, 5,dec2bcd(MM) | 0b10000000)
            self.ds3231.writeto_mem(DS3231_I2C_ADDR, 6,dec2bcd(YY-2000))
        else:
            self.ds3231.writeto_mem(DS3231_I2C_ADDR, 5,dec2bcd(MM))
            self.ds3231.writeto_mem(DS3231_I2C_ADDR, 6,dec2bcd(YY-1900))

comments powered by Disqus