Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RTC_DS3231.cpp getTemperature() doesn't provide for negative temps #287

Open
Fishbone69 opened this issue Jun 15, 2023 · 4 comments · May be fixed by #303
Open

RTC_DS3231.cpp getTemperature() doesn't provide for negative temps #287

Fishbone69 opened this issue Jun 15, 2023 · 4 comments · May be fixed by #303

Comments

@Fishbone69
Copy link

I believe as written, the DS3231 method for getting the temperature does not look at the MSB of the high byte to determine if the temperature is positive or negative. I would recommend something like this for the update:

float RTC_DS3231::getTemperature() {
uint8_t buffer[2] = {DS3231_TEMPERATUREREG, 0};
float temperatureDegC;
i2c_dev->write_then_read(buffer, 1, buffer, 2);
temperatureDegC = (float)buffer[0] + (float)(buffer[1] >> 6) * 0.25f;
if (buffer[0] & 0x80) {
temperatureDegC *= -1.0f;
}
return temperatureDegC;
}

@edgar-bonet
Copy link
Contributor

the DS3231 method for getting the temperature does not look at the MSB of the high byte to determine if the temperature is positive or negative.

Indeed you are right. This method will fail to correctly read a negative temperature.

if (buffer[0] & 0x80) {
  temperatureDegC *= -1.0f;
}

This won't work either. It would, if the temperature was encoded using the sign–magnitude representation. However, according to the datasheet of the DS3231, “The temperature is encoded in two’s complement format.” (link mine).

I would instead try this:

float RTC_DS3231::getTemperature() {
  uint8_t buffer[2] = {DS3231_TEMPERATUREREG, 0};
  i2c_dev->write_then_read(buffer, 1, buffer, 2);
  int16_t temp = uint16_t(buffer[0]) << 8 | buffer[1];
  return temp * (1 / 256.0);
}

Some notes:

  • Casting buffer[0] to an unsigned type is meant to avoid signed integer overflow, which is undefined behavior in C++.
  • Assigning the result to a signed integer makes numbers with the most significant bit set be interpreted as negative. There is no extra work to do, as all current processors use two’s complement representation internally.
  • Multiplying by 1 / 256.0 (rather than 1 / 4.0) obviates the need for the 6-bit shift. Also, 1 / 256.0 being a compile-time constant, there is no division performed at run-time (which would be expensive).

Could you give a try to this implementation?

@Fishbone69
Copy link
Author

Fishbone69 commented Jun 15, 2023

I'm not an experienced coder so I am glad there are folks like you to take me to school. I did stupidly assume sign-magnitude representation. I did some reseach and your suggestion not only appears correct but is far more elegant. I'll try it when I get home. Thank you!!

@i440bx
Copy link

i440bx commented May 6, 2024

float RTC_DS3231::getTemperature() {
  uint8_t buffer[2] = {DS3231_TEMPERATUREREG, 0};
  i2c_dev->write_then_read(buffer, 1, buffer, 2);
  int16_t temp = uint16_t(buffer[0]) << 8 | buffer[1];
  return temp * (1 / 256.0);
}

Could you give a try to this implementation?

I have copied the code into my RTC_DS3231.cpp and it runs flawless :) It shows negative temperatures low to -27.00°C.

Lower my medical ice spray cant cool down.

edgar-bonet added a commit to edgar-bonet/RTClib that referenced this issue May 6, 2024
RTC_DS3231::getTemperature() assumes the temperature register stores a
positive integer whereas, according to the datasheet,[1] it is a signed
number in two’s complement format.

Fix the method by transferring this register into a signed 16-bit
integer. As the CPU uses two’s complement natively, no further
conversion is needed other than scaling by a factor (1/256.0).

Fixes adafruit#287.

[1] https://www.analog.com/media/en/technical-documentation/data-sheets/DS3231.pdf#page=15
@edgar-bonet edgar-bonet linked a pull request May 6, 2024 that will close this issue
@edgar-bonet
Copy link
Contributor

@i440bx: Thanks for testing this! I just submitted this change as pull request #303.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants