Problems with reading temperature registers on a DS3231

Hi All,

I am wanting to use the temperature registers on a DS3231 as a local temperature indicator. I can read and set all of the time registers at will. But the temperature registers have nonsense values.

Here is a sample of the output of the program below:

Sun, 9/8/15 - 1:28:25
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:26
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:27
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:28
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:29
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:30
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:31
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:32
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:33
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:34
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:35
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:36
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:37
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:38
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:39
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:40
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:41
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:42
temperature bytes: 154 130
Sun, 9/8/15 - 1:28:43
temperature bytes: 154 130

As can be seen the time increments nicely. All that part works.

However the MSB of the temperature is 154, which is a negative number as the sign bit is set according to the data sheet (8 bit 2’s complement). Ignoring the LSB fractional part for the moment that makes it -102 Celsius in here and while it isn’t as warm as it might be, it isn’t under zero. This reading is nonsense.

DS3231 is keeping perfect time - so I suspect it is what I am doing. But it looks OK to me. Is there something tricky about the data in the registers the datasheet does not indicate.

Any ideas?

Here is the code I am using: (Thanks to Arduino basics - Real Time Clock RTC DS3231) slightly modified as I didn’t like the conversion to float used but before that I need good data.

#include <Wire.h>

#define DS3231_I2C_ADDRESS 104

// SCL - pin A5
// SDA - pin A4
// To set the clock, run the sketch and use the serial monitor.
// Enter T1124154091014; the code will read this and set the clock. See the code for full details.
//
byte seconds, minutes, hours, day, date, month, year;
char weekDay[4];

byte tMSB, tLSB; // temperature bytes here
float temp3231;   // not used

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

void loop()
{
 
//  watchConsole(); // don't need this DS3231 already set and battery backed
 
 
  get3231Date();
 
  Serial.print(weekDay); Serial.print(", "); Serial.print(date, DEC); Serial.print("/"); Serial.print(month, DEC); Serial.print("/"); Serial.print(year, DEC); Serial.print(" - ");
  Serial.print(hours, DEC); Serial.print(":"); Serial.print(minutes, DEC); Serial.print(":"); Serial.println(seconds, DEC);
 
  get3231Temp();
  
  delay(1000);
}

// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

void watchConsole()
{
  if (Serial.available()) {      // Look for char in serial queue and process if found
    if (Serial.read() == 84) {      //If command = "T" Set Date
      set3231Date();
      get3231Date();
      Serial.println(" ");
    }
  }
}
 
void set3231Date()
{
//T(sec)(min)(hour)(dayOfWeek)(dayOfMonth)(month)(year)
//T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99)
//Example: 02-Feb-09 @ 19:57:11 for the 3rd day of the week -> T1157193020209
// T1124154091014
  seconds = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result.  
  minutes = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  hours   = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  day     = (byte) (Serial.read() - 48);
  date    = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  month   = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  year    = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0x00);
  Wire.write(decToBcd(seconds));
  Wire.write(decToBcd(minutes));
  Wire.write(decToBcd(hours));
  Wire.write(decToBcd(day));
  Wire.write(decToBcd(date));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}


void get3231Date()
{
  // send request to receive data starting at register 0
  Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
  Wire.write(0x00); // start at register 0
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes

  if(Wire.available()) {
    seconds = Wire.read(); // get seconds
    minutes = Wire.read(); // get minutes
    hours   = Wire.read();   // get hours
    day     = Wire.read();
    date    = Wire.read();
    month   = Wire.read(); //temp month
    year    = Wire.read();
       
    seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111)); // convert BCD to decimal
    minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111)); // convert BCD to decimal
    hours   = (((hours & B00110000)>>4)*10 + (hours & B00001111)); // convert BCD to decimal (assume 24 hour mode)
    day     = (day & B00000111); // 1-7
    date    = (((date & B00110000)>>4)*10 + (date & B00001111)); // 1-31
    month   = (((month & B00010000)>>4)*10 + (month & B00001111)); //msb7 is century overflow
    year    = (((year & B11110000)>>4)*10 + (year & B00001111));
  }
  else {
    //oh noes, no data!
  }
 
  switch (day) {
    case 1:
      strcpy(weekDay, "Sun");
      break;
    case 2:
      strcpy(weekDay, "Mon");
      break;
    case 3:
      strcpy(weekDay, "Tue");
      break;
    case 4:
      strcpy(weekDay, "Wed");
      break;
    case 5:
      strcpy(weekDay, "Thu");
      break;
    case 6:
      strcpy(weekDay, "Fri");
      break;
    case 7:
      strcpy(weekDay, "Sat");
      break;
  }
}

void get3231Temp()
{
  //temp registers (11h-12h) get updated automatically every 64s
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0x11);
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
 
  if(Wire.available()) {
    tMSB = Wire.read(); //2's complement int portion
    tLSB = Wire.read(); //fraction portion

  Serial.print("temperature bytes: "); Serial.print(tMSB); Serial.print("   "); Serial.println(tLSB);
  }
  else {
    //oh noes, no data!
  }
  
  
  
  return;
}

I don't see anything obviously wrong, but the data sure doesn't make sense. Too many 1s where there should be 0s.

I believe that somehow register 0x11 and 0x12 have gotten corrupted.

There should only be data in the top two bits(7 and 6) of the LSB. Somehow you have a stray bit set in bit 2. In your case you really have lsb = 128 or .5C.

I believe that somehow you also have an incorrectly set bit in the sign of the MSB. If you subtract 128 from 154 you have 26.

Put the two values together without the extra 1's which Crossroads mentions and the the temperature is 26.5.

My first thought would be to clear both registers by writing them B00000000 and then see if things correct themselves as the temperature updates. If there is something physically wrong with the registers, you can certainly deal with the LSB by just using only the top two bits (which is what the >>6 is about in your referenced code). If you don't need negative temperatures, you can also mask the top bit in the MSB as is also done in the referenced code.

urbantigerau: (...)

Here is the code I am using: (Thanks to http://www.goodliffe.org.uk/arduino/rtc_ds3231.php) slightly modified as I didn't like the conversion to float used but before that I need good data.

(...)

Thanks for that link! One of the best ways to retrieve temperature information from a DS3231 without the use of a library.

A bit of a wild guess but change this:

  if(Wire.available()) {
    tMSB = Wire.read(); //2's complement int portion
    tLSB = Wire.read(); //fraction portion

  Serial.print("temperature bytes: "); Serial.print(tMSB); Serial.print("   "); Serial.println(tLSB);
  }
  else {
    //oh noes, no data!
  }

to this:

  tMSB = Wire.read(); //2's complement int portion
  tLSB = Wire.read(); //fraction portion
  // I prefer one statement per line :)
  Serial.print("temperature bytes: ");
  Serial.print(tMSB);
  Serial.print("   ");
  Serial.println(tLSB);

When you issue requestFrom, the I2C device does not send data for the library to buffer. It waits for you to issue two reads.

Pete

el_supremo: A bit of a wild guess but change this:

  if(Wire.available()) {
    tMSB = Wire.read(); //2's complement int portion
    tLSB = Wire.read(); //fraction portion

  Serial.print("temperature bytes: "); Serial.print(tMSB); Serial.print("  "); Serial.println(tLSB);   }   else {     //oh noes, no data!   }




to this:


  tMSB = Wire.read(); //2's complement int portion   tLSB = Wire.read(); //fraction portion   // I prefer one statement per line :)   Serial.print("temperature bytes: ");   Serial.print(tMSB);   Serial.print("  ");   Serial.println(tLSB); ```

When you issue requestFrom, the I2C device does not send data for the library to buffer. It waits for you to issue two reads.

Pete

And what about this:

#define DS3231_I2C_ADDRESS 0x68
byte tMSB, tLSB;
float temp3231;

void setup() {

  Serial.begin(115200);
}

float get3231Temp()
{
  //temp registers (11h-12h) get updated automatically every 64s
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0x11);
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 2);

  if (Wire.available()) {
    tMSB = Wire.read(); //2's complement int portion
    tLSB = Wire.read(); //fraction portion

    temp3231 = (tMSB & B01111111); //do 2's math on Tmsb
    temp3231 += ( (tLSB >> 6) * 0.25 ); //only care about bits 7 & 8
  }
  else {
    //oh noes, no data!
  }

  return temp3231;
}

void loop() {

    Serial.print("temperature = ");
    Serial.println(get3231Temp());
delay (500);
}

The sensor measures the temperature (both negative and positive) and puts the value as natural binary (normal decimal) in two’s complement form in the following two registers.

The integer part of the temperature value occupies the 8-bit space of the Temperature Register (Upper Byte) at address 11h. The upper two bit (Bit-7 and Bit-6) of Temperature Register (Lower Byte) at address 12h contains the fractional portion of the temperature value. For example, assume that the contents of the above two registers are 00011000 and 11000000; then, the value of the Temperature is 24.750C (integer part = 00011000 = 0 + 0 + 0 + 1x24 + 1x23 + 0 + 0 + 0 = 24; the fractional part = 11 = 1x2-1 + 1x2-2 = 1/2 + 1/4 = 0.50 + 0.25 = 0.75).

The following steps could be carried out to acquire temperature signal from the DS3231 RTC Module using MEGA and show it on the Serial Monitor at 2-sec interval.
(a) Start the Temperature Conversion by placing LH at the CONV bit of Control Register.
(b) Wait until conversion is done which is indicated by the presence of LL-state at the CONV bit of Control register.
(c) Read Temperature Signals from the Temperature Registers.
(d) Format the reading of Step-C and place it on the Serial Monitor.

#include<Wire.h>                //this file comes with IDE; it is needed for TWI Bus operation
#define deviceAddress 0b1101000 //address of the DS3231 RTC Chip = 0x68

byte x;                         //global variable

void setup()
{
  Serial.begin(9600);
  Wire.begin();       //TWI (aka I2C Bus) Bus is formed
}

void loop()
{
  Wire.beginTransmission(deviceAddress); //START, deviceAdr, data direction (write) are queued
  Wire.write(0x0E);                     //Control Register Address is queued
  Wire.write(0x20);                     //Start Temperature Conversion command byte is queued
  Wire.endTransmission();               //queued information are transferred on ACK

  do
  {
    Wire.requestFrom(deviceAddress, 1);   //command to the slave to send 1-byte data
    x = Wire.read();                      //data is read from FIFO buffer
  }
  while (bitRead(x, 5) != LOW);           //bit-5 of Control Register is LOW indicates end-of-conversion

  //--- read Temperature Signal----------------
  Wire.beginTransmission(deviceAddress);  //
  Wire.write(0x11);                       //point higher byte of Temperature Register; queued
  Wire.endTransmission();                 //transfer the above queued information

  Wire.requestFrom(deviceAddress, 2);       //request to send 2-byte data of Temperature Register
  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;
  Serial.println(temp, 2);
  delay(2000);
}