How to read DS3231 Internal temperature sensor, example code

I have replaced a DS1307 with a more accurate DS3231 RTC.

Since i was already familiar with the libraries for the DS1307 I didnt want to re-write all my code just to get a temperature reading. Instead I kept the code the same and added a function that returns the temperature. I believe this might be used with minor moddifications with other dalas I2C temperature sensors.

Since I lost a bit of time finding what I needed, I decided to share.
Example code below

#include <Wire.h>
#define DS3231_I2C_ADDR             0x68
#define DS3231_TEMPERATURE_ADDR     0x11

void setup()
{
  Serial.begin(9600);
   Wire.begin();
}

void loop()
{
   int tempC = DS3231_get_treg();  // Reads the temperature as an int, to save memory
   // float tempC = DS3231_get_treg();
   delay(500);
   Serial.print(tempC);
}

float DS3231_get_treg()
{
    int rv;  // Reads the temperature as an int, to save memory
//  float rv;
    
    uint8_t temp_msb, temp_lsb;
    int8_t nint;

    Wire.beginTransmission(DS3231_I2C_ADDR);
    Wire.write(DS3231_TEMPERATURE_ADDR);
    Wire.endTransmission();

    Wire.requestFrom(DS3231_I2C_ADDR, 2);
    temp_msb = Wire.read();
    temp_lsb = Wire.read() >> 6;

    if ((temp_msb & 0x80) != 0)
        nint = temp_msb | ~((1 << 8) - 1);      // if negative get two's complement
    else
        nint = temp_msb;

    rv = 0.25 * temp_lsb + nint;

    return rv;
}

You aren’t doing the conversion properly. The Arduino uses twos complement arithmetic so there’s no need to convert. Also, the fraction is part of the twos complement number but you are treating it separately.
I use this code:

  /*   short temp; */ 
  temp = Wire.read() << 8;
  temp |= Wire.read();
  return(temp/256.);

Pete

This is how I do it.

No, declare the temp as I showed in the comment. It must be signed. Either "short temp;" or "int16 temp;" will do.

Pete

el_supremo: No, declare the temp as I showed in the comment. It must be signed. Either "short temp;" or "int16 temp;" will do.

Pete

I got it Thanks, I forgot to declare tempC as a float!

The other thing I was trying to do was retriving the value as a decimal and a fractional part. For example,

Serial.print(tempC); Serial.print("."); Serial.print(fractionaltempC);

This is to avoid using a float as it uses about 2K of flash memory. Can you advise?

I'm curious why you guys want to access this temperature. It's the chip internal temp isn't it, for timing compensation?

Here's an idea on how to avoid using float.

The MSB is an int, and the 2 bits of the LSB represent quarter degrees as follows:

00=.00 01=.25 10=.50 11=.75

You should be able to use an if or switch case to display this value following the integer.

JimboZA: I'm curious why you guys want to access this temperature. It's the chip internal temp isn't it, for timing compensation?

Why not, it's a temperature. Not terribly accurate, ±3°C but better than nothing I guess.

cattledog: Here's an idea on how to avoid using float.

The MSB is an int, and the 2 bits of the LSB represent quarter degrees as follows:

00=.00 01=.25 10=.50 11=.75

You should be able to use an if or switch case to display this value following the integer.

Except for temperatures below zero, 00 = 0.00, 01 = -0.75, 10 = -0.50, 11 = -0.25

JimboZA: I'm curious why you guys want to access this temperature. It's the chip internal temp isn't it, for timing compensation?

Yes, for adjustment of the internal TCXO, but its quite accurate, unlike the one on the atmega chip. Since I already have the I2C clock, it saves me an interface with another sensor for the indoor temperature

[quote author=Jack Christensen link=topic=262986.msg1855322#msg1855322 date=1408991013] Why not, it's a temperature. Not terribly accurate, ±3°C but better than nothing I guess. [/quote] Here's a comparison with a dalas DS18B20 http://kevinrye.net/files/reading_the_temp_ds3231_and_ds18b20.php

[quote author=Jack Christensen link=topic=262986.msg1855329#msg1855329 date=1408991397]

cattledog: Here's an idea on how to avoid using float.

The MSB is an int, and the 2 bits of the LSB represent quarter degrees as follows:

00=.00 01=.25 10=.50 11=.75

You should be able to use an if or switch case to display this value following the integer.

Except for temperatures below zero, 00 = 0.00, 01 = -0.75, 10 = -0.50, 11 = -0.25 [/quote]

My issue now is more of a programming question:

Currently i have this:

return(temp/256.);

and on my loop this:

    float tempC = DS3231_get_treg();  //

So I am directly assigning the result to a variable. How would I re-arrange my code to return two results?

casemod: Here's a comparison with a dalas DS18B20 http://kevinrye.net/files/reading_the_temp_ds3231_and_ds18b20.php

Foo. Anecdotal evidence. A sample size of one means nothing. The guy got lucky. Maxim pays one or more engineers six figures to arrive at the ±3°C spec; ignore the datasheet at your own peril. That blog post should at least have had a prominent YMMV.

[quote author=Jack Christensen link=topic=262986.msg1855356#msg1855356 date=1408992682]

Foo. Anecdotal evidence. A sample size of one means nothing. The guy got lucky. Maxim pays one or more engineers six figures to arrive at the ±3°C spec; ignore the datasheet at your own peril. That blog post should at least have had a prominent YMMV. [/quote]

Jack, I am not mentioning tolerances, I am just saying on my particular case it is "accurate enough" to avoid using a DS18B20 (which is accurate to 0.5C) to measure ambient temperature. It is certainly more accurate that the internal atmel on-chip temperature sensor.

Im by no means saying that they are factory calibrated or can achieve xC accuracy.

My gripe was with the blog item. The author ( I assume that wasn’t you? :blush: :D) seems to conclude that the two temperature sensors have comparable accuracy, which is what I take exception with. OTOH, if the DS3231 sensor is a good match for a particular application, then by all means use it!

Here is some quick code to display the temperature with no floats. I leave it to you to figure out how to handle the below zero values of temperature. If you do see them, I’m not sure I would be sitting at your desk. :wink:

#include <Wire.h>
#define DS3231_I2C_ADDR             0x68
#define DS3231_TEMPERATURE_MSB      0x11
#define DS3231_TEMPERATURE_LSB      0x12

byte temp_msb;
byte temp_lsb;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
}

void loop()
{
  temp_msb = DS3231_get_MSB();
  temp_lsb = DS3231_get_LSB();

  Serial.print(temp_msb);

  switch(temp_lsb){
  case 0:
    Serial.println(".00");
    break;
  case 1 :
    Serial.println(".25");
    break;
  case 2:
    Serial.println(".50");
    break;
  case 3:
    Serial.println(".75");
    break;
  }


  delay(2000);
}

byte DS3231_get_MSB(){
  Wire.beginTransmission(DS3231_I2C_ADDR);
  Wire.write(DS3231_TEMPERATURE_MSB);
  Wire.endTransmission();

  Wire.requestFrom(DS3231_I2C_ADDR, 1);
  temp_msb = Wire.read();

}

byte DS3231_get_LSB(){

  Wire.beginTransmission(DS3231_I2C_ADDR);
  Wire.write(DS3231_TEMPERATURE_LSB);
  Wire.endTransmission();

  Wire.requestFrom(DS3231_I2C_ADDR, 1);
  temp_lsb = Wire.read() >> 6;


}

[quote author=Jack Christensen link=topic=262986.msg1855384#msg1855384 date=1408994020] My gripe was with the blog item. The author ( I assume that wasn't you? :blush: :D) seems to conclude that the two temperature sensors have comparable accuracy, which is what I take exception with. OTOH, if the DS3231 sensor is a good match for a particular application, then by all means use it! [/quote]

I understand, just to make clear that by quoting you I was not saying you were wrong or anything.

[Wrong content removed] - DS18B20 is 1 wire not I2C

cattledog:
Here is some quick code to display the temperature with no floats. I leave it to you to figure out how to handle the below zero values of temperature. If you do see them, I’m not sure I would be sitting at your desk. :wink:

#include <Wire.h>

#define DS3231_I2C_ADDR             0x68
#define DS3231_TEMPERATURE_MSB      0x11
#define DS3231_TEMPERATURE_LSB      0x12

byte temp_msb;
byte temp_lsb;

void setup()
{
 Serial.begin(9600);
 Wire.begin();
}

void loop()
{
 temp_msb = DS3231_get_MSB();
 temp_lsb = DS3231_get_LSB();

Serial.print(temp_msb);

switch(temp_lsb){
 case 0:
   Serial.println(".00");
   break;
 case 1 :
   Serial.println(".25");
   break;
 case 2:
   Serial.println(".50");
   break;
 case 3:
   Serial.println(".75");
   break;
 }

delay(2000);
}

byte DS3231_get_MSB(){
 Wire.beginTransmission(DS3231_I2C_ADDR);
 Wire.write(DS3231_TEMPERATURE_MSB);
 Wire.endTransmission();

Wire.requestFrom(DS3231_I2C_ADDR, 1);
 temp_msb = Wire.read();

}

byte DS3231_get_LSB(){

Wire.beginTransmission(DS3231_I2C_ADDR);
 Wire.write(DS3231_TEMPERATURE_LSB);
 Wire.endTransmission();

Wire.requestFrom(DS3231_I2C_ADDR, 1);
 temp_lsb = Wire.read() >> 6;

}

You used two functions as I was thinking, thanks for clarifying!
About negative values, what exactly happens? I know I cant read them, but havent found out the reason why, just a specification “wrong values displayed”.

Does it go “negative binary”?

This will handle temperatures below zero:

    const int DS3231_RTC_ADDR = 0x68;
    const int DS3231_TEMP_MSB = 0x11;
    union int16_byte {
        int i;
        byte b[2];
    } rtcTemp;
    
    Wire.beginTransmission(DS3231_RTC_ADDR);
    Wire.write(DS3231_TEMP_MSB);
    Wire.endTransmission();
    Wire.requestFrom(DS3231_RTC_ADDR, 2);
    rtcTemp.b[1] = Wire.read(); 
    rtcTemp.b[0] = Wire.read();
    long tempC100 = (rtcTemp.i >> 6) * 25;    //degrees celsius times 100
    Serial.print( tempC100 / 100 );
    Serial.print( '.' );
    Serial.println( abs(tempC100 % 100) );

casemod: Does it go "negative binary"?

See http://en.wikipedia.org/wiki/Two%27s_complement

[quote author=Jack Christensen link=topic=262986.msg1855442#msg1855442 date=1408996655]

casemod: Does it go "negative binary"?

See http://en.wikipedia.org/wiki/Two%27s_complement [/quote]

All explained on the DS18B20 datasheet. Thanks.