Software I2C and DS3231 Simple code

I was wondering whenever anyone has or could share simple code to set/retrieve time on a DS3231. Likewise for the temperature sensor on chip.

The reason I ask (I understand there are plenty of libraries available for this) is because i often have to port to different mcu's and it would be nice to have a single piece of code i could call as a function rather than having to hack a library it each time.

Also, I remember there was a very good I2C library for atmel MCU's written using port manipulation (rather than hardware I2C, with error detection/timeout and smaller footprint than the arduino built-in and able to run inside an IRS) which i cannot remember the name, sadly. Anyone with better memory than me that can give a hint here?

Many Thanks

I was wondering whenever anyone has or could share simple code to set/retrieve time on a DS3231. Likewise for the temperature sensor on chip.

Please follow this link.

As for the time: here you go.

#include <Wire.h>

// variables for storing the time
//   second  minute  hour    weekday  date    month   year
byte ss=0,   mi=0,   hh=0,   wd=6,    dd=1,   mo=1,   yy=0;
 
void setup()
{
  Wire.begin();
  Serial.begin(9600); // or whatever baud rate you wish to use
 
  // clear /EOSC bit
  // Sometimes necessary to ensure that the clock
  // keeps running on just battery power. Once set,
  // it shouldn't need to be reset but it's a good
  // idea to make sure.
//  Wire.beginTransmission(0x68); // address DS3231
//  Wire.write(0x0E); // select register
//  Wire.write(0b00011100); // write register bitmap, bit 7 is /EOSC
//  Wire.endTransmission();
}
 
void loop()
{
  // read the time from the RTC, if we can
  boolean gotTheTime = grabTime();
 
  if (gotTheTime) {
    // if we are here, then the time has been successfully read
    // and stored in global variables (ss, mi, hh, wd, dd, mo, yy)
    Serial.print("Got the time: ");
    printTime(); 
  }
  else {
    // if we are here, then we tried to read the time but couldn't
    Serial.println("Unable to read time from RTC");
  }
 
  delay(500);
}


boolean grabTime() {
  // get time from the RTC and put it in global variables

  // send request to receive data starting at register 0
  Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
  Wire.write((byte)0); // start at register 0
  Wire.endTransmission();
  Wire.requestFrom(0x68, 7); // request seven bytes (ss, mi, hh, wd, dd, mo, yy)
  // check for a reply from the RTC, and use it if we can
  if (Wire.available() >= 7) {
    // if we're here, we got a reply and it is long enough
    // so now we read the time
    ss = bcd2bin(Wire.read()); // get seconds
    mi = bcd2bin(Wire.read()); // get minutes
    hh = bcd2bin(Wire.read()); // get hours
    wd = bcd2bin(Wire.read()); // get day of week
    dd = bcd2bin(Wire.read()); // get day of month
    mo = bcd2bin(Wire.read()); // get month
    yy = bcd2bin(Wire.read()); // get year (two digits)
    // indicate that we successfully got the time
    return true;
  }
  else {
    // indicate that we were unable to read the time
    return false;
  }
}


byte bcd2bin(byte x) {
  // converts from binary-coded decimal to a "regular" binary number
  return ((((x >> 4) & 0xF) * 10) + (x & 0xF)) ;
}


void printTime() {
  // just like it says on the tin
  Serial.print ("\'");
  if (yy<10) Serial.print("0"); Serial.print(yy,DEC); Serial.print("-");
  if (mo<10) Serial.print("0"); Serial.print(mo,DEC); Serial.print("-");
  if (dd<10) Serial.print("0"); Serial.print(dd,DEC); Serial.print("(");
  switch (wd) {
    case 1: Serial.print("Mon"); break;
    case 2: Serial.print("Tue"); break;
    case 3: Serial.print("Wed"); break;
    case 4: Serial.print("Thu"); break;
    case 5: Serial.print("Fri"); break;
    case 6: Serial.print("Sat"); break;
    case 7: Serial.print("Sun"); break;
    default: Serial.print("Bad"); 
  }
  Serial.print(") ");
  if (hh<10) Serial.print("0"); Serial.print(hh,DEC); Serial.print(":");
  if (mi<10) Serial.print("0"); Serial.print(mi,DEC); Serial.print(":");
  if (ss<10) Serial.print("0"); Serial.print(ss,DEC); Serial.println("");
}

For the temperature, you're on your own.

These are the codes for the Temperature Sensor of DS3231 RTC. Reading will appear on the Top Line of 20x4 LCD.

#include<Wire.h>
#define deviceAddress 0b1101000

#include<LiquidCrystal.h>
LiquidCrystal lcd(A0, A1, A2, A3, 12, 13);

byte x;

void setup() 
{
  Wire.begin();   //TWI Bus is formed  
  lcd.begin(20, 4);
  lcd.setCursor(0, 0);
}

void loop() 
{
  
  Wire.beginTransmission(deviceAddress); // queue device address, START condition
  Wire.write(0x0E);                         // queue Control/Status Register Address 
  Wire.write(0x20);                         // queue Control Byte to Start Tempertaure Conversion
  Wire.endTransmission();               //transmit the above queued data, sample, ACK, and assert  STOP
  
  do
  {
    Wire.requestFrom(deviceAddress, 1); //request to read content of Status Register to check end-of-conversion
    x = Wire.read();       
  }
  while (bitRead(x, 5) != LOW);   //Bit-5 of Status Register remains High as long as conversion is going ON
  
  //--- read Temperature Signal--------
  Wire.beginTransmission(deviceAddress); //queued: deviceAddress, START
  Wire.write(0x11); //queued address of Temp Register (Uppler Byte)
  Wire.endTransmission(); //transmit the above queued items, sample ACK, and assert STOP

  Wire.requestFrom(deviceAddress, 2);
  float tempUpper = (float)Wire.read();    //integer part is saved as: XX.00000…….
  byte tempLower = Wire.read();            //fractional part as: XX000000   
  
  float tempLowerx = (float) ((tempLower>>7)*0.5 + ((tempLower>>6)&0x01) *0.250);
  float temp = tempLowerx + tempUpper;
  lcd.print(temp, 2);    //reading comes on LCD Monitor
  delay(3000);
  lcd.setCursor(0,0);
}

Just my opinion, but unless there is no hardware I2C, bit banging I2C isn't the way to go.

Spend the time on understanding what is needed for the different micros and write higher level functions for each.

Use of DS3231.h Library to measure temperature using intertanl sensor of DS3231RTC

#include <DS3231.h>

DS3231 clock;  //pre-defined object created from DS3231 Class
float temp;

void setup()
{
  Serial.begin(9600);
  clock.begin();       //RTC Clock is intialized
}

void loop()
{
  clock.forceConversion();   //start temperature conversion and wait until conversion is done
  temp = clock.readTemperature();  //read raw data from temp sensor and use it to compute actual temp using library function.
  Serial.print(temp, 2);    //show temp on Serial Monitor
  Serial.println(" deg-C");

  delay(3000);
}

sterretje:
Just my opinion, but unless there is no hardware I2C, bit banging I2C isn't the way to go.

Spend the time on understanding what is needed for the different micros and write higher level functions for each.

The standard wire library does not not work inside an interrupt. A requirement of my code is to prioritize an interrupt on power down and save variables to an FRAM/EEPROM, just before the system runs out of power. I could write my own I2C library using the hardware, but a software one just makes it portable.

GolamMostafa:

Please follow this link.

Fantastic, just what I was looking for!

casemod:
The standard wire library does not not work inside an interrupt. A requirement of my code is to prioritize an interrupt on power down and save variables to an FRAM/EEPROM, just before the system runs out of power.

I suppose that in the power down ISR, you can individually disable all interrupts except the ones needed by I2C and next enable the global interrupt again.

You are going at this the wrong way. The only thing you do in the power down interrupt is set a flag (e.g. globally declared volatile char p_down) and in the loop() function, if that flag is set, do all the I2C you need to do.

Pete