MicroPython Driver for PCT2075 Sensor

PCT2075 Driver Code for micro:bit

Download as zip file

'''
LM75A temperature sensor
PCT2075 temperature sensor
MicroPython driver for micro:bit

EXAMPLE USAGE:
LM75A sensor object with default I2C address
lm75a = LM75A()

PCT2075 sensor object with default I2C address
pct2075 = PCT2075()

AUTHOR: fredscave.com
DATE  : 2025/04
VERSION : 2.00
'''

from microbit import i2c, sleep
from micropython import const

ADDR_LM75A   = const(0x48) # A0, A1, A2 pins all to GND
ADDR_PCT2075 = const(0x37) # A0, A1, A2 pins floating
REG_TEMP     = const(0x00)
REG_CONFIG   = const(0x01)
REG_HYST     = const(0x02)
REG_OS       = const(0x03)
REG_TIDLE    = const(0x04)

# Power-on default values
DEFAULT_CONFIG    = const(0x00)
DEFAULT_THYST_MSB = const(0x4B)
DEFAULT_THYST_LSB = const(0x00)
DEFAULT_OS_MSB    = const(0x50)
DEFAULT_OS_LSB    = const(0x00)
DEFAULT_TIDLE     = const(0x01) # PCT2075 only

# Convert Centigrade to Fahrenheit
CtoF = lambda C, d=1: round((C * 9/5) +32, d)

class LM75A():
    def __init__(self, Addr = ADDR_LM75A):
        self.Addr = Addr

    #--------------------------------------------
    #               Public Methods
    #--------------------------------------------

    # Get temperature reading in Centigrade.
    # Resolution is 9-bits or 11-bits
    def Read(self, Res = 9):
        res = Res if (Res in [9, 11]) else 9
        i2c.write(self.Addr, bytes([REG_TEMP]))
        buf = i2c.read(self.Addr, 2)
        return LM75A._Convert(buf, res)

    # Resets configuration defaults to power-on values.
    def ResetDefaults(self):
        i2c.write(self.Addr, bytes([REG_CONFIG,
                                    DEFAULT_CONFIG]))
        i2c.write(self.Addr, bytes([REG_HYST,
                                    DEFAULT_THYST_MSB,
                                    DEFAULT_THYST_LSB]))
        i2c.write(self.Addr, bytes([REG_OS,
                                    DEFAULT_OS_MSB,
                                    DEFAULT_OS_LSB]))

    # Returns the Configuration register value
    def GetConfig(self):
        i2c.write(self.Addr, bytes([REG_CONFIG]))
        return i2c.read(self.Addr, 1)[0]

    # Sets the three user settable parameters in
    # the Configuration register.
    # Read the Datasheet for more details.
    def SetConfig(self, Queue = 1, Polarity = 0, CmpInt = 0):
        cmpint = CmpInt if (CmpInt in (0, 1)) else 0
        polarity = Polarity if (Polarity in (0, 1)) else 0
        t = (1, 2, 4, 6)
        if Queue in t:
            queue = t.index(Queue)
        else:
            queue = 0
        config = self.GetConfig()
        config = LM75A._ChangeBit(config, cmpint, 1)
        config = LM75A._ChangeBit(config, polarity, 2)
        config = LM75A._ChangeBit(config, queue & 0b01, 3)
        config = LM75A._ChangeBit(config, queue >> 1, 4)
        i2c.write(self.Addr, bytes([REG_CONFIG, config]))

    # Place the sensor in shutdown mode.
    def Off(self):
        i2c.write(self.Addr, bytes([REG_CONFIG]))
        config = i2c.read(self.Addr, 1)[0]
        config = config | 0x01
        i2c.write(self.Addr, bytes([REG_CONFIG, config]))

    # Wake the sensor if it is in shutdown mode.
    def On(self):
        i2c.write(self.Addr, bytes([REG_CONFIG]))
        config = i2c.read(self.Addr, 1)[0]
        config = config & 0b11111110
        i2c.write(self.Addr, bytes([REG_CONFIG, config]))
        sleep(300)

    # Set or get the Hysteresis Centigrade temperature.
    # This is used by the Watchdog. See the Datasheet
    # for more details.
    def Hyst(self, Temp = None):
        if Temp == None:
            i2c.write(self.Addr, bytes([REG_HYST]))
            binary = i2c.read(self.Addr, 2)[0]
            return LM75A._fromComp2(binary, 8)
        else:
            if isinstance(Temp, int):
                temp2comp = LM75A._toComp2(Temp, 8)
                buf = bytes([REG_HYST, temp2comp, 0])
                i2c.write(self.Addr, buf)

    # Set or get the Over-temperature shutdown
    # temperature (Centigrade).
    # This is used by the Watchdog. See the Datasheet
    # for more details.
    def OS(self, Temp = None):
        if Temp == None:
            i2c.write(self.Addr, bytes([REG_OS]))
            binary = i2c.read(self.Addr, 2)[0]
            return LM75A._fromComp2(binary, 8)
        else:
            if isinstance(Temp, int):
                temp2comp = LM75A._toComp2(Temp, 8)
                buf = bytes([REG_OS, temp2comp, 0])
                i2c.write(self.Addr, buf)

    #--------------------------------------------
    #               Private Methods
    #--------------------------------------------

    # Returns binary temperature reading as decimal.
    @staticmethod
    def _Convert(buf, res):
        b = LM75A._fromComp2(buf[0], 8)
        dec = buf[1] >> (16 - res)
        return b + dec/(2**(res-8))

    # Converts Twos Complementary binary
    # to decimal equivalent.
    @staticmethod
    def _fromComp2(binary, bits):
        mask = 1 << (bits-1)
        if not (binary & mask) >> (bits-1):
            return  binary
        else:
            return -((binary ^ 2**bits - 1) + 1)

    # Converts signed integer to Twos Complementary.
    @staticmethod
    def _toComp2(sint, bits):
        if sint >= 0:
            return sint
        else:
            return (abs(sint) ^ 2**bits - 1) + 1

    # Changes the value of a bit in a given position
    # of a byte.
    @staticmethod
    def _ChangeBit(byte, bit, pos):
        if bit == 1:
            return byte | (1 << pos)
        else:
            return byte & ~(1 << pos)

#-----------------------------------------
#      Derived class for PCT2075
#
# The PCT2075 sensor is drop in replacement
# for the older LM75A sensor.
#
# The PCT2075 has a few additional features
# that this derived class provides.
#------------------------------------------

class PCT2075(LM75A):
    def __init__(self, Addr = ADDR_PCT2075):
        self.Addr = Addr

    def Read(self, Res = 11):
        res = Res if (Res in [9, 11]) else 11
        return super().Read(res)

    # Resets configuration defaults to power-on values.
    def ResetDefaults(self):
        i2c.write(self.Addr, bytes([REG_TIDLE,
                                    DEFAULT_TIDLE]))
        super().ResetDefaults()

    # The temperature conversion cycle time can be
    # user set in multiples of 100 ms.
    def CycleTime(self, Multiples = None):
        if Multiples == None:
            i2c.write(self.Addr, bytes([REG_TIDLE]))
            buf = i2c.read(self.Addr, 1)
            return buf[0]
        else:
            multiples = Multiples if (Multiples in range(0, 32)) else 1
            i2c.write(self.Addr, bytes([REG_TIDLE, multiples]))

          

Combining LM75A & PCT2075

Since the base class of this module is a driver for the LM75A it is possible to use this module to control both an LM75A and PCT2075 at the same time on the same I2C bus. This example shows how simple it is to do this.

Requirements
  • micro:bit
  • LM75A breakout board
  • PCT2075 breakout board
  • Breadboard, jumper wires
  • Driver: fc_lm75a_pct2075.py
Hookup Guide
  • LM75A and PCT2075 GND pins micro:bit GND pin
  • LM75A and PCT2075 VCC pins micro:bit 3.3V
  • LM75A and PCT2075 SCL pins micro:bit Pin 19
  • LM75A and PCT2075 SDA pins micro:bit Pin 20

Ensure that the driver file fc_lm75a_pct2075.py has been copied t0 the micro:bit's file system.


The code:
# Test the PCT2075 driver by reading
# temperatures simultaneously from
# an LM75A sensor and a PCT2075 sensor.

from fc_lm75a_pct2075 import *
from microbit import sleep

lm75a = LM75A()
pct2075 = PCT2075()

# Print headings
print('LM75A   PCT2075')

# Take 5 readings at two minute
# intervals from each sensor.
for x in range(5):
    print(lm75a.Read(), '  ', pct2075.Read())
    if x != 4:
        sleep(120000)


Output:
LM75A   PCT2075
27.0    29.0
27.5    29.125
27.5    29.0
27.0    28.875
27.0    28.75
          
PCT2075 breakout board (left), LM75A breakout board (right) and the micro:bit
PCT2075 breakout board (left), LM75A breakout board (right) and the micro:bit
Wrap Up

Interestingly the PCT2075 consistently read about 2°C higher than the LM75A.