├── README.md ├── SDL_DS3231.py └── testSDL_DS3231.py /README.md: -------------------------------------------------------------------------------- 1 | 2 | Raspberry Pi Python Library for SwitchDoc Labs DS3231/AT24C32 RTC Module 3 | 4 | SwitchDoc Labs, LLC December 19, 2014 5 | 6 | Clone respository and run testDS3231.py to test 7 | 8 | More Information on www.switchdoc.com 9 | 10 | Runs RTC and EEPROM 11 | 12 | 13 | -------------------------------------------------------------------------------- /SDL_DS3231.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SDL_DS3231.py Python Driver Code 4 | # SwitchDoc Labs 12/19/2014 5 | # V 1.2 6 | # only works in 24 hour mode 7 | # now includes reading and writing the AT24C32 included on the SwitchDoc Labs 8 | # DS3231 / AT24C32 Module (www.switchdoc.com) 9 | # please send patches to: https://github.com/switchdoclabs/RTC_SDL_DS3231 10 | 11 | 12 | #encoding: utf-8 13 | 14 | # Copyright (C) 2013 @XiErCh 15 | # 16 | # Permission is hereby granted, free of charge, to any person obtaining a copy 17 | # of this software and associated documentation files (the "Software"), to deal 18 | # in the Software without restriction, including without limitation the rights 19 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | # copies of the Software, and to permit persons to whom the Software is 21 | # furnished to do so, subject to the following conditions: 22 | # 23 | # The above copyright notice and this permission notice shall be included in all 24 | # copies or substantial portions of the Software. 25 | # 26 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | # SOFTWARE. 33 | 34 | from __future__ import print_function 35 | 36 | from datetime import datetime 37 | import time 38 | 39 | import smbus 40 | 41 | 42 | SECONDS_PER_MINUTE = 60 43 | MINUTES_PER_HOUR = 60 44 | HOURS_PER_DAY = 24 45 | DAYS_PER_WEEK = 7 46 | MAX_DAYS_PER_MONTH = 31 47 | MONTHS_PER_YEAR = 12 48 | YEARS_PER_CENTURY = 100 49 | 50 | OSCILLATOR_ON_MASK = 0b1<<7 51 | 52 | def bcd_to_int(bcd, n=2): 53 | """Decode n least significant packed binary coded decimal digits to binary. 54 | Return binary result. 55 | n defaults to 2 (BCD digits). 56 | n=0 decodes all digits. 57 | """ 58 | return int(('%x' % bcd)[-n:]) 59 | 60 | 61 | def int_to_bcd(x, n=2): 62 | """ 63 | Encode the n least significant decimal digits of x 64 | to packed binary coded decimal (BCD). 65 | Return packed BCD value. 66 | n defaults to 2 (digits). 67 | n=0 encodes all digits. 68 | """ 69 | return int(str(x)[-n:], 0x10) 70 | 71 | 72 | class SDL_DS3231(): 73 | ( 74 | _REG_SECONDS, 75 | _REG_MINUTES, 76 | _REG_HOURS, 77 | _REG_DAY, 78 | _REG_DATE, 79 | _REG_MONTH, 80 | _REG_YEAR, 81 | ) = range(7) 82 | 83 | ########################### 84 | # DS3231 Code 85 | # datasheet: https://datasheets.maximintegrated.com/en/ds/DS3231.pdf 86 | ########################### 87 | def __init__(self, twi=1, addr=0x68, at24c32_addr=0x56): 88 | self._bus = smbus.SMBus(twi) 89 | self._addr = addr 90 | self._at24c32_addr = at24c32_addr 91 | 92 | def _write(self, register, data): 93 | if False: 94 | print( 95 | "addr =0x%x register = 0x%x data = 0x%x %i " % 96 | (self._addr, register, data, bcd_to_int(data))) 97 | self._bus.write_byte_data(self._addr, register, data) 98 | 99 | def _read(self, register_address): 100 | data = self._bus.read_byte_data(self._addr, register_address) 101 | if False: 102 | print( 103 | "addr = 0x%x register_address = 0x%x %i data = 0x%x %i " 104 | % ( 105 | self._addr, register_address, register_address, 106 | data, bcd_to_int(data))) 107 | return data 108 | 109 | def _incoherent_read_all(self): 110 | """Return tuple of year, month, date, day, hours, minutes, seconds. 111 | Since each value is read one byte at a time, 112 | it might not be coherent.""" 113 | 114 | register_addresses = ( 115 | self._REG_SECONDS, 116 | self._REG_MINUTES, 117 | self._REG_HOURS, 118 | self._REG_DAY, 119 | self._REG_DATE, 120 | self._REG_MONTH, 121 | self._REG_YEAR, 122 | ) 123 | seconds, minutes, hours, day, date, month, year = ( 124 | self._read(register_address) 125 | for register_address in register_addresses 126 | ) 127 | seconds &= ~OSCILLATOR_ON_MASK 128 | if True: 129 | # This stuff is suspicious. 130 | if hours == 0x64: 131 | hours = 0x40 132 | hours &= 0x3F 133 | return tuple( 134 | bcd_to_int(t) 135 | for t in (year, month, date, day, hours, minutes, seconds)) 136 | 137 | def read_all(self): 138 | """Return tuple of year, month, date, day, hours, minutes, seconds. 139 | """ 140 | 141 | """Read until one gets same result twice in a row. 142 | Then one knows the time is coherent.""" 143 | 144 | old = self._incoherent_read_all() 145 | while True: 146 | new = self._incoherent_read_all() 147 | if old == new: 148 | break 149 | old = new 150 | return new 151 | 152 | def read_str(self): 153 | """Return a string such as 'YY-DD-MMTHH-MM-SS'. 154 | """ 155 | year, month, date, _, hours, minutes, seconds = self.read_all() 156 | return ( 157 | '%02d-%02d-%02dT%02d:%02d:%02d' % 158 | (year, month, date, hours, minutes, seconds) 159 | ) 160 | 161 | def read_datetime(self, century=21, tzinfo=None): 162 | """Return the datetime.datetime object. 163 | """ 164 | year, month, date, _, hours, minutes, seconds = self.read_all() 165 | year = 100 * (century - 1) + year 166 | return datetime( 167 | year, month, date, hours, minutes, seconds, 168 | 0, tzinfo=tzinfo) 169 | 170 | def write_all(self, seconds=None, minutes=None, hours=None, day=None, 171 | date=None, month=None, year=None, save_as_24h=True): 172 | """Direct write un-none value. 173 | Range: seconds [0,59], minutes [0,59], hours [0,23], 174 | day [0,7], date [1-31], month [1-12], year [0-99]. 175 | """ 176 | if seconds is not None: 177 | if not 0 <= seconds < SECONDS_PER_MINUTE: 178 | raise ValueError('Seconds is out of range [0,59].') 179 | seconds_reg = int_to_bcd(seconds) 180 | self._write(self._REG_SECONDS, seconds_reg) 181 | 182 | if minutes is not None: 183 | if not 0 <= minutes < MINUTES_PER_HOUR: 184 | raise ValueError('Minutes is out of range [0,59].') 185 | self._write(self._REG_MINUTES, int_to_bcd(minutes)) 186 | 187 | if hours is not None: 188 | if not 0 <= hours < HOURS_PER_DAY: 189 | raise ValueError('Hours is out of range [0,23].') 190 | self._write(self._REG_HOURS, int_to_bcd(hours) ) # not | 0x40 according to datasheet 191 | 192 | if year is not None: 193 | if not 0 <= year < YEARS_PER_CENTURY: 194 | raise ValueError('Years is out of range [0,99].') 195 | self._write(self._REG_YEAR, int_to_bcd(year)) 196 | 197 | if month is not None: 198 | if not 1 <= month <= MONTHS_PER_YEAR: 199 | raise ValueError('Month is out of range [1,12].') 200 | self._write(self._REG_MONTH, int_to_bcd(month)) 201 | 202 | if date is not None: 203 | # How about a more sophisticated check? 204 | if not 1 <= date <= MAX_DAYS_PER_MONTH: 205 | raise ValueError('Date is out of range [1,31].') 206 | self._write(self._REG_DATE, int_to_bcd(date)) 207 | 208 | if day is not None: 209 | if not 1 <= day <= DAYS_PER_WEEK: 210 | raise ValueError('Day is out of range [1,7].') 211 | self._write(self._REG_DAY, int_to_bcd(day)) 212 | 213 | def write_datetime(self, dt): 214 | """Write from a datetime.datetime object. 215 | """ 216 | self.write_all(dt.second, dt.minute, dt.hour, 217 | dt.isoweekday(), dt.day, dt.month, dt.year % 100) 218 | 219 | def write_now(self): 220 | """Equal to DS3231.write_datetime(datetime.datetime.now()). 221 | """ 222 | self.write_datetime(datetime.now()) 223 | 224 | def getTemp(self): 225 | byte_tmsb = self._bus.read_byte_data(self._addr,0x11) 226 | byte_tlsb = bin(self._bus.read_byte_data(self._addr,0x12))[2:].zfill(8) 227 | return byte_tmsb+int(byte_tlsb[0])*2**(-1)+int(byte_tlsb[1])*2**(-2) 228 | 229 | ########################### 230 | # AT24C32 Code 231 | # datasheet: atmel.com/Images/doc0336.pdf 232 | ########################### 233 | 234 | def set_current_AT24C32_address(self,address): 235 | a1, a0 = divmod(address, 1<<8) 236 | self._bus.write_i2c_block_data(self._at24c32_addr,a1,[a0]) 237 | 238 | def read_AT24C32_byte(self, address): 239 | if False: 240 | print( 241 | "i2c_address =0x%x eepromaddress = 0x%x " % 242 | (self._at24c32_addr, address)) 243 | 244 | self.set_current_AT24C32_address(address) 245 | return self._bus.read_byte(self._at24c32_addr) 246 | 247 | def write_AT24C32_byte(self, address, value): 248 | if False: 249 | print( 250 | "i2c_address =0x%x eepromaddress = 0x%x value = 0x%x %i " % 251 | (self._at24c32_addr, address, value, value)) 252 | a1, a0 = divmod(address, 1<<8) 253 | self._bus.write_i2c_block_data(self._at24c32_addr,a1,[a0, value]) 254 | time.sleep(0.20) 255 | -------------------------------------------------------------------------------- /testSDL_DS3231.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Test SDL_DS3231 4 | # John C. Shovic, SwitchDoc Labs 5 | # 08/03/2014 6 | # 7 | # 8 | 9 | # imports 10 | 11 | import sys 12 | import time 13 | import datetime 14 | import random 15 | import SDL_DS3231 16 | 17 | # Main Program 18 | 19 | print "" 20 | print "Test SDL_DS3231 Version 1.0 - SwitchDoc Labs" 21 | print "" 22 | print "" 23 | print "Program Started at:"+ time.strftime("%Y-%m-%d %H:%M:%S") 24 | print "" 25 | 26 | filename = time.strftime("%Y-%m-%d%H:%M:%SRTCTest") + ".txt" 27 | starttime = datetime.datetime.utcnow() 28 | 29 | ds3231 = SDL_DS3231.SDL_DS3231(1, 0x68) 30 | #comment out the next line after the clock has been initialized 31 | ds3231.write_now() 32 | 33 | # Main Loop - sleeps 10 seconds, then reads and prints values of all clocks 34 | # Also reads two bytes of EEPROM and writes the next value to the two bytes 35 | 36 | # do the AT24C32 eeprom 37 | 38 | print "----------------- " 39 | print "----------------- " 40 | print " Test the AT24C32 EEPROM" 41 | print "----------------- " 42 | print "writing first 10 addresses with random data" 43 | for x in range(0,10): 44 | value = random.randint(0,255) 45 | print "address = %i writing value=%i" % (x, value) 46 | ds3231.write_AT24C32_byte(x, value) 47 | print "----------------- " 48 | 49 | print "reading first 10 addresses" 50 | for x in range(0,10): 51 | print "address = %i value = %i" %(x, ds3231.read_AT24C32_byte(x)) 52 | print "----------------- " 53 | print "----------------- " 54 | 55 | 56 | while True: 57 | 58 | 59 | 60 | 61 | # 62 | currenttime = datetime.datetime.utcnow() 63 | 64 | deltatime = currenttime - starttime 65 | 66 | print "" 67 | print "Raspberry Pi=\t" + time.strftime("%Y-%m-%d %H:%M:%S") 68 | 69 | print "DS3231=\t\t%s" % ds3231.read_datetime() 70 | 71 | print "DS3231 Temp=", ds3231.getTemp() 72 | time.sleep(10.0) 73 | --------------------------------------------------------------------------------