First off, sorry for the confusing title. It's pretty late here and I wasn't able to come up with a better one.
So, I have a I2C temperature sensor that outputs the current temperature as a 16 bit word. Reading from LEFT to RIGHT, the 1st bit is the MSB and the 13th bit is the LSB, so 13 bits are payload and the last 3 bits are zeros. I want to read out that sensor with a Raspberry Pi and convert the data.
The first byte (8 bits) are the integer part of the current temperature. If and only if the temperature is negative, the two's complement of the entire word has to be built.
the second byte is the decimal part which has to be multiplied by 0.03125.
So, just a couple of examples (TEMP DIGITAL OUTPUT (Binary) / DIGITAL OUTPUT (Hex), taken from the data sheet here http://datasheets.maximintegrated.com/en/ds/DS1624.pdf)
+125˚C | 01111101 00000000 | 7D00h
+25.0625˚C | 00011001 00010000 | 1910h
+½˚C | 00000000 10000000 | 0080h
0˚C | 00000000 00000000 | 0000h
-½˚C | 11111111 10000000 | FF80h
-25.0625˚C | 11100110 11110000 | E6F0h
-55˚C | 11001001 00000000 | C900h
Because of a difference in endianness the byte order is reversed when reading the sensor, which is not a problem. For example, the first line would become 0x007D instead of 0x7D00, 0xE6F0 becomes F0E6, and so on...
However, once I build the two's complement for negative values I'm not able to come up with a correct conversion.
What I came up with (not working for negative values) is:
import smbus
import time
import logging
class TempSensor:
"""
Class to read out an DS1624 temperature sensor with a given address.
DS1624 data sheet: http://datasheets.maximintegrated.com/en/ds/DS1624.pdf
Usage:
>>> from TempSensor import TempSensor
>>> sensor = TempSensor(0x48)
>>> print "%02.02f" % sensor.get_temperature()
23.66
"""
# Some constants
DS1624_READ_TEMP = 0xAA
DS1624_START = 0xEE
DS1624_STOP = 0x22
def __init__(self, address):
self.address = address
self.bus = smbus.SMBus(0)
def __send_start(self):
self.bus.write_byte(self.address, self.DS1624_START);
def __send_stop(self):
self.bus.write_byte(self.address, self.DS1624_STOP);
def __read_sensor(self):
"""
Gets the temperature data. As the DS1624 is Big-endian and the Pi Little-endian,
the byte order is reversed.
"""
"""
Get the two-byte temperature value. The second byte (endianness!) represents
the integer part of the temperature and the first byte the fractional part in terms
of a 0.03125 multiplier.
The first byte contains the value of the 5 least significant bits. The remaining 3
bits are set to zero.
"""
return self.bus.read_word_data(self.address, self.DS1624_READ_TEMP)
def __convert_raw_to_decimal(self, raw):
# Check if temperature is negative
negative = ((raw & 0x00FF) & 0x80) == 0x80
if negative:
# perform two's complement
raw = (~raw) + 1
# Remove the fractional part (first byte) by doing a bitwise AND with 0x00FF
temp_integer = raw & 0x00FF
# Remove the integer part (second byte) by doing a bitwise AND with 0XFF00 and
# shift the result bits to the right by 8 places and another 3 bits to the right
# because LSB is the 5th bit
temp_fractional = ((raw & 0xFF00) >> 8) >> 3
return temp_integer + ( 0.03125 * temp_fractional)
def run_test(self):
logging.basicConfig(filename='debug.log', level=logging.DEBUG)
# Examples taken from the data sheet (byte order swapped)
values = [0x7D, 0x1019, 0x8000, 0, 0x80FF, 0xF0E6, 0xC9]
for value in values:
logging.debug('value: ' + hex(value) + ' result: ' + str(self.__convert_raw_to_decimal(value)))
def get_temperature(self):
self.__send_start();
time.sleep(0.1);
return self.__convert_raw_to_decimal(self.__read_sensor())
If you run the run_test() method you'll see what i mean. All negatives values are wrong.
The results I get are:
DEBUG:root:value: 0x7d result: 125.0
DEBUG:root:value: 0x1019 result: 25.0625
DEBUG:root:value: 0x8000 result: 0.5
DEBUG:root:value: 0x0 result: 0.0
DEBUG:root:value: 0x80ff result: 1.46875
DEBUG:root:value: 0xf0e6 result: 26.03125
DEBUG:root:value: 0xc9 result: 55.96875
So, I've been banging my head for hours on this one, but it seems I'm lacking the fundamentals of bit-wise operations. I believe that the problem is the masking with the logical AND when values are negative.
EDIT: There are a couple of implementations on the web. None of them works for negative temperatures. I tried it by actually putting the sensor in ice water. I haven't tried the Arduino C++ version yet, but from looking at the source code it seems it doesn't build the two's complement at all, so no negative temperatures either (https://github.com/federico-galli/Arduino-i2c-temperature-sensor-DS1624/blob/master/DS1624.cpp).