Interfacing with AM2320 on FiPy over I2C?



  • Has anyone had any success using the AM2320 on any of the pycom boards over i2c? I have been trying to interface with one using my FiPy and have only been able to receive errors. I have tried using various micropython libraries including ones by alexmrqt and mcauser. I always get some sort of OSError. I know my sensor works and is wired properly because I have been able to get accurate readings using a Raspberry Pi.

    Here is the code I have to use the alexmrqt library:

    i2c = I2C(0, I2C.MASTER)
    i2c = I2C(0, pins=('P10','P11'))
    i2c.init(I2C.MASTER, baudrate=100000)
    am = AM2320.AM2320(i2c)
    
    while True:
        pycom.rgbled(0x7f0000) # red
        try:
            temp = am.temperature
            humidity = am.relative_humidity
            print([str(temp), str(humidity)])
        except OSError:
            # In case the sensor fails
            print(OSError)
            pass
        except RuntimeError:
            print(RuntimeError)
            pass
        pycom.rgbled(0x007f00) # green
        time.sleep(5)
    

    And here is the driver itself:

    # Driver code for AM2320 temperature and humidity sensor
    
    try:
        import struct
    except ImportError:
        import ustruct as struct
    import time
    from micropython import const
    
    __version__ = "0.0.0-auto.0"
    __repo__ = "https://github.com/alexmrqt/Adafruit_CircuitPython_am2320.git"
    
    _AM2320_DEFAULT_ADDR = const(0x5C)
    _AM2320_CMD_READREG = const(0x03)
    _AM2320_REG_TEMP_H = const(0x02)
    _AM2320_REG_HUM_H = const(0x00)
    
    def _crc16(data):
        crc = 0xffff
        for byte in data:
            crc ^= byte
            for _ in range(8):
                if crc & 0x0001:
                    crc >>= 1
                    crc ^= 0xA001
                else:
                    crc >>= 1
        return crc
    
    
    class AM2320:
        """A driver for the AM2320 temperature and humidity sensor.
        :param i2c_bus: The `I2C` object to use. This is the only required parameter.
        :param int address: (optional) The I2C address of the device.
        """
        def __init__(self, i2c_bus, address=_AM2320_DEFAULT_ADDR):
            self._i2c_bus = i2c_bus
            self._addr = address
    
        def _read_register(self, register, length):
            # wake up sensor
            self._i2c_bus.writeto(self._addr, bytes([0x00]))
            time.sleep(0.01)  # wait 10 ms
    
            # Send command to read register
            cmd = [_AM2320_CMD_READREG, register & 0xFF, length]
            # print("cmd: %s" % [hex(i) for i in cmd])
            self._i2c_bus.writeto(self._addr, bytes(cmd))
            time.sleep(0.002)  # wait 2 ms for reply
            result = bytearray(length+4) # 2 bytes pre, 2 bytes crc
            self._i2c_bus.readfrom_into(self._addr, result)
            # print("$%02X => %s" % (register, [hex(i) for i in result]))
            # Check preamble indicates correct readings
            if result[0] != 0x3 or result[1] != length:
                raise RuntimeError('I2C modbus read failure')
            # Check CRC on all but last 2 bytes
            crc1 = struct.unpack("<H", bytes(result[-2:]))[0]
            crc2 = _crc16(result[0:-2])
            if crc1 != crc2:
                raise RuntimeError('CRC failure 0x%04X vs 0x%04X' % (crc1, crc2))
            return result[2:-2]
    
        @property
        def temperature(self):
            """The measured temperature in celsius."""
            temperature = struct.unpack(">H", self._read_register(_AM2320_REG_TEMP_H, 2))[0]
            if temperature >= 32768:
                temperature = 32768 - temperature
            return temperature/10.0
    
        @property
        def relative_humidity(self):
            """The measured relative humidity in percent."""
            humidity = struct.unpack(">H", self._read_register(_AM2320_REG_HUM_H, 2))[0]
            return humidity/10.0
    
    

    I have been searching everywhere but nobody seems to have any info on combining the AM2320 with the pycom boards. Any help would be greatly appreciated!



  • Hi, @zwarcola I saw this post and was curious if you managed to find the solution for your problems, or did you just catch the error and then keep going.



  • @zwarcola No clue, and I cannot test since I do not have that sensor here. Reading the data sheet it seems important, that SCL is pulled high during power up of the device, since that defines, whether the sensor runs in I2C mode or the old Single-Wire mode of the AM2302/DHT11.



  • @robert-hh Hey Robert, sorry for the delayed response. So I have been toying with the i2c sensor a little more. After changing the initialization of the i2c device and adding pull-up resistors, I am still getting a blank list of addresses "[]" and am unable to read values from it either.

    Here is my updated i2c initialization:

    i2c = I2C(0, I2C.MASTER, baudrate=100000, pins=('P10','P11'))
    

    Any ideas?



  • @zwarcola I see the problem. The call to i2c.init() resets the pin definition. Just drop that call and add baudrate=100000 to the constructor call for i2c, or add pins=() to the init call.
    That's subtle pitfall in the code, for SPI and other interfaces too. The constructor and init() share the same init code, which means, you have to do all initialization in a single call.



  • @zwarcola Do you have added pull-up resistors? If yes, what is the result to i2c.scan()? Does that report the address of the device, which is 92?


Log in to reply
 

Pycom on Twitter