|
| 1 | +# Parts transcribed from https://www.raspberrypi.org/forums/viewtopic.php?t=76688 |
| 2 | +import array, fcntl, os, sys |
| 3 | + |
| 4 | +python_2 = sys.version_info[0] == 2 |
| 5 | + |
| 6 | +def bus_path(bus): |
| 7 | + return "/dev/i2c-{}".format(bus) |
| 8 | + |
| 9 | +def available_bus(): |
| 10 | + for bus in range(5): |
| 11 | + if os.access(bus_path(bus), os.F_OK): |
| 12 | + return bus |
| 13 | + raise IOError("No detectable i2c bus available") |
| 14 | + |
| 15 | +def array_to_int_be(byte_array): |
| 16 | + outcome = 0 |
| 17 | + for val in byte_array: |
| 18 | + outcome = (outcome << 8) + val |
| 19 | + return outcome |
| 20 | + |
| 21 | +def array_to_int_le(byte_array): |
| 22 | + return array_to_int_be(reversed(list(byte_array))) |
| 23 | + |
| 24 | +def array_to_int(byte_array, big_endian=True): |
| 25 | + return array_to_int_be(byte_array) if big_endian else array_to_int_le(byte_array) |
| 26 | + |
| 27 | +def crc8check(values, big_endian=True): |
| 28 | + # Ported and refactored from Sparkfun Arduino HTU21D Library: https://github.com/sparkfun/HTU21D_Breakout |
| 29 | + remainder = array_to_int(values, big_endian) |
| 30 | + |
| 31 | + # POLYNOMIAL = 0x0131 = x^8 + x^5 + x^4 + 1 |
| 32 | + # divsor = 0x988000 is the 0x0131 polynomial shifted to farthest left of three bytes |
| 33 | + divsor = 0x988000 |
| 34 | + |
| 35 | + for i in range(0, 16): |
| 36 | + if remainder & 1 << (23 - i): |
| 37 | + remainder ^= divsor |
| 38 | + divsor = divsor >> 1 |
| 39 | + |
| 40 | + return remainder == 0 |
| 41 | + |
| 42 | +def try_ord(val): |
| 43 | + if isinstance(val, int): |
| 44 | + return val |
| 45 | + return ord(val) |
| 46 | + |
| 47 | +def any_py_bytes(bytes_str): |
| 48 | + if python_2 and not isinstance(bytes_str, basestring): |
| 49 | + bytes_str = chr(bytes_str) |
| 50 | + elif not python_2 and not isinstance(bytes_str, (str, bytes)): |
| 51 | + bytes_str = chr(bytes_str) |
| 52 | + if not python_2 and isinstance(bytes_str, str): |
| 53 | + bytes_str = bytes_str.encode('charmap') |
| 54 | + return bytes_str |
| 55 | + |
| 56 | +class CRC8Error(IOError): pass |
| 57 | + |
| 58 | +class I2C(object): |
| 59 | + I2C_SLAVE=0x0703 |
| 60 | + |
| 61 | + def __init__(self, addr, bus=None): |
| 62 | + if bus is None: |
| 63 | + bus = available_bus() |
| 64 | + self._fd = open(bus_path(bus), 'rb+', 0) |
| 65 | + self.set_address(addr) |
| 66 | + |
| 67 | + def set_address(self, addr): |
| 68 | + fcntl.ioctl(self._fd, self.I2C_SLAVE, addr) |
| 69 | + self.addr = addr |
| 70 | + |
| 71 | + def write(self, byte): |
| 72 | + self._fd.write(any_py_bytes(byte)) |
| 73 | + |
| 74 | + def read(self): |
| 75 | + return any_py_bytes(self._fd.read(1)) |
| 76 | + |
| 77 | + def read_many(self, num_bytes, big_endian=True, crc8=False): |
| 78 | + if crc8: |
| 79 | + num_bytes += 1 |
| 80 | + result = list(any_py_bytes(self._fd.read(num_bytes))) |
| 81 | + if crc8: |
| 82 | + if not crc8check(map(try_ord, result), big_endian): |
| 83 | + raise CRC8Error("Bad i2c checksum") |
| 84 | + result = result[:-1] if big_endian else result[1:] |
| 85 | + return result |
| 86 | + |
| 87 | + def read_int(self, num_bytes=1, big_endian=True, crc8=False): |
| 88 | + results = map(try_ord, self.read_many(num_bytes, big_endian, crc8)) |
| 89 | + return array_to_int(results, big_endian) |
| 90 | + |
| 91 | + def close(self): |
| 92 | + if self._fd is not None: |
| 93 | + self._fd.close() |
| 94 | + self._fd = None |
| 95 | + |
| 96 | + def __enter__(self): |
| 97 | + return self |
| 98 | + |
| 99 | + def __exit__(self, exc_type, exc_val, exc_tb): |
| 100 | + self.close() |
| 101 | + |
| 102 | + def __del__(self): |
| 103 | + self.close() |
0 commit comments