Note that there are some explanatory texts on larger screens.

plurals
  1. POConvert I2C Sensor (DS1624) reading into number
    text
    copied!<p>First off, sorry for the confusing title. It's pretty late here and I wasn't able to come up with a better one.</p> <p>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.</p> <p>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.</p> <p>the second byte is the decimal part which has to be multiplied by 0.03125.</p> <p>So, just a couple of examples (TEMP DIGITAL OUTPUT (Binary) / DIGITAL OUTPUT (Hex), taken from the data sheet here <a href="http://datasheets.maximintegrated.com/en/ds/DS1624.pdf" rel="nofollow">http://datasheets.maximintegrated.com/en/ds/DS1624.pdf</a>)</p> <pre><code> +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 </code></pre> <p>Because of a difference in endianness the byte order is reversed when reading the sensor, which is not a problem. <strong>For example, the first line would become 0x007D instead of 0x7D00, 0xE6F0 becomes F0E6, and so on...</strong></p> <p>However, once I build the two's complement for negative values I'm not able to come up with a correct conversion.</p> <p>What I came up with (not working for negative values) is:</p> <pre><code>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: &gt;&gt;&gt; from TempSensor import TempSensor &gt;&gt;&gt; sensor = TempSensor(0x48) &gt;&gt;&gt; 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 &amp; 0x00FF) &amp; 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 &amp; 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 &amp; 0xFF00) &gt;&gt; 8) &gt;&gt; 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()) </code></pre> <p>If you run the run_test() method you'll see what i mean. All negatives values are wrong.</p> <p>The results I get are:</p> <pre><code>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 </code></pre> <p>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.</p> <p><strong>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</strong>. 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 (<a href="https://github.com/federico-galli/Arduino-i2c-temperature-sensor-DS1624/blob/master/DS1624.cpp" rel="nofollow">https://github.com/federico-galli/Arduino-i2c-temperature-sensor-DS1624/blob/master/DS1624.cpp</a>).</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload