Page 1 of 2

TMP100 library - Please critique

Posted: Sat Dec 24, 2016 9:37 am
by jimako
Hello. The following code is for the TMP100 [1] temperature sensor. I would like your feedback on the code. What would make the code more correct?

Code: Select all

"""TMP100 driver for ESP8266 in MicroPython."""

from machine import I2C


class TMP100:
    # Registers
    T_REG = 0x00
    CONF_REG = 0x01

    # Configuration Register
    SHUTDOWN = 0x01
    RES_9bits = 0x00  # 9bits, 0.5C, 40ms
    RES_10bits = 0x20  # 10bits, 0.25C, 80ms
    RES_11bits = 0x40  # 11bits, 0.125C, 160ms
    RES_12bits = 0x60  # 12bits, 0.0625C, 320ms
    ONESHOT = 0x80

    def __init__(self, scl, sda, addr, res):
        self.i2c = I2C(scl=scl, sda=sda, freq=400000)
        self.write_address = addr
        self.read_address = addr | 0x01
        self.set_resolution(res)

    def write_register(self, reg, data):
        buf = bytearray(1)
        self.i2c.start()
        buf[0] = self.write_address
        self.i2c.write(buf)
        buf[0] = reg
        self.i2c.write(buf)
        buf[0] = data
        self.i2c.write(buf)
        self.i2c.stop()

    def read_register(self, reg):
        buf = bytearray(1)
        self.i2c.start()
        buf[0] = self.write_address
        self.i2c.write(buf)
        buf[0] = reg
        self.i2c.write(buf)
        self.i2c.start()
        buf[0] = self.read_address
        self.i2c.write(buf)
        if reg == self.T_REG:
            rbuf = bytearray(2)
        elif reg == self.CONF_REG:
            rbuf = bytearray(1)
        self.i2c.readinto(rbuf)
        self.i2c.stop()
        return rbuf

    def set_resolution(self, res):
        current = self.read_register(self.CONF_REG)[0] & 0x9F
        self.write_register(self.CONF_REG, current | res)

    def set_shutdown(self, state):
        current = self.read_register(self.CONF_REG)[0] & 0xFE
        if state is True:
            self.write_register(self.CONF_REG, current | self.SHUTDOWN)
        else:
            self.write_register(self.CONF_REG, current)

    def set_oneshot(self):
        current = self.read_register(self.CONF_REG)[0] & 0xEF
        self.write_register(self.CONF_REG, current | self.ONESHOT)

    def read_temp(self):
        t = self.read_register(self.T_REG)
        return ((t[0] << 8 | t[1]) >> 4) * 0.0625
Usage:

Code: Select all

>>> from tmp100 import TMP100
>>> from machine import Pin
>>> mytmp = TMP100(Pin(5), Pin(4), 0x90, TMP100.RES_9bits)
>>> mytmp.read_temp()
23.5
>>> mytmp.set_resolution(TMP100.RES_12bits)
>>> mytmp.read_temp()
23.875
[1] http://www.ti.com/lit/ds/symlink/tmp101.pdf

Re: TMP100 library - Please critique

Posted: Tue Dec 27, 2016 12:35 am
by ernitron
It looks like (but I can be wrong as I don't have a TMP100) you didn't consider the negative values. According to specs negative are represented as twos complement with msb set. Check the code for ds18b20.

Test it putting your sensor in the refrigerator ;)

Besides, I would have called read_temp() temperature() to be similar to other libraries like dht11. But it's more personal style.

Re: TMP100 library - Please critique

Posted: Wed Dec 28, 2016 7:31 am
by ernitron
Bonus. The corrected read_temp()

Code: Select all

def read_temp(self):
     t = self.read_register(self.T_REG)
     temp = ((t[0] << 8 | t[1]) >> 4)
     if temp & 0x800: # sign bit set
         temp = -((temp ^ 0xfff) + 1)
     return temp * 0.0625

Re: TMP100 library - Please critique

Posted: Wed Dec 28, 2016 3:25 pm
by jimako
Thank you for your comments. You are right, I neglected negative numbers. Thanks for the two's complement code too!

Re: TMP100 library - Please critique

Posted: Thu Dec 29, 2016 9:42 am
by deshipu
Why don't you use the ustruct module to do such conversions?

Re: TMP100 library - Please critique

Posted: Thu Dec 29, 2016 7:18 pm
by jimako
Hm but the number is made out of 12 bits. Also (for my understanding) wouldn't importing ustruct module consume more resources than those bitwise operations?

Re: TMP100 library - Please critique

Posted: Sat Dec 31, 2016 2:40 pm
by deshipu
No, ustruct is built into the firmware, it already takes those resources whether you use it or not.

Re: TMP100 library - Please critique

Posted: Sun Jan 01, 2017 9:31 am
by pythoncoder
How about

Code: Select all

def read_temp(self):
    t = self.read_register(self.T_REG)
    return 0.00390625 * ustruct.unpack('>h', t)[0]
@jimako the point being that ustruct performs the sign handling on a 16 bit quantity before it is reduced to 12 bits. Although the reduction to 12 bits is not explicit: it's subsumed into the floating point scale factor.

Re: TMP100 library - Please critique

Posted: Sun Jan 01, 2017 8:41 pm
by jimako
Thanks guys for your valuable feedback. I will incorporate the changes in the code.

Re: TMP100 library - Please critique

Posted: Mon Jan 02, 2017 9:59 pm
by ernitron
pythoncoder wrote:How about

Code: Select all

def read_temp(self):
    t = self.read_register(self.T_REG)
    return 0.00390625 * ustruct.unpack('>h', t)[0]
@jimako the point being that ustruct performs the sign handling on a 16 bit quantity before it is reduced to 12 bits. Although the reduction to 12 bits is not explicit: it's subsumed into the floating point scale factor.
Brilliant! You guys rock! Still as performance is concern I think the bitwise operation is faster, but the elegance of ustruct is undeniable.