#!/usr/bin/env python3 # Python library to control Zookeeper import ft4222 import ft4222.I2CMaster DEBUG = False INA231_ADDR = 0x40 INA231_REG_CONFIG = 0x00 INA231_REG_SHUNT_VOLTAGE = 0x01 INA231_REG_BUS_VOLTAGE = 0x02 INA231_REG_POWER = 0x03 INA231_REG_CURRENT = 0x04 INA231_REG_CALIBRATION = 0x05 INA231_BUS_LSB = 1.25e-3 INA231_SHUNT_LSB = 2.5e-6 SHUNT_RESISTOR = 30e-3 CURRENT_LSB = 1e-5 class Zookeeper: def __init__(self): if ft4222.createDeviceInfoList() < 2: raise Exception("No connected zookeeper found!") self.dev_a = ft4222.openByDescription("FT4222 A") self.dev_b = ft4222.openByDescription("FT4222 B") if DEBUG: for i in range(ft4222.createDeviceInfoList()): print(f"Device {i}: {ft4222.getDeviceInfoDetail(i, False)}") # Setup GPIO self.dev_b.gpio_Init(gpio2=ft4222.Dir.OUTPUT, gpio3=ft4222.Dir.OUTPUT) self.dev_b.setSuspendOut(False) self.dev_b.setWakeUpInterrut(False) # Setup I2C self.dev_a.i2cMaster_Init(kbps=400) self._initialize_ina() # Helper functions def _read_ina_register(self, register, length): self.dev_a.i2cMaster_WriteEx(INA231_ADDR, data=register, flag=ft4222.I2CMaster.Flag.REPEATED_START) return self.dev_a.i2cMaster_Read(INA231_ADDR, bytesToRead=length) def _write_ina_register(self, register, data): msg = register.to_bytes(1, byteorder="big") + data.to_bytes(2, byteorder="big") self.dev_a.i2cMaster_Write(INA231_ADDR, data=msg) def _initialize_ina(self): # Config self._write_ina_register(INA231_REG_CONFIG, 0x4127) # Calibration CAL_VALUE = int(0.00512 / (CURRENT_LSB * SHUNT_RESISTOR)) if DEBUG: print(f"Calibration value: {hex(CAL_VALUE)}") self._write_ina_register(INA231_REG_CALIBRATION, CAL_VALUE) def _set_gpio(self, number, enabled): self.dev_b.gpio_Write(portNum=number, value=enabled) # Public API functions def set_device_power(self, enabled): self._set_gpio(2, enabled) def set_device_ignition(self, enabled): self._set_gpio(3, enabled) def read_current(self): # Returns in A return int.from_bytes(self._read_ina_register(INA231_REG_CURRENT, 2), byteorder="big") * CURRENT_LSB def read_power(self): # Returns in W return int.from_bytes(self._read_ina_register(INA231_REG_POWER, 2), byteorder="big") * CURRENT_LSB * 25 def read_voltage(self): # Returns in V return int.from_bytes(self._read_ina_register(INA231_REG_BUS_VOLTAGE, 2), byteorder="big") * INA231_BUS_LSB