1

I can't reach my sht21 sensor throughout the Multiplexer tca9548a (adafruit). When I run it with python3 in the raspberry pi's terminal, I get this error report:

Traceback (most recent call last):
  File "sensormeasurement.py", line 15, in <module>
    sensor1 = sht21.SHT21(tca[0])
  File "/home/pi/programming_stuff/sht21.py", line 20, in __init__
    self.bus.write_byte(self._I2C_ADDRESS, self._SOFTRESET)
AttributeError: 'TCA9548A_Channel' object has no attribute 'write_byte'

I think, this is because I mixed the code from adafruit blinka: Github repository Adafruit_CircuitPython_TCA9548A

# The MIT License (MIT)
#
# Copyright (c) 2018 Carter Nelson for Adafruit Industries
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
``Adafruit_TCA9548A``
====================================================

CircuitPython driver for the TCA9548A I2C Multiplexer.

* Author(s): Carter Nelson

Implementation Notes
--------------------

**Hardware:**

* TCA9548A I2C Multiplexer: https://www.adafruit.com/product/2717

**Software and Dependencies:**

* Adafruit CircuitPython firmware for the supported boards:
  https://github.com/adafruit/circuitpython/releases
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
"""

# imports
from micropython import const

_DEFAULT_ADDRESS = const(0x70)

__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_TCA9548A.git"

class TCA9548A_Channel():
    """Helper class to represent an output channel on the TCA9548A and take care
       of the necessary I2C commands for channel switching. This class needs to
       behave like an I2CDevice."""

    def __init__(self, tca, channel):
        self.tca = tca
        self.channel_switch = bytearray([1 << channel])

    def try_lock(self):
        """Pass thru for try_lock."""
        while not self.tca.i2c.try_lock():
            pass
        self.tca.i2c.writeto(self.tca.address, self.channel_switch)
        return True

    def unlock(self):
        """Pass thru for unlock."""
        return self.tca.i2c.unlock()

    def readfrom_into(self, address, buffer, **kwargs):
        """Pass thru for readfrom_into."""
        if address == self.tca.address:
            raise ValueError("Device address must be different than TCA9548A address.")
        return self.tca.i2c.readfrom_into(address, buffer, **kwargs)

    def writeto(self, address, buffer, **kwargs):
        """Pass thru for writeto."""
        if address == self.tca.address:
            raise ValueError("Device address must be different than TCA9548A address.")
        return self.tca.i2c.writeto(address, buffer, **kwargs)


class TCA9548A():
    """Class which provides interface to TCA9548A I2C multiplexer."""

    def __init__(self, i2c, address=_DEFAULT_ADDRESS):
        self.i2c = i2c
        self.address = address
        self.channels = [None]*8

    def __len__(self):
        return 8

    def __getitem__(self, key):
        if not 0 <= key <= 7:
            raise IndexError("Channel must be an integer in the range: 0-7")
        if self.channels[key] is None:
            self.channels[key] = TCA9548A_Channel(self, key)
        return self.channels[key]

with a program for the i2c triggered sht21 sensor from this repository modified for python3:

import smbus 
import time

class SHT21:
    """Class to read temperature and humidity from SHT21.
    Ressources: 
      http://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/Humidity/Sensirion_Humidity_SHT21_Datasheet_V3.pdf
      https://github.com/jaques/sht21_python/blob/master/sht21.py
      Martin Steppuhn's code from http://www.emsystech.de/raspi-sht21"""

    #control constants
    _SOFTRESET = 0xFE
    _I2C_ADDRESS = 0x40
    _TRIGGER_TEMPERATURE_NO_HOLD = 0xF3
    _TRIGGER_HUMIDITY_NO_HOLD = 0xF5

    def __init__(self, bus):
        """According to the datasheet the soft reset takes less than 15 ms."""
        self.bus = bus
        self.bus.write_byte(self._I2C_ADDRESS, self._SOFTRESET)
        time.sleep(0.015)

    def read_temperature(self):    
        """Reads the temperature from the sensor.  Not that this call blocks
    for 250ms to allow the sensor to return the data"""
        data = []
        self.bus.write_byte(self._I2C_ADDRESS, self._TRIGGER_TEMPERATURE_NO_HOLD)
        time.sleep(0.250)
        data.append(self.bus.read_byte(self._I2C_ADDRESS))
        data.append(self.bus.read_byte(self._I2C_ADDRESS))
        return self._get_temperature_from_buffer(data)


    def read_humidity(self):    
        """Reads the humidity from the sensor.  Not that this call blocks 
    for 250ms to allow the sensor to return the data"""
        data = []
        self.bus.write_byte(self._I2C_ADDRESS, self._TRIGGER_HUMIDITY_NO_HOLD)
        time.sleep(0.250)
        data.append(self.bus.read_byte(self._I2C_ADDRESS))
        data.append(self.bus.read_byte(self._I2C_ADDRESS))
        return self._get_humidity_from_buffer(data)    

    def _get_temperature_from_buffer(self, data):
        """This function reads the first two bytes of data and 
        returns the temperature in C by using the following function:
        T = =46.82 + (172.72 * (ST/2^16))
        where ST is the value from the sensor
        """
        unadjusted = (data[0] << 8) + data[1]
        unadjusted *= 175.72
        unadjusted /= 1 << 16 # divide by 2^16
        unadjusted -= 46.85
        return unadjusted

    def _get_humidity_from_buffer(self, data):
        """This function reads the first two bytes of data and returns 
        the relative humidity in percent by using the following function:
        RH = -6 + (125 * (SRH / 2 ^16))
        where SRH is the value read from the sensor
        """
        unadjusted = (data[0] << 8) + data[1]
        unadjusted *= 125
        unadjusted /= 1 << 16 # divide by 2^16
        unadjusted -= 6
        return unadjusted



    def close(self):
        """Closes the i2c connection"""
        self.bus.close()


    def __enter__(self):
        """used to enable python's with statement support"""
        return self


    def __exit__(self, type, value, traceback):
        """with support"""
        self.close()


if __name__ == "__main__":
    try:
        bus = smbus.SMBus(1)
        with SHT21(bus) as sht21:
            print "Temperature: %s"%sht21.read_temperature()
            print "Humidity: %s"%sht21.read_humidity()
    except IOError, e:
        print e
print 'Error creating connection to i2c.'

in the modified document (which is also from Github repository Adafruit_CircuitPython_TCA9548A) which I named sensormeasurement.py:

# This program is using Senirion SHT21 sensors for humidity and temperature measurement which is attached to TCA9548A Multiplexer through channels 0 and 1. This is like the example in https://github.com/adafruit/Adafruit_CircuitPython_TCA9548A 
import time
import board
import busio
import adafruit_tca9548a
import sht21

# Create I2C bus as normal
i2c = busio.I2C(board.SCL, board.SDA)

# Create the TCA9548A object and give it the I2C bus
tca = adafruit_tca9548a.TCA9548A(i2c)

# For each sensor, create it using the TCA9548A channel instead of the I2C object
sensor1 = sht21.SHT21(tca[0])
sensor2 = sht21.SHT21(tca[1])
sensor3 = sht21.SHT21(tca[2])

# Loop and profit!
while True:
    print ("Temperature: %s" % sensor1.read_temperature())
    print ("Humidity: %s" % sensor1.read_humidity())
    time.sleep(0.1)

I installed CiruitPython, and when I rewire my circuit, so that the sensor is directly plugged in the i2c port of the raspberry pi, then the sht21.py runs perfectly. But since I what to use more than one sensor, I want to use the multiplexer. Maybe the problem is that I use smbus in sht21.py and in sensormeasurement.py I used busio instead. So, how should I modify sht21.py? I am new at this subject, so please explain it in detail. Thanks.

1 Answer 1

2

I figured it out with a little help: Circuit Python doesn't work with this sht21.py, because it is smbus based. So here it is: I used the multiplexer code from this homepage but this is all I needed

# TCA9548A I2C multiplexer
# I2C Address: 70 through 77
# Channel: 0 - 7

import smbus

# class for the I2C switch 
class TCA9548A(object):
    # init
    def __init__(self,name,address,bus_nr):
        self.name=name
        self.address=address
        self.bus_nr=bus_nr
        self.bus=smbus.SMBus(bus_nr) # for bus_nr = /def/i2c-bus_nr

    # Change to i2c channel 0..7
    def chn(self,channel):
        self.bus.write_byte(self.address,2**channel)

And then I adjusted the sensormeasurement.py:

import time
import multiplexer
import sht21
import smbus 

# I2C bus already in multiplexer.py created

# Create the TCA9548A object and give it the I2C bus
tca = multiplexer.TCA9548A('I2C switch 0',0x70,1)

# For each sensor, create it using the TCA9548A channel instead of the I2C object
for x in [0, 1, 2]:
    tca.chn(x)
    sensor = sht21.SHT21(tca.bus)
    print ("Temperature: %s" % sensor.read_temperature())
    print ("Humidity: %s" % sensor.read_humidity())

So, perhaps this will help someone in the future.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.